Android源码学习之装饰模式应用
主要内容:
一、装饰模式定义
装饰模式定义: |
Attach additional responsibilities to an object dynamically keeping the same interface. Decoators provide a flexible alternative to subclassing for extending functionality. |
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。 |
如上图所示(截取自《Head First Design Patterns》一书),主要包括四个部分:
1. Component抽象组件,是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象。(注:在装饰模式中,必然有一个最基本、最核心、最原始的接口或者抽象类充当Component抽象组件)
2. ConcreteComponent具体组件,是最核心、最原始、最基本的接口或抽象类的实现,我们需要装饰的就是它。
3. Decorator装饰角色, 一般是一个抽象类,实现接口或者抽象方法,它的属性里必然有一个private变量指向Component抽象组件。
4. 具体装饰角色,如上图中的ConcreteDecoratorA和ConcreteDecoratorB,我们要把我们最核心的、最原始的、最基本的东西装饰成其它东西。
二、装饰模式的优势
装饰类和被装饰类可以独立发展,而不会相互耦合。换句话说,Component类无须知道Decorator类,Decorator类是从外部来扩展Component类的功能,而Decorator也不用知道具体的组件。 |
装饰模式是继承关系的一个替代方案。我们看装饰类Decorator,不管装饰多少层,返回的对象还是Component,实现的还是is-a的关系。 |
装饰模式可以动态地扩展一个实现类的功能。 |
三、装饰模式在Android源码中的应用
在Android源码中,其中一个比较经典的使用到装饰模式的就是由Context抽象类扩展出的ContextWrapper的设计。继承结构如下图所示:
1. Context就是我们的抽象组件,它提供了应用运行的基本环境,是各组件和系统服务通信的桥梁,隐藏了应用与系统服务通信的细节,简化了上层应用的开发。所以Contex就是“装饰模式”里的Component。
2. Context类是个抽象类,android.app.ContextImpl派生实现了它的抽象接口。ContextImpl对象会与Android框架层的各个服务(包括组件管理服务、资源管理服务、安装管理服务等)建立远程连接,通过对Android进程间的通信机制(IPC)和这些服务进行通信。所以ContextImpl就是“装饰模式”里的ConcreteComponent。
3. 如果上层应用期望改变Context接口的实现,就需要使用android.content.ContextWrapper类,它派生自Context,其中具体实现都是通过组合的方式调用ContextImpl类的实例(在ContextWrapper中的private属性mBase)来完成的。这样的设计,使得ContextImpl与ContextWrapper子类的实现可以单独变化,彼此独立。所以可以看出ContextWrapper就是“装饰模式”里的Decorator。
4. Android的界面组件Activity、服务组件Service以及应用基类Application都派生于ContextWrapper,它们可以通过重载来修改Context接口的实现。所以可以看出Activity、服务组件Service以及应用基类Application就是“装饰模式”里的具体装饰角色A、B、C。
注:上图可以看出界面组件基类android.app.Activity添加了界面绘制相关的实现,增加了处理界面事件的相关接口。它存放界面中各控件的对象,并与窗口管理服务建立连接,传递界面相关的事件和操作。
现在开始看看源代码是怎么进行组织使用“装饰模式”的。
1. Context抽象类:在该抽象类中定义了一系列get***()和set***()等抽象函数,其中有一个没有实现的startActivity抽象函数:
/**
* Interface to global information about an application environment. This is
* an abstract class whose implementation is provided by
* the Android system. It
* allows access to application-specific resources and classes, as well as
* up-calls for application-level operations such as launching activities,
* broadcasting and receiving intents, etc.
*/
public abstract class Context {
/**
* File creation mode: the default mode, where the created file can only
* be accessed by the calling application (or all applications sharing the
* same user ID).
* @see #MODE_WORLD_READABLE
* @see #MODE_WORLD_WRITEABLE
*/
。。。。。。
/**
* Launch a new activity. You will not receive any information about when
* the activity exits.
*
* <p>Note that if this method is being called from outside of an
* {@link android.app.Activity} Context, then the Intent must include
* the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag. This is because,
* without being started from an existing Activity, there is no existing
* task in which to place the new activity and thus it needs to be placed
* in its own separate task.
*
* <p>This method throws {@link ActivityNotFoundException}
* if there was no Activity found to run the given Intent.
*
* @param intent The description of the activity to start.
*
* @throws ActivityNotFoundException
*
* @see PackageManager#resolveActivity
*/
public abstract void startActivity(Intent intent);
......
2. 看android.app.ContextImpl类,找到startActivity方法,看它的实现:
/**
* Common implementation of Context API, which provides the base
* context object for Activity and other application components.
*/
class ContextImpl extends Context {
private final static String TAG = "ApplicationContext";
private final static boolean DEBUG = false;
private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs =
new HashMap<String, SharedPreferencesImpl>();
。。。。。。
@Override
public void startActivity(Intent intent) {
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ " Is this really what you want?");
}
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null,
(Activity)null, intent, -1);
}
.....
这里不在阐述是如何实现的,但至少可以看出的ContextImpl是实现了Context的抽象方法startActivity函数。
3. 现在来看装饰类ContextWrapper如何来调用这个startActivity方法的:
/**
* Proxying implementation of Context that simply delegates all of its calls to
* another Context. Can be subclassed to modify behavior without changing
* the original Context.
*/
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
3.1 首先必须包含属性Context抽象类的实例对象mBase。
@Override
public void startActivity(Intent intent) {
mBase.startActivity(intent);
}
3.2 看出它只是单纯的调用父类Context的方法mBase.startActivity(intent),并未做修改。
4. 看看具体装饰类如何来装饰和扩展父类ContextWrapper的:
Activity类:
不断扩展自己的属性和方法;
同样的Service类也是:
public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
private static final String TAG = "Service";
public Service() {
super(null);
}
/** Return the application that owns this service. */
public final Application getApplication() {
return mApplication;
}
/**
* Called by the system when the service is first created. Do not call this method directly.
*/
public void onCreate() {
}
/**
* @deprecated Implement {@link #onStartCommand(Intent, int, int)} instead.
*/
@Deprecated
public void onStart(Intent intent, int startId) {
}
/**
* Bits returned by {@link #onStartCommand} describing how to continue
* the service if it is killed. May be {@link #START_STICKY},
* {@link #START_NOT_STICKY}, {@link #START_REDELIVER_INTENT},
* or {@link #START_STICKY_COMPATIBILITY}.
*/
public static final int START_CONTINUATION_MASK = 0xf;
/**
* Constant to return from {@link #onStartCommand}: compatibility
* version of {@link #START_STICKY} that does not guarantee that
* {@link #onStartCommand} will be called again after being killed.
*/
public static final int START_STICKY_COMPATIBILITY = 0;
/**
* Constant to return from {@link #onStartCommand}: if this service‘s
* process is killed while it is started (after returning from
* {@link #onStartCommand}), then leave it in the started state but
* don‘t retain this delivered intent. Later the system will try to
* re-create the service. Because it is in the started state, it will
* guarantee to call {@link #onStartCommand} after creating the new
* service instance; if there are not any pending start commands to be
* delivered to the service, it will be called with a null intent
* object, so you must take care to check for this.
*
* <p>This mode makes sense for things that will be explicitly started
* and stopped to run for arbitrary periods of time, such as a service
* performing background music playback.
*/
public static final int START_STICKY = 1;
/**
* Constant to return from {@link #onStartCommand}: if this service‘s
* process is killed while it is started (after returning from
* {@link #onStartCommand}), and there are no new start intents to
* deliver to it, then take the service out of the started state and
* don‘t recreate until a future explicit call to
* {@link Context#startService Context.startService(Intent)}. The
* service will not receive a {@link #onStartCommand(Intent, int, int)}
* call with a null Intent because it will not be re-started if there
* are no pending Intents to deliver.
*
。。。。。。
Application类同样:
public class Application extends ContextWrapper implements ComponentCallbacks2 {
private ArrayList<ComponentCallbacks> mComponentCallbacks =
new ArrayList<ComponentCallbacks>();
private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
new ArrayList<ActivityLifecycleCallbacks>();
/** @hide */
public LoadedApk mLoadedApk;
public interface ActivityLifecycleCallbacks {
void onActivityCreated(Activity activity, Bundle savedInstanceState);
void onActivityStarted(Activity activity);
void onActivityResumed(Activity activity);
void onActivityPaused(Activity activity);
void onActivityStopped(Activity activity);
void onActivitySaveInstanceState(Activity activity, Bundle outState);
void onActivityDestroyed(Activity activity);
}
public Application() {
super(null);
}
最后让我们记住支撑“装饰模式”的设计原则:
Classes should be open for extension, but closed for modification.
自己可以看操作文件的Java源代码,设计思想也是应用到“装饰模式”,祝大家元旦快乐~~~
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。