Android开发Android开发半栈工程师

Activity启动过程中你所不知道的细节

2018-10-06  本文已影响9人  小智pikapika

在安卓面试过程中,经常被问到以下几个问题:
1、Activity的生命周期
2、Activity启动后到页面展现出来的过程
3、应用启动过程
其实这些问题都可以在Activity的启动过程中(应用启动其实也是Activity启动的一个分支)寻找到答案,下面就让我们来探究下Activity启动过程到底经历了什么东西吧。

核心流程

先上一张核心的流程图:


startActivity.png

由上图可知,Activity启动涉及到应用层、framework层两个部分,应用层的交互主要由Instrumentation类承担,作为与framework层通讯的入口管理类,framework层主要涉及ActivityManager对全局Activity栈相关的管理、Application进程的管理以及最终ApplicationThread对Activity生命周期的调度。
整个过程涉及了两次AIDL跨进程通讯,第一次是从startActivity发起方的进程切换到系统进程,调用入口代码位于类ActivityManager中:

private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
 };

在系统进程中完成了Intent flag等数据处理以及Activity栈、TaskRecord的管理,然后再次使用AIDL跨进程调用,切换到应用进程完成Activity的创建及相关生命周期方法的回调。所以,要理清楚Activity的启动过程主要就是要理清楚framework层的代码流程,这时就需要去分析framework层的源码了,sdk里面是不包含framework层的代码的,需要翻墙查看:https://android.googlesource.com/platform/frameworks/base/+refs,笔者这边分析的是android-cts-7.1_r20 tag的代码,不同版本的代码可能会有细微的差异。

Framework源码分析

首先从最开始的地方也就是ActivityManagerService开始(代码地址:https://android.googlesource.com/platform/frameworks/base/+/android-cts-7.1_r20/services/core/java/com/android/server/am/ActivityManagerService.java

public class ActivityManagerService extends ActivityManagerNative
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
...
@Override
    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(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode, startFlags, profilerInfo, bOptions,
                UserHandle.getCallingUserId());
    }
    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);
    }
    @Override
    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) {
        enforceNotIsolatedCaller("startActivity");
        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                userId, false, ALLOW_FULL_ONLY, "startActivity", null);
        // TODO: Switch to user app stacks here.
        return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
                profilerInfo, null, null, bOptions, false, userId, null, null);
    }

  ...
}

由上述代码片段可以看出几点:ActivityManager继承了IActivityManager.Stub,熟悉AIDL的同学应该一眼就能看出这是AIDL的特点;startActivity最终由mActivityStarter执行,然后顺着代码一步步往下分析即可。
最终经过一系列方法调用:ActivityStarter#startActivityUnchecked->
ActivityStackSupervisor#resumeFocusedStackTopActivityLocked->
ActivityStack#resumeTopActivityUncheckedLocked->
ActivityStack#resumeTopActivityInnerLocked->
ActivityStackSupervisor#startSpecificActivityLocked->
ApplicationThread#schedule*完成了Activity的启动过程。(有兴趣的同学可以从ActivityManagerService类开始一步步分析下去。)
其中,我们只需要分析核心类ActivityStackActivityStackSupervisorApplicationThread即可。
首先看ActivityStack#resumeTopActivityInnerLocked方法

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
        ···
        // We need to start pausing the current activity so the top one can be resumed...
        final boolean dontWaitForPause = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0;
        boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, dontWaitForPause);
        if (mResumedActivity != null) {
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Pausing " + mResumedActivity);
            pausing |= startPausingLocked(userLeaving, false, next, dontWaitForPause);
        }
        ···
        mStackSupervisor.startSpecificActivityLocked(next, true, true);
        ···
}

由上述代码片段可知,Activity启动之前,必须先pause当前Activity,这里最终也会调用到ApplicationThread里面的方法,具体就不细说了。
然后再看ActivityStackSupervisor#startSpecificActivityLocked方法,最终发现同样是调用到了app.thread中的调度方法,这也是整个过程的第二次AIDL跨进程调用。

public final class ActivityStackSupervisor implements DisplayListener {
       ...
       void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        // Is this activity's application already running?
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);
        r.task.stack.setLaunchTime(r);
        if (app != null && app.thread != null) {
            try {
                if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
                        || !"android".equals(r.info.packageName)) {
                    // Don't add this if it is a platform component that is marked
                    // to run in multiple processes, because this is actually
                    // part of the framework so doesn't make sense to track as a
                    // separate apk in the process.
                    app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
                            mService.mProcessStats);
                }
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }
            // If a dead object exception was thrown -- fall through to
            // restart the application.
        }
        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true);
    }

    final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
            boolean andResume, boolean checkConfig) throws RemoteException {
        ...
        app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
                    new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage,
                    task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
                    newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
        ...
    }
    ...
}

最后看ApplicationThread#scheduleLaunchActivity方法(注意ApplicationThread是ActivityThread的内部类哦)

// we use token to identify this activity without having to send the
        // activity itself back to the activity manager. (matters more with ipc)
        @Override
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
            ...
            sendMessage(H.LAUNCH_ACTIVITY, r);
        }

这里就是采用message机制的地方,通过message机制通知Handler H(也是ActivityThread的内部类)完成了Activity的启动(大家可以思考下为什么这里用了message机制,笔者认为message机制主要是可以确保应用层Activity启动的过程都是在主线程中完成的)

后续的代码片段如下:

Handler H:
public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
              }
              ...
}

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
       Activity a = performLaunchActivity(r, customIntent);
       ...
       handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
       ...
}

//创建Activity并通过Instrumentation调用Activity的onCreate方法
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
            ...
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            ...
            mInstrumentation.callActivityOnCreate(activity, r.state);
            ....
}

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
                ...
                r = performResumeActivity(token, clearHide, reason);
                ...
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ...
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                }
}

由上面的代码片段可知Activity启动顺序为onCreate->onResume->DecorView绘制。
综上所述可以得知Activity启动生命周期为:
Activity source onPause->Activity target onCreate->Activity target onResume->页面绘制(onStop的调用周期没有找到就不列了)
最初的问题1,2也就有了答案了。

至于问题3,首先要明确的是,Android的桌面其实也是一个Activity,应用启动其实也是startActivity的过程,唯一的区别在于此时应用进程还未创建,回到ActivityStackSupervisor#startSpecificActivityLocked方法可以发现,应用启动的分支就在此处,当应用进程不存在时,会创建一个新的进程。

public final class ActivityStackSupervisor implements DisplayListener {
       ...
       void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        // Is this activity's application already running?
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);
        r.task.stack.setLaunchTime(r);
        if (app != null && app.thread != null) {
            try {
                if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
                        || !"android".equals(r.info.packageName)) {
                    // Don't add this if it is a platform component that is marked
                    // to run in multiple processes, because this is actually
                    // part of the framework so doesn't make sense to track as a
                    // separate apk in the process.
                    app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
                            mService.mProcessStats);
                }
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }
            // If a dead object exception was thrown -- fall through to
            // restart the application.
        }
        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true);
    }
}

除了应用进程的创建,应用启动过程的区别还包括ActivityThread的创建、向ActivityManagerService绑定新建的ApplicationThread、创建UI Thread等操作,然后就是MainActivity的启动过程与上述情况一至,具体流程就不详细列举了。

总结

通过上述framework层代码的分析,我们大致了解了Activity启动的过程,其中笔者最重要的还是最开始的流程图,我们主要需要了解整体的工作原理,对于代码细节没必要死抠。

整体看下来,AIDL跨进程通讯贯穿了整个Acitivity的生命周期,这其实也是Android系统整体的一个设计思路:通过一系列framework层的ManagerService来实现和管理各种应用功能,相当于C/S架构,这些service相当于服务端,对于我们应用开发者只暴露基础的接口,具体实现封装在framework层可以随时升级替换,应用层通过AIDL向“服务端”发起请求获取各种基础功能服务。

上一篇 下一篇

猜你喜欢

热点阅读