C/C++教程

源码阅读体验 - Activity启动

本文主要是介绍源码阅读体验 - Activity启动,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

这是第一次真正意义上的阅读源码,文章的目的是记录一下阅读源码的体验和心路历程,探索一下自己后续应该怎样阅读源码和使用源码;文章中可能会存在错误和理解偏差,这些不是文章的重点,各位大佬留情。

这篇不是技术文章,请勿参考 这篇不是技术文章,请勿参考 这篇不是技术文章,请勿参考

Activity的启动流程最初由Zygote发起,通过AMS来启动Activity,其中的过程可以总结为Launcher => AMS,AMS => ApplicationThread,ApplicationThread => Activity

Launcher请求AMS

1. 点击桌面图标后,Launcher调用startActivitySalely() => 这里会执行Launcher的父类BaseDraggingActivity中对应的方法
// BaseDraggingActivity源码节选
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
        
        ...省略部分...

        // Prepare intent
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (v != null) {
            intent.setSourceBounds(getViewBounds(v));
        }
        try {
            ...省略部分...
            
            if (isShortcut) {
                // 快捷入口shortcut的处理逻辑
                startShortcutIntentSafely(intent, optsBundle, item);
            } else if (user == null || user.equals(Process.myUserHandle())) {
                // Activity启动路径
                startActivity(intent, optsBundle);
            }
            ...省略部分...
            
        return false;
    }
  • addFlag(NEW_TASK)
  • startActivity(intent, bundle) => 内部会调用Activity中的startActivityForResult(intent,reqestCode,options)
    PS:shortcut组件的点击处理逻辑也在这个方法同级处理,以后有机会深入看看
2. 逻辑进入Activity的startActivityForResult,通过Instrumentation执行execStartActivity()
// Activity源码节选
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        // 这里mParent指的是Activity类型,判断是否存在父Activity
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            ...省略部分...
            if (requestCode >= 0) {
                mStartedActivity = true;
            }
            cancelInputsAndStartExitTransition(options);
        }
        ...省略部分...
    }
    
    
public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
    
        ...省略部分...
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess(who);
        int result = ActivityManager.getService().startActivity(whoThread, who.getBasePackageName(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()),token, target != null ? target.mEmbeddedID : null,requestCode, 0, null, options);
        checkStartActivityResult(result, intent);
        return null;
    }    
    
  • 首先会判断mParent是否为空,在启动过程中mParent自然也是空的,此时就会进入根Activity的启动过程
  • 传递的requestCode<0,在startActivityForResult的逻辑中,不会将mStartActivity标记为true,也就不会有返回值内容
  • Instrumentation的实例会执行execStartActivity方法。(Instrumentation实例是在Activity的attach方法中传递进入Activity的)
3. execStartActivity中对启动的主要内容在于通过ActivityManager获取ActivityManagerService实例并调用其中的startActivity实现Activity的启动
// ActivityManager源码节选
public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}
    
private static final Singleton<IActivityManager> IActivityManagerSingleton =
    new Singleton<IActivityManager>() {
        @Override
        protected IActivityManager create() {
            final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
            final IActivityManager am = IActivityManager.Stub.asInterface(b);
            return am;
        }
    };
  • ActivityManagerService实例是由ActivityManager.getService()内部通过AIDL获取到的ACTIVITY_SERVICE单例对象
  • 通过ActivityManagerService调用startActivity()

总的来看,这一阶段就是Activity及其子类借助Instrumentation来获取AMS实例,可以说应用启动也是通过Activity发起的,而这也就是一个启动Activity的过程。


还行,我还可以


AMS调用ApplicationThread

1. 内部调用startActivityAsUser方法,验证调用者进程是否隔离;是否拥有权限;之后通过ActivityStarter调用startActivityMayWait
  • 通过ActivityStartController调用obtainStarter获取到ActivityStarter实例,之后执行execute。这里参考的是《Android进阶解密》
  • 相比于Android8.0,在10版本已经把原先的ActivityStarter调用startActivityMayWait的逻辑通过ActivityStartController封装了一次,内部使用mayWait来决定调用startActivity还是startActivityMayWait。
ActivityManagerService源码节选

public final int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
        return startActivityAsUser(......, UserHandle.getCallingUserId());
    }
    
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
            boolean validateIncomingUser) {
        // 判断调用者进程是否被隔断,入参是在抛出异常时加入Log的,内容并不影响判断结果
        // 内部获取调用者uid后,通过UserHandler来判断uid是否在隔断范围内
        enforceNotIsolatedCaller("startActivity");

        // 权限判断同样通过ActivityStartController进行封装
        userId = mActivityStartController.checkTargetUser(userId, validateIncomingUser,
                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");

        return mActivityStartController.obtainStarter(intent, "startActivityAsUser")
                .setCaller(caller)
                .setCallingPackage(callingPackage)
                .setResolvedType(resolvedType)
                .setResultTo(resultTo)
                .setResultWho(resultWho)
                .setRequestCode(requestCode)
                .setStartFlags(startFlags)
                .setProfilerInfo(profilerInfo)
                .setActivityOptions(bOptions)
                .setMayWait(userId)
                .execute();     // 建造者模式
    }    
2. 逻辑进入startActivtyMayWait后会继续调用startActivity方法,接着会继续调用同名重载方法
ActivityStarter源码节选
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, String reason,
            boolean allowPendingRemoteAnimationRegistryLookup) {

        // 启动原因为空抛出异常,这里的reason是从AMS传递过来的
        if (TextUtils.isEmpty(reason)) {
            throw new IllegalArgumentException("Need to specify a reason.");
        }
        
        ...省略部分...

        mLastStartActivityResult = startActivity(caller, intent, ephemeralIntent, resolvedType,
                aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
                callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
                options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,
                inTask, allowPendingRemoteAnimationRegistryLookup);

        ...省略部分...

        return getExternalResult(mLastStartActivityResult);
    }


emm…

  1. 经过一系列的调用,最终在ActivityStarter中的startActivity中获取Launcher进程,借此获取进程信息(ProcessRecord),在通过它创建ActivityRecord并继续传递,经过ActivityStack中处理最终调取到ApplicationThread叼起scheduleLaunchActivity方法。这部分主要是处理Activity启动时的模式选择和生命周期判断。

ActivityThread启动Activity

将一路传递的参数转换为ActivityClientRecord,之后获取packageInfo,加载Activity所属的Apk,之后将Activity状态职位Resume,获取ActivityInfo存储AndroidManifest设置的Activity节点信息,最终通过Instrumentation调起performCreate启动Activity,进入Activity的onCreate。


总结

其实整个阅读流程已经非常清晰了,从最开始信心满满细致入微到逐渐暴躁失去耐心最后彻底掀桌,第一次的源码阅读从知识层面来说并没有什么收获。我也确定了源码不是这么读滴,把源码当成demo读我也是有一手的(叉腰,也可能是我的功力太浅导致的,之后的源码阅读不能这么整了。有问题求解时就仔细观摩一处,单纯膜拜时就放弃细节。啥都想要太消耗精力也太低效了。

ok,历时很久以后终于又沉淀出了一丢丢东西,希望能再接再厉继续年更吧!ヽ(゚∀゚)メ(゚∀゚)ノ

这篇关于源码阅读体验 - Activity启动的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!