Android設計模式之裝飾者模式(Decorator Pattern)
概論
在Android原始碼裡很多時候會看到這個模式。下面來講一些這個模式。
裝飾者模式主要的優點是動態給一個物件新增一些額外的職責。使用Decorator模式相比用生成子類方式達到功能的擴充顯得更為靈活。缺點是,要建立比繼承更多的物件。
先來個熱身,下面是裝飾者模式的uml圖。它大概就長得這樣子的。
1. Component抽象元件,是一個介面或者是抽象類,就是定義我們最核心的物件,也就是最原始的物件。(注:在裝飾模式中,必然有一個最基本、最核心、最原始的介面或者抽象類充當Component抽象元件)
2. ConcreteComponent具體元件,是最核心、最原始、最基本的介面或抽象類的實現,我們需要裝飾的就是它。
3. Decorator裝飾角色, 一般是一個抽象類,實現介面或者抽象方法,它的屬性裡必然有一個private變數指向Component抽象元件
4. 具體裝飾角色,如上圖中的ConcreteDecorator,我們要把我們最核心的、最原始的、最基本的東西裝飾成其它東西。
Android原始碼中應用
再來一個Android的Context的uml圖
兩個圖一對比,你這下你知道怎麼回事了吧!
再看看context裡面有什麼。
public abstract class Context {
......
public abstract Resources getResources();
/** Return PackageManager instance to find global package information. */
public abstract PackageManager getPackageManager ();
/** Return a ContentResolver instance for your application's package. */
public abstract ContentResolver getContentResolver();
public abstract Looper getMainLooper();
public abstract Context getApplicationContext();
public abstract void sendBroadcast(@RequiresPermission Intent intent);
......
@Nullable
public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver,
IntentFilter filter);
......
}
Context個抽象類,提供了一組通用的API,具體操作還沒實現。 對應裝飾者模式中的Componetnt。
ContextImpl類是下面這個樣子的,它是Context的實現。對應ConcreteComponent。它會與Android框架層的各個服務(包括元件管理服務、資源管理服務、安裝管理服務等)建立遠端連線,通過對Android程序間的通訊機制(IPC)和這些服務進行通訊。
class ContextImpl extends Context {
......
@Override
public void startActivity(Intent intent) {
warnIfCallingFromSystemProcess();
startActivity(intent, null);
}
/** @hide */
@Override
public void startActivityAsUser(Intent intent, UserHandle user) {
startActivityAsUser(intent, null, user);
}
......
/** @hide */
@Override
public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
try {
ActivityManagerNative.getDefault().startActivityAsUser(
mMainThread.getApplicationThread(), getBasePackageName(), intent,
intent.resolveTypeIfNeeded(getContentResolver()),
null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options,
user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@Override
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
......
}
這個是ContextWrapper ,它對應裝飾者模式中的Decorator。在Android原始碼中有不少這樣帶Wrapper字尾的類,其實都可以往裝飾者模式方面想。在外部呼叫時候需要把ContextImpl傳進來賦值給mBase。像這樣,ContextWrapper(new ContextImpl)。
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
......
@Override
public void startActivity(Intent intent, Bundle options) {
mBase.startActivity(intent, options);
}
......
@Override
public void sendBroadcast(Intent intent) {
mBase.sendBroadcast(intent);
}
......
@Override
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
}
.....
}
Android的介面元件Activity、服務元件Service以及應用基類Application都派生於ContextWrapper,它們可以通過過載來修改Context介面的實現。
ContextThemeWrapper
public class ContextThemeWrapper extends ContextWrapper {
......
public ContextThemeWrapper(Context base, @StyleRes int themeResId) {
super(base);
mThemeResource = themeResId;
}
......
@Override
public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
}
return mInflater;
}
return getBaseContext().getSystemService(name);
}
......
}
Activity
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback {
private static final String TAG = "Activity";
private static final boolean DEBUG_LIFECYCLE = false;
/** 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;
/** @hide Task isn't finished when activity is finished */
public static final int DONT_FINISH_TASK_WITH_ACTIVITY = 0;
/**
* @hide Task is finished if the finishing activity is the root of the task. To preserve the
* past behavior the task is also removed from recents.
*/
public static final int FINISH_TASK_WITH_ROOT_ACTIVITY = 1;
/**
* @hide Task is finished along with the finishing activity, but it is not removed from
* recents.
*/
public static final int FINISH_TASK_WITH_ACTIVITY = 2;
static final String FRAGMENTS_TAG = "android:fragments";
private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState";
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 final String HAS_CURENT_PERMISSIONS_REQUEST_KEY =
"android:hasCurrentPermissionsRequest";
private static final String REQUEST_PERMISSIONS_WHO_PREFIX = "@android:requestPermissions:";
private static final String KEYBOARD_SHORTCUTS_RECEIVER_PKG_NAME = "com.android.systemui";
private static final String KEYBOARD_SHORTCUTS_RECEIVER_CLASS_NAME =
"com.android.systemui.statusbar.KeyboardShortcutsReceiver";
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;
/*package*/ String mReferrer;
private ComponentName mComponent;
/*package*/ ActivityInfo mActivityInfo;
/*package*/ ActivityThread mMainThread;
Activity mParent;
boolean mCalled;
/*package*/ boolean mResumed;
/*package*/ boolean mStopped;
boolean mFinished;
boolean mStartedActivity;
private boolean mDestroyed;
private boolean mDoReportFullyDrawn = true;
/** 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;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
/* 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*/ ActionBar mActionBar = null;
private boolean mEnableDefaultActionBarUp;
private VoiceInteractor mVoiceInteractor;
private CharSequence mTitle;
private int mTitleColor = 0;
// we must have a handler before the FragmentController is constructed
final Handler mHandler = new Handler();
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
// Most recent call to requestVisibleBehind().
boolean mVisibleBehind;
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 TranslucentConversionListener mTranslucentCallback;
private boolean mChangeCanvasToTranslucent;
private SearchEvent mSearchEvent;
private boolean mTitleReady = false;
private int mActionModeTypeStarting = ActionMode.TYPE_PRIMARY;
private int mDefaultKeyMode = DEFAULT_KEYS_DISABLE;
private SpannableStringBuilder mDefaultKeySsb = null;
private ActivityManager.TaskDescription mTaskDescription =
new ActivityManager.TaskDescription();
protected static final int[] FOCUSED_STATE_SET = {com.android.internal.R.attr.state_focused};
@SuppressWarnings("unused")
private final Object mInstanceTracker = StrictMode.trackActivity(this);
private Thread mUiThread;
ActivityTransitionState mActivityTransitionState = new ActivityTransitionState();
SharedElementCallback mEnterTransitionListener = SharedElementCallback.NULL_CALLBACK;
SharedElementCallback mExitTransitionListener = SharedElementCallback.NULL_CALLBACK;
private boolean mHasCurrentPermissionsRequest;
private boolean mEatKeyUpEvent;
private static native String getDlWarning();
/** 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;
}
......
}
Application
public class Application extends ContextWrapper implements ComponentCallbacks2 {
private ArrayList<ComponentCallbacks> mComponentCallbacks =
new ArrayList<ComponentCallbacks>();
private ArrayList<ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =
new ArrayList<ActivityLifecycleCallbacks>();
private ArrayList<OnProvideAssistDataListener> mAssistCallbacks = null;
......
@CallSuper
public void onLowMemory() {
Object[] callbacks = collectComponentCallbacks();
if (callbacks != null) {
for (int i=0; i<callbacks.length; i++) {
((ComponentCallbacks)callbacks[i]).onLowMemory();
}
}
}
@CallSuper
public void onTrimMemory(int level) {
Object[] callbacks = collectComponentCallbacks();
if (callbacks != null) {
for (int i=0; i<callbacks.length; i++) {
Object c = callbacks[i];
if (c instanceof ComponentCallbacks2) {
((ComponentCallbacks2)c).onTrimMemory(level);
}
}
}
}
public void registerComponentCallbacks(ComponentCallbacks callback) {
synchronized (mComponentCallbacks) {
mComponentCallbacks.add(callback);
}
}
public void unregisterComponentCallbacks(ComponentCallbacks callback) {
synchronized (mComponentCallbacks) {
mComponentCallbacks.remove(callback);
}
}
public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
synchronized (mActivityLifecycleCallbacks) {
mActivityLifecycleCallbacks.add(callback);
}
}
public void unregisterActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
synchronized (mActivityLifecycleCallbacks) {
mActivityLifecycleCallbacks.remove(callback);
}
}
public void registerOnProvideAssistDataListener(OnProvideAssistDataListener callback) {
synchronized (this) {
if (mAssistCallbacks == null) {
mAssistCallbacks = new ArrayList<OnProvideAssistDataListener>();
}
mAssistCallbacks.add(callback);
}
}
public void unregisterOnProvideAssistDataListener(OnProvideAssistDataListener callback) {
synchronized (this) {
if (mAssistCallbacks != null) {
mAssistCallbacks.remove(callback);
}
}
/**
* @hide
*/
/* package */ final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
/* package */ void dispatchActivityCreated(Activity activity, Bundle savedInstanceState) {
Object[] callbacks = collectActivityLifecycleCallbacks();
if (callbacks != null) {
for (int i=0; i<callbacks.length; i++) {
((ActivityLifecycleCallbacks)callbacks[i]).onActivityCreated(activity,
savedInstanceState);
}
}
}
/* package */ void dispatchActivityStarted(Activity activity) {
Object[] callbacks = collectActivityLifecycleCallbacks();
if (callbacks != null) {
for (int i=0; i<callbacks.length; i++) {
((ActivityLifecycleCallbacks)callbacks[i]).onActivityStarted(activity);
}
}
}
......
}
Service
public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
private static final String TAG = "Service";
......
public void onDestroy() {
}
public void onConfigurationChanged(Configuration newConfig) {
}
public void onLowMemory() {
}
public void onTrimMemory(int level) {
}
......
}
結尾附上Context的應用場景
tables | Application | Activity | Service | ContentProvider | BroadcastReceiver |
---|---|---|---|---|---|
顯示Dialog | NO | YES | NO | NO | NO |
啟動Activity | NO1 | YES | NO1 | NO1 | NO1 |
Layout Inflation | NO2 | YES | NO2 | NO2 | NO2 |
啟動Service | YES | YES | YES | YES | YES |
繫結到Service | YES | YES | YES | YES | NO |
傳送Broadcast | YES | YES | YES | YES | YES |
註冊BroadcastReceiver | YES | YES | YES | YES | NO3 |
載入Resource | YES | YES | YES | YES | YES |
注:
NO1 表示Application context的確可以開始一個Activity,但是它需要建立一個新的task。這可能會滿足一些特定的需求,但是在你的應用中會建立一個不標準的回退棧(back stack),這通常是不推薦的或者不是是好的實踐。
NO2 表示這是非法的,但是這個填充(inflation)的確可以完成,但是是使用所執行的系統預設的主題(theme),而不是你app定義的主題。
NO3 在Android4.2以上,如果Receiver是null的話(這是用來獲取一個sticky broadcast的當前 值的),這是允許的。