面試題:Android 中 Intent 採用了什麼設計模式?

語言: CN / TW / HK

答案是採用了原型模式

原型模式的好處在於方便地拷貝某個例項的屬性進行使用、又不會對原例項造成影響,其邏輯在於對 Cloneable 介面的實現。

話不多說看下 Intent 的關鍵原始碼:

// frameworks/base/core/java/android/content/Intent.java  public class Intent implements Parcelable, Cloneable {     ...      private static final int COPY_MODE_ALL = 0;      private static final int COPY_MODE_FILTER = 1;      private static final int COPY_MODE_HISTORY = 2;  ​      @Override      public Object clone() {          return new Intent(this);     }  ​      public Intent(Intent o) {          this(o, COPY_MODE_ALL);     }  ​      private Intent(Intent o, @CopyMode int copyMode) {          this.mAction = o.mAction;          this.mData = o.mData;          this.mType = o.mType;          this.mIdentifier = o.mIdentifier;          this.mPackage = o.mPackage;          this.mComponent = o.mComponent;          this.mOriginalIntent = o.mOriginalIntent;         ...  ​          if (copyMode != COPY_MODE_FILTER) {             ...              if (copyMode != COPY_MODE_HISTORY) {                 ...             }         }     }     ...  }

可以看到 Intent 實現的 clone() 邏輯是直接呼叫了 new 並傳入了自身例項,而非呼叫 super.clone() 進行拷貝。

預設的拷貝策略是 COPY_MODE_ALL,顧名思義,將完整拷貝源例項的所有屬性進行構造。其他的拷貝策略是 COPY_MODE_FILTER 指的是隻拷貝跟 Intent-filter 相關的屬性,即用來判斷啟動目標元件的 actiondatatypecomponentcategory 等必備資訊。無視啟動 flagbundle 等資料。

// frameworks/base/core/java/android/content/Intent.java  public class Intent implements Parcelable, Cloneable {     ...      public @NonNull Intent cloneFilter() {          return new Intent(this, COPY_MODE_FILTER);     }  ​      private Intent(Intent o, @CopyMode int copyMode) {          this.mAction = o.mAction;         ...  ​          if (copyMode != COPY_MODE_FILTER) {              this.mFlags = o.mFlags;              this.mContentUserHint = o.mContentUserHint;              this.mLaunchToken = o.mLaunchToken;             ...         }     }  }

還有中拷貝策略是 COPY_MODE_HISTORY,不需要 bundle 等歷史資料,保留 action 等基本資訊和啟動 flag 等資料。

// frameworks/base/core/java/android/content/Intent.java  public class Intent implements Parcelable, Cloneable {     ...      public Intent maybeStripForHistory() {          if (!canStripForHistory()) {              return this;         }          return new Intent(this, COPY_MODE_HISTORY);     }  ​      private Intent(Intent o, @CopyMode int copyMode) {          this.mAction = o.mAction;         ...  ​          if (copyMode != COPY_MODE_FILTER) {             ...              if (copyMode != COPY_MODE_HISTORY) {                  if (o.mExtras != null) {                      this.mExtras = new Bundle(o.mExtras);                 }                  if (o.mClipData != null) {                      this.mClipData = new ClipData(o.mClipData);                 }             } else {                  if (o.mExtras != null && !o.mExtras.isDefinitelyEmpty()) {                      this.mExtras = Bundle.STRIPPED;                 }             }         }     }  }

總結起來:

| Copy Mode | action 等資料 | flags 等資料 | bundle 等歷史 | | ----------------- | ---------- | --------- | ---------- | | COPY_MODE_ALL | YES | YES | YES | | COPY_MODE_FILTER | YES | NO | NO | | COPY_MODE_HISTORY | YES | YES | NO |

除了 Intent,Android 原始碼中還有很多地方採用了原型模式。

  • Bundle 也實現了 clone(),提供了 new Bundle(this) 的處理:

    public final class Bundle extends BaseBundle implements Cloneable, Parcelable {     ...      @Override      public Object clone() {          return new Bundle(this);     }  }

  • 元件資訊類 ComponentName 也在 clone() 中提供了類似的實現:

    public final class ComponentName implements Parcelable, Cloneable, Comparable<ComponentName> {     ...      public ComponentName clone() {          return new ComponentName(mPackage, mClass);     }  }

  • 工具類 IntArray 亦是如此:

    public class IntArray implements Cloneable {     ...      @Override      public IntArray clone() {          return new IntArray(mValues.clone(), mSize);     }  }

原型模式也不一定非得實現 Cloneable,提供了類似的實現即可。比如:

  • Bitmap 沒有實現該介面但提供了 copy(),內部將傳遞原始 Bitmap 在 native 中的物件指標並伴隨目標配置進行新例項的建立:

    public final class ComponentName implements Parcelable, Cloneable, Comparable<ComponentName> {     ...      public Bitmap copy(Config config, boolean isMutable) {         ...          noteHardwareBitmapSlowCall();          Bitmap b = nativeCopy(mNativePtr, config.nativeInt, isMutable);          if (b != null) {              b.setPremultiplied(mRequestPremultiplied);              b.mDensity = mDensity;         }          return b;     }  }

-