zoukankan      html  css  js  c++  java
  • 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源码中,其中一个比较经典的使用到装饰模式的就是由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类:

    View Code
    复制代码
    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.

    自己可以看操作文件的Java源代码,设计思想也是应用到“装饰模式”,祝大家元旦快乐~~~

  • 相关阅读:
    记一次在线阅卷系统设计开发总结
    卡座录音经验汇总
    TEAC 6030S 统调
    空白磁带SONY TDK 万胜,3大品牌空白磁带,入门必读
    cnetos 7 mysql 从data还原数据库
    java的BigDecimal比较大小
    nginx配置静态访问txt文件
    小程序筛选
    买分产整合
    开窗函数
  • 原文地址:https://www.cnblogs.com/yuxiang204/p/2840208.html
Copyright © 2011-2022 走看看