我们通常说Android有四大组件,也有说法是五大组件,将Intent也算一种,不管是五大还是四大,它们都是Android app开发中最经常打交道的东西,刚开始的时候也都会碰到一些坑,很多时候我们都是搜索资料,看其他人写的博客来学习和避坑,其实很多博客资料也就是对官方文档的翻译,加上一些自己避坑心得的记录,有些时候即使通过一些资料解决了某个问题,但其实自己也说不出个所以然来,也是云里雾里,只是记住结论就行了,有些时候身边一些同事会说,做应用层的,不用太深入底层,先把功能需求做好就行了。这话是有一定道理,但我想,他们说的底层到底是哪一层呢?这可能就是很多程序员自嘲为码农的原因吧,不管是做什么开发,如果只是停留在API的熟练使用,即使再干十年,也只是在搬砖吧,所以我个人认为,从初级工程师进阶,最关键的一步就是要了解源码,要学习源码,而不是掌握多少流行库的调用。Read the fucking source code!另外讨论一个问题,什么是底层呢?我认为涉及到Java的都不能算底层,只有到C/C++代码那一层才能算底层,因此Framework不能称为底层。
这一篇博客就从Activity的启动流程开始写吧,这里以Android 6.0源码为例,其他版本或许会有不同。这篇博客力求简单通俗,给应用的开发带来一些启示,因此不会深入底层,不会涉及C/C++层,更不会涉及Linux内核的一些原理,因为博主也不甚了解,掩面而逃……
通常启动Activity是有三种方式的
事实上第一种和第二种是差不多的,Android系统的桌面就是一个应用。那我们就从startActivity方法开始跟踪
这里mParent应该为空,如不确定,继续查看mParent不为空的情况,则调用如下方法
mParent.startActivityFromChild(this, intent, requestCode);
由此,可知,最后仍然是调用到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.getDefault().startActivity
方法,从方法名看就能推测出来这是个关键方法,那么继续跟踪该方法,打开源码
这是个抽象类,那么具体实现肯定不是它,最关键的一点是看到他继承于Binder,回顾一下Android的IPC机制AIDL接口,我们知道编写的AIDL接口文件生成的对应Java类中,内部类Stub就是继承于Binder的,看到其内部的startActivity方法,可知这里已经在进行跨进程通信了,到此本进程的调用已经结束,也就是说真正的startActivity方法,是远程服务端实现的。基于C/S模型,我们可以把以上的这些调用看做是客户端实现,接下来就要跟踪到远程服务端具体实现了。
关于binder的跨进程通信就先不讨论了,这里只需知道ActivityManagerNative.getDefault()调用对应的系统服务是AMS(即ActivityManagerService)就行。
可以看到,ActivityManagerService正是继承自ActivityManagerNative,是其具体实现。我们找到startActivity方法,发现内部调用的是startActivityAsUser方法,直接看到该方法实现
略过一些检查方法,可以发现,这里的关键方法是startActivityMayWait
,该方法是在ActivityStackSupervisor
类中调用的,该类是一个重要的类,我们看可以在声明处看到一行注释
由此注释可知,这个类是用来管理所有的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);
我们看到,这里是调用的ActivityStack
的startActivityLocked
方法,那么ActivityStack
又是什么呢?看声明
/**
* State and management of a single stack of activities.
*/
final class ActivityStack
这里的ActivityStack
也就是我们常常说的,管理Activity的栈。
ActivityStack
的startActivityLocked
方法不是特别长,它主要是用来配置任务栈的,将目标ActivityRecord
放到适当的地方,这里我们仍然略过,不去研究具体逻辑,到研究Activity启动模式的时候再回来查看。
这次就不是start开头了,叫resumeTopActivitiesLocked
。这里的doResume默认传的是true,它是在startActivityUncheckedLocked
方法参数中设置的,可以回溯查看
这个方法从名字看,是用来恢复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
来到startPausingLocked
方法查看,有如下代码
可以看到,如果调用者的进程存在,就会走里面的schedulePauseActivity
方法,我们之前是假设从桌面点击启动Activity的,那么桌面进程肯定是存在的,就会走这里。那么该方法的具体实现是在哪里呢?这里基本可以看出来,是从调用者进程的主线程执行该方法的,那么我们可以去一个app的主线程类中去查找一下,实际上这里是一个IPC远程调用
我们都知道,Java程序是从一个静态的main方法开始执行的,它是程序的入口,那么在Android开发中,这个main方法在哪儿呢?这个其实很容易查找到,无论是在eclipse中还是as中,我们通过打断点调试的方法,可以很容易的查看方法调用栈,从中就可以找到程序的入口,我们这里就直接找到该类
ActivityThread
一路跟踪
handlePauseActivity
–> performPauseActivity
这里有几个地方值得深入看一下,
performUserLeavingActivity
方法是用来处理回调onUserInteraction()
和onUserLeaveHint()
方法的,这两个方法不知道大家有没有用过,使用也很简单,就和其他生命周期方法一样使用。在处理某些场景的时候是比常规的生命周期方法有用的,它表示的是用户的主动操作行为,比如按返回键,或者home键触发当前Activity的生命周期,而其他的生命周期方法比如onPause和onStop是不区分主动还是被动的,举个栗子,比如说正处在你的Activity界面,突然来了一个电话,incall界面就会出现到前台,你的Activity肯定就会触发生命周期的方法,但是这种就是典型的被动触发,不是用户主动去操作的,那么他们就不会回调。我们这里可以看出来,这两个方法的执行还在onPause之前。
好了,继续回到正题,看到performPauseActivity
这里实际上就是在执行生命周期方法了,分别处理了onSaveInstanceState
和onPause
我们也可以穷根究底的进去看一下
在Activity中的具体实现
到这里完成了该Activity的暂停,我们返回到ActivityThread
类继续查看handlePauseActivity
方法
这里可以看到,Activity的暂停阶段的过程还并未完全结束,又通过远程调用,将Activity暂停状态返回给AMS,那么我们继续进入ActivityManagerService
中,找到activityPaused
看到是调用的ActivityStack
的activityPausedLocked
方法,进入该方法
…………
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
我们再次来到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
这里是通过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);
}
}
继续进入到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;
继续找到真正的实现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
是关键代码,我们进入到ActivityStackSupervisor
的attachApplicationLocked
方法
这里遍历是要找到位于顶端的栈,并取出目标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
方法,这里就不细看了,只有一个地方多说一下,那就是上图中调用的activity
的attach
方法,正是在这个方法中将全局Context
、PhoneWindow
、FragmentManager
等等关联到自己的。
相关概念
ActivityRecord:记录着Activity信息,相当于一个Activity
TaskRecord:记录着task信息
ActivityStack:管理Activity的栈
启动流程时序图
2.暂停上一个Activity
3.启动目标进程
4.创建目标Activity
5.显示