面試題:Android 中 Intent 採用了什麼設計模式?
答案是採用了原型模式。
原型模式的好處在於方便地拷貝某個例項的屬性進行使用、又不會對原例項造成影響,其邏輯在於對 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 相關的屬性,即用來判斷啟動目標元件的 action、data、type、component、category 等必備資訊。無視啟動 flag、bundle 等資料。
// 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; } }
-
- 面試題:Android 中 Intent 採用了什麼設計模式?
- 終於理解~Android 模組化裡的資源衝突
- 跳槽、換房、堅持輸出,與你分享我匆忙的 2022~
- 別搞錯了,nonTransitiveRClass 不能解決資源衝突!
- 從 internal 修飾符一探 kotlin 的可見性控制
- 一文吃透 Kotlin 中眼花繚亂的函式家族...
- 開發這麼久,gradle 和 gradlew 啥區別、怎麼選?
- 能說一說 Kotlin 中 lateinit 和 lazy 的區別嗎?
- M1 Pro 折騰一年的收穫:一堆“哇塞”的技巧和 App
- 如何打造車載語音互動:Google Voice Interaction 給你答案
- Android 車機初體驗:Auto,Automotive 傻傻分不清楚?
- 深入分析 Android 系統返回手勢的實現原理
- Android 13 返回導航大變更:返回鍵徹底廢棄 可預見型返回手勢
- 從顯示 Tap 位置的原理一探 Android Input 系統
- Android 巢狀 Intent 的隱患以及解決方案
- Android 13 針對 Intent filters 安全的再加強
- Android 13 新的換行策略和針對日文的優化
- 電子廠裡撂了挑子,我默默自學起了Android|2021 年中總結
- 鴻蒙Harmony談了這麼久,和Android到底啥區別?
- Jetpack新成員SplashScreen:打造全新的App啟動畫面