【Android原始碼】Intent 原始碼分析

指間沙似流年發表於2017-12-23

在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呢?

  1. app資訊表的構建

    參見PackageManagerService 淺析

  2. 匹配

    當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的核心,它的大致過程如下:

    1. 首先獲取Intent的Component物件,如果不為空,說明指定了Componet,那麼就直接通過Componet找到ActivityInfo列表,並且這個列表size為1,所以這個ActivityInfo就是指定需要跳轉的元件。
    2. 如果沒有指定Component,那就是隱式Intent呼叫,接著獲取Intent傳遞的需要跳轉的包名。
    3. 如果包名為空,則會通過ActivityIntentResolver等進行模糊匹配,比如根據Action、Category等。
    4. 如果包名不為空,則直接根據包名來獲取到對應的ActivityInfo物件,而mActivities就是PMS儲存的activity資訊表。

相關文章