首页 > 代码库 > Android源码学习之装饰模式应用

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类:

public class Activity extends ContextThemeWrapper        implements LayoutInflater.Factory2,        Window.Callback, KeyEvent.Callback,        OnCreateContextMenuListener, ComponentCallbacks2 {    private static final String TAG = "Activity";    /** Standard activity result: operation canceled. */    public static final int RESULT_CANCELED    = 0;    /** Standard activity result: operation succeeded. */    public static final int RESULT_OK           = -1;    /** Start of user-defined activity results. */    public static final int RESULT_FIRST_USER   = 1;    private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";    private static final String FRAGMENTS_TAG = "android:fragments";    private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds";    private static final String SAVED_DIALOGS_TAG = "android:savedDialogs";    private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_";    private static final String SAVED_DIALOG_ARGS_KEY_PREFIX = "android:dialog_args_";    private static class ManagedDialog {        Dialog mDialog;        Bundle mArgs;    }    private SparseArray<ManagedDialog> mManagedDialogs;    // set by the thread after the constructor and before onCreate(Bundle savedInstanceState) is called.    private Instrumentation mInstrumentation;    private IBinder mToken;    private int mIdent;    /*package*/ String mEmbeddedID;    private Application mApplication;    /*package*/ Intent mIntent;    private ComponentName mComponent;    /*package*/ ActivityInfo mActivityInfo;    /*package*/ ActivityThread mMainThread;    Activity mParent;    boolean mCalled;    boolean mCheckedForLoaderManager;    boolean mLoadersStarted;    /*package*/ boolean mResumed;    private boolean mStopped;    boolean mFinished;    boolean mStartedActivity;    /** true if the activity is going through a transient pause */    /*package*/ boolean mTemporaryPause = false;    /** true if the activity is being destroyed in order to recreate it with a new configuration */    /*package*/ boolean mChangingConfigurations = false;    /*package*/ int mConfigChangeFlags;    /*package*/ Configuration mCurrentConfig;    private SearchManager mSearchManager;    private MenuInflater mMenuInflater;    static final class NonConfigurationInstances {        Object activity;        HashMap<String, Object> children;        ArrayList<Fragment> fragments;        SparseArray<LoaderManagerImpl> loaders;    }    /* package */ NonConfigurationInstances mLastNonConfigurationInstances;        private Window mWindow;    private WindowManager mWindowManager;    /*package*/ View mDecor = null;    /*package*/ boolean mWindowAdded = false;    /*package*/ boolean mVisibleFromServer = false;    /*package*/ boolean mVisibleFromClient = true;    /*package*/ ActionBarImpl mActionBar = null;    private CharSequence mTitle;    private int mTitleColor = 0;    final FragmentManagerImpl mFragments = new FragmentManagerImpl();        SparseArray<LoaderManagerImpl> mAllLoaderManagers;    LoaderManagerImpl mLoaderManager;        private static final class ManagedCursor {        ManagedCursor(Cursor cursor) {            mCursor = cursor;            mReleased = false;            mUpdated = false;        }        private final Cursor mCursor;        private boolean mReleased;        private boolean mUpdated;    }    private final ArrayList<ManagedCursor> mManagedCursors =        new ArrayList<ManagedCursor>();    // protected by synchronized (this)     int mResultCode = RESULT_CANCELED;    Intent mResultData = null;    private boolean mTitleReady = false;    private int mDefaultKeyMode = DEFAULT_KEYS_DISABLE;    private SpannableStringBuilder mDefaultKeySsb = null;        protected static final int[] FOCUSED_STATE_SET = {com.android.internal.R.attr.state_focused};    private final Object mInstanceTracker = StrictMode.trackActivity(this);    private Thread mUiThread;    final Handler mHandler = new Handler();    /** Return the intent that started this activity. */    public Intent getIntent() {        return mIntent;    }    /**      * Change the intent returned by {@link #getIntent}.  This holds a      * reference to the given intent; it does not copy it.  Often used in      * conjunction with {@link #onNewIntent}.      *       * @param newIntent The new Intent object to return from getIntent      *      * @see #getIntent     * @see #onNewIntent     */     public void setIntent(Intent newIntent) {        mIntent = newIntent;    }    /** Return the application that owns this activity. */    public final Application getApplication() {        return mApplication;    }    /** Is this activity embedded inside of another activity? */    public final boolean isChild() {        return mParent != null;    }        /** Return the parent activity if this view is an embedded child. */    public final Activity getParent() {        return mParent;    }    /** Retrieve the window manager for showing custom windows. */    public WindowManager getWindowManager() {        return mWindowManager;    }    /**     * Retrieve the current {@link android.view.Window} for the activity.     * This can be used to directly access parts of the Window API that     * are not available through Activity/Screen.     *      * @return Window The current window, or null if the activity is not     *         visual.     */

不断扩展自己的属性和方法;

同样的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.

本文转自:http://www.cnblogs.com/yemeishu/archive/2012/12/30/2839489.html

Android源码学习之装饰模式应用