我们通常说Android有四大组件,也有说法是五大组件,将Intent也算一种,不管是五大还是四大,它们都是Android app开发中最经常打交道的东西,刚开始的时候也都会碰到一些坑,很多时候我们都是搜索资料,看其他人写的博客来学习和避坑,其实很多博客资料也就是对官方文档的翻译,加上一些自己避坑心得的记录,有些时候即使通过一些资料解决了某个问题,但其实自己也说不出个所以然来,也是云里雾里,只是记住结论就行了,有些时候身边一些同事会说,做应用层的,不用太深入底层,先把功能需求做好就行了。这话是有一定道理,但我想,他们说的底层到底是哪一层呢?这可能就是很多程序员自嘲为码农的原因吧,不管是做什么开发,如果只是停留在API的熟练使用,即使再干十年,也只是在搬砖吧,所以我个人认为,从初级工程师进阶,最关键的一步就是要了解源码,要学习源码,而不是掌握多少流行库的调用。Read the fucking source code!另外讨论一个问题,什么是底层呢?我认为涉及到Java的都不能算底层,只有到C/C++代码那一层才能算底层,因此Framework不能称为底层。

这一篇博客就从Activity的启动流程开始写吧,这里以Android 6.0源码为例,其他版本或许会有不同。这篇博客力求简单通俗,给应用的开发带来一些启示,因此不会深入底层,不会涉及C/C++层,更不会涉及Linux内核的一些原理,因为博主也不甚了解,掩面而逃……

Activity启动流程

通常启动Activity是有三种方式的

  • 点击桌面上的图标
  • 在我们当前应用中startActivity
  • Am命令启动,在进阶第一篇中有详细讲到

事实上第一种和第二种是差不多的,Android系统的桌面就是一个应用。那我们就从startActivity方法开始跟踪

Activity.java

Activity中最终调用的是

这里mParent应该为空,如不确定,继续查看mParent不为空的情况,则调用如下方法

mParent.startActivityFromChild(this, intent, requestCode);

Instrumentation.java

由此,可知,最后仍然是调用到Instrumentation类的execStartActivity方法,我们打开该类的源码,查看execStartActivity方法,内部有三个同名方法,实际上三个方法具体实现都是类似的,我们找到刚刚跟踪的那个方法

public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent,        int requestCode, Bundle options, UserHandle user) {        //……        try {            //……            //通过直观感受分析,排除一些遍历检查之类的代码,继续浏览,可以发现又一个冠以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);        }       //………… }
ActivityManagerNative.java

我们看到了ActivityManagerNative.getDefault().startActivity方法,从方法名看就能推测出来这是个关键方法,那么继续跟踪该方法,打开源码

这是个抽象类,那么具体实现肯定不是它,最关键的一点是看到他继承于Binder,回顾一下Android的IPC机制AIDL接口,我们知道编写的AIDL接口文件生成的对应Java类中,内部类Stub就是继承于Binder的,看到其内部的startActivity方法,可知这里已经在进行跨进程通信了,到此本进程的调用已经结束,也就是说真正的startActivity方法,是远程服务端实现的。基于C/S模型,我们可以把以上的这些调用看做是客户端实现,接下来就要跟踪到远程服务端具体实现了。

关于binder的跨进程通信就先不讨论了,这里只需知道ActivityManagerNative.getDefault()调用对应的系统服务是AMS(即ActivityManagerService)就行。

ActivityManagerService.java

可以看到,ActivityManagerService正是继承自ActivityManagerNative,是其具体实现。我们找到startActivity方法,发现内部调用的是startActivityAsUser方法,直接看到该方法实现

略过一些检查方法,可以发现,这里的关键方法是startActivityMayWait,该方法是在ActivityStackSupervisor类中调用的,该类是一个重要的类,我们看可以在声明处看到一行注释

ActivityStackSupervisor.java

由此注释可知,这个类是用来管理所有的Activity任务栈的,这里先继续跟踪,进入startActivityMayWait方法,该方法很长,先看参数声明


这里将对应的一些值做了标记,标黄的一些参数,就是传的null值,在这个方法中,我们看到下面一行代码和注释:

        // Don't modify the client's object!        intent = new Intent(intent);

由此我们可以推断,这里面很多代码都是对Intent里面携带的一些数据的获取、转换、校验以及修改,所以这里重新拷贝了一份intent,就是为了不改变原始的Intent,依据我一些大量查看源码的经验,其实查看纷繁复杂的源码的时候,很多情况下要靠直觉猜测推断,先有了一个自己的思路,再往下看源码印证,否则就很容易被复杂的源码带歪了,找不到北了,还有重要的一点就是追踪关键方法,不要纠结细节,看不懂的细节先忽略,即使你自己一两年前写的代码,没有注释的情况下你回头看,也不一定能说出每行代码的作用,更何况Android这么庞大复杂的源码,所以看源码,千万别纠结,不要有强迫症,学会认方法名,大胆猜测,小心验证。

继续往下看,也确实是在做一些Intent数据的处理,其中有个地方可以稍微关注一下:

       // Collect information about the target of the Intent.        ActivityInfo aInfo =                resolveActivity(intent, resolvedType, startFlags, profilerInfo, userId);

可以进入resolveActivity方法中去看看,这里和我们后面要讲的一个重要地方其实是调用的同一个方法,而ActivityInfo类其实就包含了所有的关于Activity的信息,这里获取的aInfo实例其实只是为了做一个检查,至于是检查什么,注释也告诉我们了,可以跳过了

好了,接下来就看一看startActivityMayWait这个方法中做的最主要的一件事吧

从方法名看,就知道应该就是解析意图的,实际上此处正是用来处理意图匹配的,可以进入具体实现查看,这里的AppGlobals.getPackageManager()调用resolveIntent的真实实现其实是在远程服务PackageManagerService中,需要研究意图是如何匹配的时候,就可以找到地方去查看了,源码如下

往下查看,还可以看到一些熟悉的地方,比如我在第一篇博客命令工具中提到做activity启动时间测试的命令adb shell am start -W -S,当我们指定activity启动后,就会输出以下一些信息,如果是从应用启动的,这里outResult是传的null,不会走


好了,还是回到正题吧,接力继续下去,这个方法中,真正的关代码是startActivityLocked

int res = startActivityLocked(caller, intent, resolvedType, aInfo,                    voiceSession, voiceInteractor, resultTo, resultWho,                    requestCode, callingPid, callingUid, callingPackage,                    realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity,                    componentSpecified, null, container, inTask);

同样,还是从名称就能分辨,我们就认准了startActivity关键字

startActivityLocked方法又是比较蛋疼了,继续做各种检查,并且处理一些错误信息,方法体也不短,这些代码就先略过吧,其实这个方法对于activity启动流程来讲,最主要做的一件事就是为目标activity创建了一个ActivityRecord。什么是ActivityRecord呢?我们之前博客提到用adb shell dumpsys activity a命令查看任务栈的一些具体信息的时候,就会输出一些TaskRecord和ActivityRecord信息,这里ActivityRecord就包含了即将要被创建的目标Activityd的各种信息。

可以看到这里将各种信息都通过构造方法传入ActivityRecord中保存起来

继续找我们的关键方法,看到startActivityUncheckedLocked方法名我们可以舒一口气了,终于不检查了……

启动模式处理

就先不查看具体方法实现了,这个方法体非常长,但是极具价值,这个方法就是用来处理Activity的启动模式的,这里要着重标记一下,当我们要研究Activity四种启动模式的时候,就要到这里来查找研究源码,这里先继续主题,暂时略过

继续查找跟踪关键方法

//…………ActivityStack targetStack;//…………targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);

我们看到,这里是调用的ActivityStackstartActivityLocked方法,那么ActivityStack又是什么呢?看声明

/** * State and management of a single stack of activities. */final class ActivityStack

这里的ActivityStack也就是我们常常说的,管理Activity的栈。

ActivityStackstartActivityLocked方法不是特别长,它主要是用来配置任务栈的,将目标ActivityRecord放到适当的地方,这里我们仍然略过,不去研究具体逻辑,到研究Activity启动模式的时候再回来查看。

继续找关键方法

这次就不是start开头了,叫resumeTopActivitiesLocked。这里的doResume默认传的是true,它是在startActivityUncheckedLocked方法参数中设置的,可以回溯查看

ActivityStack.java

这个方法从名字看,是用来恢复Activity的,这里检查了目标栈是不是在前台的,如果是就执行两个参数的resumeTopActivityLocked方法

继续跟踪,最终都会调用到resumeTopActivityInnerLocked方法中,这个方法中的代码也是相当多了,先浏览一下,可以发现一行注释

        // Remember how we'll process this pause/resume situation, and ensure        // that the state is reset however we wind up proceeding.

由此我们可以猜测到这个方法中该处理上个Activity的暂停状态了。我们知道生命周期方法中,当前Activity先进入onPause状态,然后被启动的Activity才开始走生命周期方法,当这个Activity展现到前台之后,前一个Activity的生命周期才继续走onStop

继续查看代码,果然是找到了处理Pause的地方

来到startPausingLocked方法查看,有如下代码

可以看到,如果调用者的进程存在,就会走里面的schedulePauseActivity方法,我们之前是假设从桌面点击启动Activity的,那么桌面进程肯定是存在的,就会走这里。那么该方法的具体实现是在哪里呢?这里基本可以看出来,是从调用者进程的主线程执行该方法的,那么我们可以去一个app的主线程类中去查找一下,实际上这里是一个IPC远程调用

我们都知道,Java程序是从一个静态的main方法开始执行的,它是程序的入口,那么在Android开发中,这个main方法在哪儿呢?这个其实很容易查找到,无论是在eclipse中还是as中,我们通过打断点调试的方法,可以很容易的查看方法调用栈,从中就可以找到程序的入口,我们这里就直接找到该类ActivityThread

ActivityThread.java


找到Handler中的具体实现

一路跟踪
handlePauseActivity –> performPauseActivity

这里有几个地方值得深入看一下,performUserLeavingActivity方法是用来处理回调onUserInteraction()onUserLeaveHint()方法的,这两个方法不知道大家有没有用过,使用也很简单,就和其他生命周期方法一样使用。在处理某些场景的时候是比常规的生命周期方法有用的,它表示的是用户的主动操作行为,比如按返回键,或者home键触发当前Activity的生命周期,而其他的生命周期方法比如onPause和onStop是不区分主动还是被动的,举个栗子,比如说正处在你的Activity界面,突然来了一个电话,incall界面就会出现到前台,你的Activity肯定就会触发生命周期的方法,但是这种就是典型的被动触发,不是用户主动去操作的,那么他们就不会回调。我们这里可以看出来,这两个方法的执行还在onPause之前。

好了,继续回到正题,看到performPauseActivity

这里实际上就是在执行生命周期方法了,分别处理了onSaveInstanceStateonPause 我们也可以穷根究底的进去看一下

在Activity中的具体实现

到这里完成了该Activity的暂停,我们返回到ActivityThread类继续查看handlePauseActivity方法

这里可以看到,Activity的暂停阶段的过程还并未完全结束,又通过远程调用,将Activity暂停状态返回给AMS,那么我们继续进入ActivityManagerService中,找到activityPaused

看到是调用的ActivityStackactivityPausedLocked方法,进入该方法

………… final ActivityRecord r = isInStackLocked(token);        if (r != null) {            mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);            if (mPausingActivity == r) {                …………                completePauseLocked(true);            }        …………

关键代码是completePauseLocked(true),调用完这个方法,才是真正结束了整个暂停阶段的一系列工作。进入该方法,查看到关键代码块

       if (resumeNext) {            final ActivityStack topStack = mStackSupervisor.getFocusedStack();            if (!mService.isSleepingOrShuttingDown()) {                mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);            } else {                mStackSupervisor.checkReadyForSleepLocked();                ActivityRecord top = topStack.topRunningActivityLocked(null);                if (top == null || (prev != null && top != prev)) {                    // If there are no more activities available to run,                    // do resume anyway to start something.  Also if the top                    // activity on the stack is not the just paused activity,                    // we need to go ahead and resume it to ensure we complete                    // an in-flight app switch.                    mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);                }            }        }

可以看到,最终再次调用了mStackSupervisor.resumeTopActivitiesLocked方法处理任务栈的问题,这次我们对它内部的调用就已经轻车路熟了,依次调用如下
resumeTopActivitiesLocked -> resumeTopActivityLocked -> resumeTopActivityInnerLocked

再次进入ActivityStack.java

我们再次来到resumeTopActivityInnerLocked方法,这次就有些不同了

…………if (mResumedActivity != null) {            if (DEBUG_STATES) Slog.d(TAG_STATES,                    "resumeTopActivityLocked: Pausing " + mResumedActivity);            pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);        }…………

mResumedActivity已经没有了,此时应该为空了,因为刚刚暂停了上一个Activity,新的Activity却还未创建,那么继续往下浏览

…………if (next.app != null && next.app.thread != null) {    …………    //此处新的应用还未启动,进程肯定是不存在的,就会走else逻辑else{    // Whoops, need to restart this activity!    …………    mStackSupervisor.startSpecificActivityLocked(next, true, true);}

我们看到注释,即可确定,这里的startSpecificActivityLocked就是关键方法,进入到该方法,发现又一次判断了进程是否存在,我们进程仍然是不存在的,继续找到关键方法

…………mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,                "activity", r.intent.getComponent(), false, false, true);

startProcessLocked从方法名已经可以判断,这里就是去启动目标Activity的进程了,mService就直接是ActivityManagerService,我们进入源码继续,发现调用的是参数最多的那个startProcessLocked方法,其实里面仍然不是真正创建进程的地方

这里只是创建了一个ProcessRecord,它是记录进程的全部信息的,可以看到声明

/** * Full information about a particular process that * is currently running. */final class ProcessRecord

下面继续调用重载的startProcessLocked方法

那么进程到底是如何被启动的呢?进入该方法中查看

这里是通过zygote去启动一个新进程,实际上就是利用Linux的fork机制创建新进程,并调用该进程的Java入口main函数。我们知道在Linux下,是可以调用fork函数创建新进程的。

到这里可以舒一口气了,目标app进程终于被创建出来了,接下来在次进入ActivityThread类中,这次我们从Java的main函数开始了,终于找到了一点Java程序的感觉,有木有……?

继续看关键代码

public static void main(String[] args) {        ……………………        Looper.prepareMainLooper();        ActivityThread thread = new ActivityThread();        thread.attach(false);        ……………………

这里new了一个ActivityThread,创建了我们的主线程,也就是通常说的UI线程,这里还有一个重要的东西,Looper.prepareMainLooper(),这次就不讨论了,下次分析Handler机制的时候再说,这里的关键代码其实是thread.attach(false)

在Android中,创建一个Activity并不是自己去new出来,Activity的创建都交给系统去统一管理调度的,也是说我们接下来的创建阶段,肯定不是自己玩自己的就行了,而是需要交给AMS去统一管理,因此,摆在眼前的事实就是我们要到attach方法中找到远程调用的相关逻辑,有了这个猜测就好看代码了

 private void attach(boolean system) {           ……………………            final IActivityManager mgr = ActivityManagerNative.getDefault();            try {                mgr.attachApplication(mAppThread);            } catch (RemoteException ex) {                // Ignore            }………………}

果然,这里正好有一个远程调用,将一个ApplicationThread引用交给了远程服务端,我们进入AMS中看看具体实现

    public final void attachApplication(IApplicationThread thread) {        synchronized (this) {            int callingPid = Binder.getCallingPid();            final long origId = Binder.clearCallingIdentity();            attachApplicationLocked(thread, callingPid);            Binder.restoreCallingIdentity(origId);        }    }
应用Application的创建

继续进入到attachApplicationLocked方法中查看,该方法体代码较多,但是结合注释和一些猜测,并不难理解,里面其实是做了一些预处理,继续浏览看到如下代码

 thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,                    profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,                    app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,                    isRestrictedBackupMode || !normalMode, app.persistent,                    new Configuration(mConfiguration), app.compat,                    getCommonServicesLocked(app.isolated),                    mCoreSettingsObserver.getCoreSettingsLocked());

这里有一个叫bindApplication的方法,看名字,应该进去看一下,这里的thread就是我们之前的attach方法参数中传入的ApplicationThread对象,那么我们找到具体实现看一下

…………sendMessage(H.BIND_APPLICATION, data);

找到handler中对于的具体实现

         case BIND_APPLICATION:                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");                    AppBindData data = (AppBindData)msg.obj;                    handleBindApplication(data);                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);                    break;
进入ActivityThread.java

继续找到真正的实现handleBindApplication方法,这个方法代码很多,快速浏览一下

private void handleBindApplication(AppBindData data) {      …………      final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);      …………      if (data.instrumentationName !=null) {          …………          ApplicationInfo instrApp = new ApplicationInfo();          …………          try {                java.lang.ClassLoader cl = instrContext.getClassLoader();                mInstrumentation = (Instrumentation)cl.loadClass(data.instrumentationName.getClassName()).newInstance();           }          …………      } else {         mInstrumentation =new Instrumentation();      }      …………       // If the app is being launched for full backup or restore, bring it up in       // a restricted environment with the base application class.       Application app = data.info.makeApplication(data.restrictedBackupMode, null);       mInitialApplication = app;       …………       try {            mInstrumentation.onCreate(data.instrumentationArgs);       }       …………       try {            mInstrumentation.callApplicationOnCreate(app);       }…………}

发现在这个方法中相关的处理还不少,程序的Context、Application以及Instrumentation都是在这里创建的,连Application的onCreate方法都是在这里回调的,这个地方要记住。现在我们回到刚刚的attachApplicationLocked方法,继续往下看

…………      // See if the top visible activity is waiting to run in this process...        if (normalMode) {            try {                if (mStackSupervisor.attachApplicationLocked(app)) {                    didSomething = true;                }            } catch (Exception e) {                Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);                badApp = true;            }        }…………

从注释很容易发现mStackSupervisor.attachApplicationLocked是关键代码,我们进入到ActivityStackSupervisorattachApplicationLocked方法

这里遍历是要找到位于顶端的栈,并取出目标ActivityRecord对象,可以看到这里的app为空,因为我们很早之前就创建了ActivityRecord,但当时进程并没被创建出来,后续也还未关联到进程,所以一直是空,看到这里的关键方法realStartActivityLocked,看到这个名字,感觉长征快要结束了,代码都要看吐了,有木有……?

进到realStartActivityLocked里面,代码又是又臭又长,不过先别急,还是继续找关键代码,快速浏览

…………app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,                    System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),                    new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage,                    task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,                    newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);…………

这里的关键代码是scheduleLaunchActivity,调用再次回到了应用的进程,我们进入ActivityThread中查看具体实现

…………ActivityClientRecord r = new ActivityClientRecord();…………sendMessage(H.LAUNCH_ACTIVITY, r);

这里创建了一个客户端的ActivityRecord,真正的实现仍然是在Handler里,我们继续跟踪

…………case LAUNCH_ACTIVITY: {     …………     handleLaunchActivity(r, null);}

handleLaunchActivity -> performLaunchActivity

performLaunchActivity方法就是真正的实现了,在这个方法里,终于看到了我们想要看到的,整个流程到此基本结束了,西天取经不容易啊!

…………        Activity activity = null;        try {            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();            activity = mInstrumentation.newActivity(                    cl, component.getClassName(), r.intent);        …………        }

原来我们的Activity是反射创建的,往下就是生命周期的方法了,没什么好说了

至于Activity的显示,则要回到handleLaunchActivity方法往下看,找到handleResumeActivity方法,这里就不细看了,只有一个地方多说一下,那就是上图中调用的activityattach方法,正是在这个方法中将全局ContextPhoneWindowFragmentManager等等关联到自己的。

启动流程归纳总结

相关概念

ActivityRecord:记录着Activity信息,相当于一个Activity
TaskRecord:记录着task信息
ActivityStack:管理Activity的栈

盗一张图描述它们之间的关系

启动流程时序图

  • 在客户端应用中发起
Created with Raphaël 2.1.2 Activity Activity Intrumentation Intrumentation ActivityManagerProxy ActivityManagerProxy ActivityManagerService ActivityManagerService startActivity startActivityForResult execStartActivity startActivity
  • 在服务端控制逻辑
    1.准备阶段
Created with Raphaël 2.1.2 ActivityManagerService ActivityManagerService ActivityStackSupervisor ActivityStackSupervisor ActivityStack ActivityStack startActivity startActivityAsUser startActivityMayWait startActivityLocked startActivityUncheckedLocked resumeTopActivitiesLocked resumeTopActivityLocked resumeTopActivityInnerLocked

2.暂停上一个Activity

Created with Raphaël 2.1.2 ActivityStack ActivityStack ActivityThread ActivityThread Instrumentation Instrumentation Activity Activity ActivityManagerService ActivityManagerService startPausingLocked schedulePauseActivity handlePauseActivity performPauseActivity callActivityOnPause performPause activityPaused activityPausedLocked completePauseLocked

3.启动目标进程

Created with Raphaël 2.1.2 ActivityStack ActivityStack ActivityStackSupervisor ActivityStackSupervisor ActivityManagerService ActivityManagerService ActivityThread ActivityThread resumeTopActivityInnerLocked startSpecificActivityLocked startProcessLocked main

4.创建目标Activity

Created with Raphaël 2.1.2 ActivityThread ActivityThread ActivityManagerService ActivityManagerService ActivityStackSupervisor ActivityStackSupervisor main attach attachApplication attachApplicationLocked realStartActivityLocked scheduleLaunchActivity handleLaunchActivity performLaunchActivity

5.显示

Created with Raphaël 2.1.2 ActivityThread ActivityThread Activity Activity Instrumentation Instrumentation performLaunchActivity attach setTheme callActivityOnCreate performCreate onCreate

更多相关文章

  1. Android手势源码浅析-----手势绘制(GestureOverlayView)
  2. Android进程守护详解及解决方案
  3. Android性能优化篇:Android中如何避免创建不必要的对象
  4. flappy bird游戏源代码揭秘和下载后续---移植到android真机上
  5. android高仿微信视频编辑页-视频多张图片提取
  6. Android(安卓)layout 优化:使用include和merge 标签
  7. 我的工具太少了之Android无限轮播图片,最后一张过度动画很重要
  8. 一起来开发Android的天气软件(三)——使用Volley实现网络通信
  9. Android滑动冲突之完美实现RecycleView+下拉刷新+上拉加载+粘性H

随机推荐

  1. MySQL故障切换笔记之应用无感知设计详解
  2. Innodb中mysql快速删除2T的大表方法示例
  3. Mysql优化order by语句的方法详解
  4. 关于case when语句的报错问题详解
  5. Mysql中禁用与启动触发器教程【推荐】
  6. 解决MySQL 5.7中定位DDL被阻塞的问题
  7. MySQL表结构变更你不可不知的Metadata Lo
  8. 如何修改Mysql中group_concat的长度限制
  9. MySQL删除表时I/O错误的原因分析与解决
  10. MySQL中通过EXPLAIN如何分析SQL的执行计