在Android中,我們經常需要使用到Intent類,它用於跳轉Activity、啟動Service、釋出廣播等功能,它是系統各元件之間的紐帶,也可以通過Intent傳遞資料,因為有Intent才使得Android的元件耦合度降低。
原型模式
首先我們跳轉到Intent.java:
public class Intent implements Parcelable, Cloneable {
}
複製程式碼
我們可以發現Intent實現了Cloneable介面,所以我們可以得出一個結論,Intent使用了原型設計模式,(原型設計模式介紹參考:點選此處)我們搜尋clone()方法:
@Override
public Object clone() {
return new Intent(this);
}
public Intent(Intent o) {
this.mAction = o.mAction;
this.mData = o.mData;
this.mType = o.mType;
this.mPackage = o.mPackage;
this.mComponent = o.mComponent;
this.mFlags = o.mFlags;
this.mContentUserHint = o.mContentUserHint;
if (o.mCategories != null) {
this.mCategories = new ArraySet<String>(o.mCategories);
}
if (o.mExtras != null) {
this.mExtras = new Bundle(o.mExtras);
}
if (o.mSourceBounds != null) {
this.mSourceBounds = new Rect(o.mSourceBounds);
}
if (o.mSelector != null) {
this.mSelector = new Intent(o.mSelector);
}
if (o.mClipData != null) {
this.mClipData = new ClipData(o.mClipData);
}
}
複製程式碼
Intent如何工作
Intent在使用的時候,可以通過新增flag、category還有需要跳轉的物件等來實現功能,那麼Intent是如何通過查詢並匹配所需要跳轉的Activity呢?
-
app資訊表的構建
-
匹配
當app資訊表被構建好之後,Intent就可以通過資訊表來匹配。
我們以啟動某個具體的Activity來分析,首先我們啟動的程式碼是這樣的:
// 顯式Intent Intent intent = new Intent(this, SecondActivity.class); startActivity(intent); // 隱式Intent Intent intent = new Intent(Intent.ACTION_SENDTO); startActivity(intent); 複製程式碼
startActivity方法,通過一系列的呼叫:
@Override public void startActivity(Intent intent) { this.startActivity(intent, null); } @Override public void startActivity(Intent intent, @Nullable Bundle options) { if (options != null) { startActivityForResult(intent, -1, options); } else { // Note we want to go through this call for compatibility with // applications that may have overridden the method. startActivityForResult(intent, -1); } } public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) { if (mParent == null) { // 啟動Activity Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); // 傳送啟動請求 if (ar != null) { mMainThread.sendActivityResult( mToken, mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData()); } cancelInputsAndStartExitTransition(options); } else { } } 複製程式碼
最終呼叫的是startActivityForResult方法,在這個方法中直接呼叫execStartActivity方法啟動Activity:
public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { IApplicationThread whoThread = (IApplicationThread) contextThread; Uri referrer = target != null ? target.onProvideReferrer() : null; try { // 將Intent資料新增到剪下板上 intent.migrateExtraStreamToClipData(); // 準備離開當前程式 intent.prepareToLeaveProcess(who); // 呼叫ActivityManagerService的startActivity方法 int result = ActivityManagerNative.getDefault() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, options); // 檢查並回撥給呼叫者 checkStartActivityResult(result, intent); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } return null; } 複製程式碼
execStartActivity方法裡面其實就是呼叫了ActivityManagerService的startActivity方法:
final int startActivity(Intent intent, ActivityStackSupervisor.ActivityContainer container) { enforceNotIsolatedCaller("ActivityContainer.startActivity"); final int userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), mStackSupervisor.mCurrentUser, false, ActivityManagerService.ALLOW_FULL_ONLY, "ActivityContainer", null); // TODO: Switch to user app stacks here. String mimeType = intent.getType(); final Uri data = intent.getData(); if (mimeType == null && data != null && "content".equals(data.getScheme())) { mimeType = getProviderMimeType(data, userId); } container.checkEmbeddedAllowedInner(userId, intent, mimeType); intent.addFlags(FORCE_NEW_TASK_FLAGS); return mActivityStarter.startActivityMayWait(null, -1, null, intent, mimeType, null, null, null, null, 0, 0, null, null, null, null, false, userId, container, null); } 複製程式碼
這個方法會呼叫ActivityStarter的startActivityMayWait方法,這個方法中又會呼叫ActivityStackSupervisor.resolveIntent方法,而這個方法就是呼叫的PMS的resolveIntent方法:
// ActivityStackSupervisor.java ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) { try { return AppGlobals.getPackageManager().resolveIntent(intent, resolvedType, PackageManager.MATCH_DEFAULT_ONLY | flags | ActivityManagerService.STOCK_PM_FLAGS, userId); } catch (RemoteException e) { } return null; } // PackageManagerService.java @Override public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent"); if (!sUserManager.exists(userId)) return null; flags = updateFlagsForResolve(flags, userId, intent); enforceCrossUserPermission(Binder.getCallingUid(), userId, false /*requireFullPermission*/, false /*checkShell*/, "resolve intent"); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities"); // 獲取列表 final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); // 通過列表選擇出最合適的info物件 final ResolveInfo bestChoice = chooseBestActivity(intent, resolvedType, flags, query, userId); if (isEphemeralAllowed(intent, query, userId)) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral"); final EphemeralResolveInfo ai = getEphemeralResolveInfo(intent, resolvedType, userId); if (ai != null) { if (DEBUG_EPHEMERAL) { Slog.v(TAG, "Returning an EphemeralResolveInfo"); } bestChoice.ephemeralInstaller = mEphemeralInstallerInfo; bestChoice.ephemeralResolveInfo = ai; } Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } return bestChoice; } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } 複製程式碼
queryIntentActivitiesInternal方法返回的結果就是符合intent的ActivityInfo列表:
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int userId) { // 獲取Intent的Component物件 ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } // 不為空,是顯式Intent,直接獲取到ActivityInfo返回 if (comp != null) { final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); final ActivityInfo ai = getActivityInfo(comp, flags, userId); if (ai != null) { final ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; list.add(ri); } return list; } // 為空,是隱式Intent synchronized (mPackages) { final String pkgName = intent.getPackage(); if (pkgName == null) { // 程式碼省略 return result; } // 通過包名獲取到Package物件 final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { // 在獲取到ActivityInfo物件 return filterIfNotSystemUser( mActivities.queryIntentForPackage( intent, resolvedType, flags, pkg.activities, userId), userId); } return new ArrayList<ResolveInfo>(); } } 複製程式碼
上面這個方法就是Intent獲取到ActivityInfo的核心,它的大致過程如下:
- 首先獲取Intent的Component物件,如果不為空,說明指定了Componet,那麼就直接通過Componet找到ActivityInfo列表,並且這個列表size為1,所以這個ActivityInfo就是指定需要跳轉的元件。
- 如果沒有指定Component,那就是隱式Intent呼叫,接著獲取Intent傳遞的需要跳轉的包名。
- 如果包名為空,則會通過ActivityIntentResolver等進行模糊匹配,比如根據Action、Category等。
- 如果包名不為空,則直接根據包名來獲取到對應的ActivityInfo物件,而mActivities就是PMS儲存的activity資訊表。