Android判斷Activity是否在AndroidManifest.xml裏面註冊(源碼分析)

Android判斷Activity是否在AndroidManifest.xml裏面註冊(源碼分析)


這個問題相信大家在實際的開發中,都遇到過這個問題,答案就不用說了,在AndroidManifest.xml中添加Activity的註冊,畢竟Activity屬於四大組件之一,使用的時候,需要要在清單文件中註冊。

<activity android:name=".TargetActivity"></activity>

但是這個出現這個問題的根源在哪裏?下面我們就進入源碼仔細看看。

這裏就不一步一步進入源碼,直接分析關鍵代碼:

public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) { ...
     try {
      intent.migrateExtraStreamToClipData();
      intent.prepareToLeaveProcess(who);
     //1.通過IActivityManager調用我們執行AMS的startActivity方法,並返回執行 結果
      int result = ActivityManager.getService() .startActivity(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
     //2. 檢查結果
     checkStartActivityResult(result, intent);
     } catch (RemoteException e) {
         throw new RuntimeException("Failure from system", e);
     }
      return null;
  }

通過源碼execStartActivity這個方法可以看到主要是在這個檢查結果這裏面去分析的checkStartActivityResult(result, intent);

public static void checkStartActivityResult(int res, Object intent) {
        if (!ActivityManager.isStartResultFatalError(res)) {
            return;
        }
        switch (res) {
            case ActivityManager.START_INTENT_NOT_RESOLVED:
            case ActivityManager.START_CLASS_NOT_FOUND:
                //3. 這裏我們找到了報錯的地方,原來是res結果爲 START_INTENT_NOT_RESOLVED,
                // START_CLASS_NOT_FOUND就會報這個錯誤
                if (intent instanceof Intent && ((Intent) intent).getComponent() != null)
                    throw new ActivityNotFoundException("Unable to find explicit activity class " + ((Intent) intent).getComponent().toShortString() + "; have you declared this activity in your AndroidManifest.xml?");
                throw new ActivityNotFoundException("No Activity found to handle " + intent);
            case ActivityManager.START_PERMISSION_DENIED:
                throw new SecurityException("Not allowed to start activity " + intent);
            case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
                throw new AndroidRuntimeException("FORWARD_RESULT_FLAG used while also requesting a result");
            case ActivityManager.START_NOT_ACTIVITY:
                throw new IllegalArgumentException("PendingIntent is not an activity");
            case ActivityManager.START_NOT_VOICE_COMPATIBLE:
                throw new SecurityException("Starting under voice control not allowed for: " + intent);
            case ActivityManager.START_VOICE_NOT_ACTIVE_SESSION:
                throw new IllegalStateException("Session calling startVoiceActivity does not match active session");
            case ActivityManager.START_VOICE_HIDDEN_SESSION:
                throw new IllegalStateException("Cannot start voice activity on a hidden session");
            case ActivityManager.START_ASSISTANT_NOT_ACTIVE_SESSION:
                throw new IllegalStateException("Session calling startAssistantActivity does not match active session");
            case ActivityManager.START_ASSISTANT_HIDDEN_SESSION:
                throw new IllegalStateException("Cannot start assistant activity on a hidden session");
            case ActivityManager.START_CANCELED:
                throw new AndroidRuntimeException("Activity could not be started for " + intent);
            default:
                throw new AndroidRuntimeException("Unknown error code " + res + " when starting " + intent);
        }
    }

可以看到當結果爲

case ActivityManager.START_INTENT_NOT_RESOLVED:

case ActivityManager.START_CLASS_NOT_FOUND:

就會報
throw new ActivityNotFoundException("Unable to find explicit activity class " + ((Intent) intent).getComponent().toShortString() + "; have you declared this activity in your AndroidManifest.xml?");

這下我們就知道了如果沒在清單文件中添加這個註冊,報錯的位置。

AMS是如何判斷activity沒有註冊的,首先我們得明白startActivity執行的主流程

這個篇幅太多了,可以自己去源碼跟一下,這裏不作介紹,

我們這裏分析主要流程代碼

找到在ASR.startActivity (ActivityStarter)中返回了

START_INTENT_NOT_RESOLVED,START_CLASS_NOT_FOUND

private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent, String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid, String callingPackage, int realCallingPid, int realCallingUid, int startFlags, SafeActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity, TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup) {
        int err = ActivityManager.START_SUCCESS;
        ...
        //接下來開始做一些校驗判斷
        if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
            // We couldn't find a class that can handle the given Intent.
            // That's the end of that! err = ActivityManager.START_INTENT_NOT_RESOLVED;
            // 從Intent中無法找 到相應的Component
        }
        if (err == ActivityManager.START_SUCCESS && aInfo == null) {
            // We couldn't find the specific class specified in the Intent.
            // Also the end of the line.
            err = ActivityManager.START_CLASS_NOT_FOUND;
            // 從Intent中無法找到相 應的ActivityInfo
        }
                 ...
        if (err != START_SUCCESS) {
            //不能成功啓動了,返回err
            if (resultRecord != null) {
                resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null);
            }
            SafeActivityOptions.abort(options);
            return err;
        }
        //創建出我們的目標ActivityRecord對象,存到傳入數組0索引上
        ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid, callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null, mSupervisor, checkedOptions, sourceRecord);
        ...
        return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask, outActivity);
    }

但是 intent.getComponent(),aInfo又是從哪兒獲取的呢,我們回溯到

startActivityMayWait.

看下上面的aInfo哪來的.

ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags, ProfilerInfo profilerInfo) {
        final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null;
        if (aInfo != null) {
            // Store the found target back into the intent, because now that
            // we have it we never want to do this again. For example, if the
            // user navigates back to this point in the history, we should
            // always restart the exact same activity.
            intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
            // Don't debug things in the system process ...
        }
        return aInfo;
    }

發現是從rInfo來的

ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags, int filterCallingUid) {
        synchronized (mService) {
            try {...final long token = Binder.clearCallingIdentity();
                try {
                    return mService.getPackageManagerInternalLocked().resolveIntent(intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
                } finally {
                    Binder.restoreCallingIdentity(token);
                } ...
            }
        }
    }

rInfo的獲取

PackageManagerInternal getPackageManagerInternalLocked() {
        if (mPackageManagerInt == null) {
            mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
        }
        return mPackageManagerInt;
    }

具體實現類是PackageManagerService

@Override
    public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) {
        return resolveIntentInternal(intent, resolvedType, flags, userId, false, Binder.getCallingUid());
    }

看resolveIntentInternal

private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,int flags, int userId, boolean resolveForStart, int filterCallingUid) {
        try {...
            //獲取ResolveInfo列表
            final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags, filterCallingUid, userId, resolveForStart, true /*allowDynamicSplits*/);
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            //找出最好的返回
            final ResolveInfo bestChoice = chooseBestActivity(intent, resolvedType, flags, query, userId);
            return bestChoice;
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }

看 queryIntentActivitiesInternal

private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) {
        ...
        if (comp != null) {
            final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
            final ActivityInfo ai = getActivityInfo(comp, flags, userId);
            if (ai != null) {
                ...
            }
        }

原來是從getActivityInfo獲取的

    @Override
    public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
        return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId);
    }

getActivityInfoInternal方法

private ActivityInfo getActivityInfoInternal(ComponentName component, int flags, int filterCallingUid, int userId) {
        if (!sUserManager.exists(userId)) return null;
        flags = updateFlagsForComponent(flags, userId, component);
        if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
            mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get activity info");
        }
        synchronized (mPackages) {
            //關鍵點
            PackageParser.Activity a = mActivities.mActivities.get(component);
            if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);
            if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) {
                PackageSetting ps = mSettings.mPackages.get(component.getPackageName());
                if (ps == null) return null;
                if (filterAppAccessLPr(ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
                    return null;
                }
                //關鍵點
                return PackageParser.generateActivityInfo(a, flags, ps.readUserState(userId), userId);
            }
            if (mResolveComponentName.equals(component)) {
                return PackageParser.generateActivityInfo(mResolveActivity, flags, new PackageUserState(), userId);
            }
        }
        return null;
    }

分析到這裏,大家應該知道怎麼回事了吧,其實就是解析了AndroidManifest.xml裏面的信息,具體怎麼解析,等有空了分析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章