分析Android中应用的启动流程
前言
在我们开始之前,希望您能最好已经满足以下条件:
1、有一份编译后的android源码(亲自动手实践才会有更深入的理解)
2、对binder机制有一定的了解
本文启动流程分析基于android 5.1的源码。为什么是5.1的源码呢?因为手边编译完的代码只有这个版本…另外,用什么版本的源码并不重要,大体的流程并无本质上的区别,仅仅是实现细节的调整,找一个你熟悉的版本就好。
1、启动时序图
作为一个轻微强迫症的人,整理的时序图,相信大家按图索骥,一定能搞明白整个启动流程:
说明:为了让大家更清楚的理解整个过程,将时序图中划分为三个部分:launcher进程、system进程、app进程,其中有涉及共用的类以l / a进行区分表示跟哪个进程有关,便于理解。
2、关键类说明
整个启动流程因为会涉及到多次binder通信,这里先简要说明一下几个类的用途,方便大家理解整个交互流程:
1、activitymanagerservice:ams是android中最核心的服务之一,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,其职责与操作系统中的进程管理和调度模块相类似,因此它在android中非常重要,它本身也是一个binder的实现类。
2、instrumentation:顾名思义,它用来监控应用程序和系统的交互。
3、activitythread:应用的入口类,系统通过调用main函数,开启消息循环队列。activitythread所在线程被称为应用的主线程(ui线程)。
4、applicationthread:applicationthread提供binder通讯接口,ams则通过代理调用此app进程的本地方法。
5、activitymanagerproxy:ams服务在当前进程的代理类,负责与ams通信。
6、applicationthreadproxy:applicationthread在ams服务中的代理类,负责与applicationthread通信。
3、流程分析
首先交代下整个流程分析的场景:用户点击launcher上的应用图标到该应用主界面启动展示在用户眼前。
这整个过程涉及到跨进程通信,所以我们将其划分为时序图中所展示三个进程:launcher进程、system进程、app进程。为了不贴过长的代码又能说清楚进程间交互的流程,这里简述几个重要的交互点。
从时序图上大家也可以看到调用链相当长,对应的代码量也比较大,而且时序图只是分析了这个一个场景下的流程。道阻且长,行则将至!
3.1 launcher响应用户点击,通知ams
launcher做为应用的入口,还是有必要交代一下的,我们来看看launcher的代码片段,launcher使用的是packages/apps/launcher3的的源码。
public class launcher extends activity implements view.onclicklistener, onlongclicklistener, launchermodel.callbacks, view.ontouchlistener, pageswitchlistener, launcherproviderchangelistener { ... /** * launches the intent referred by the clicked shortcut. * * @param v the view representing the clicked shortcut. */ public void onclick(view v) { // make sure that rogue clicks don't get through while allapps is launching, or after the // view has detached (it's possible for this to happen if the view is removed mid touch). if (v.getwindowtoken() == null) { return; } ... object tag = v.gettag(); if (tag instanceof shortcutinfo) { onclickappshortcut(v); } else if (tag instanceof folderinfo) { ... } else if (v == mallappsbutton) { onclickallappsbutton(v); } else if (tag instanceof appinfo) { startappshortcutorinfoactivity(v); } else if (tag instanceof launcherappwidgetinfo) { ... } } private void startappshortcutorinfoactivity(view v) { ... boolean success = startactivitysafely(v, intent, tag); ... } boolean startactivitysafely(view v, intent intent, object tag) { ... try { success = startactivity(v, intent, tag); } catch (activitynotfoundexception e) { ... } return success; } boolean startactivity(view v, intent intent, object tag) { intent.addflags(intent.flag_activity_new_task); try { ... if (user == null || user.equals(userhandlecompat.myuserhandle())) { // could be launching some bookkeeping activity startactivity(intent, optsbundle); } else { ... } return true; } catch (securityexception e) { ... } return false; } }
通过staracticity辗转调用到activity:startactivityforresult
而后则调用至instrumentation:execstartactivity
,代码片段如下:
public class instrumentation { ... public activityresult execstartactivity( context who, ibinder contextthread, ibinder token, activity target, intent intent, int requestcode, bundle options) { iapplicationthread whothread = (iapplicationthread) contextthread; ... try { ... int result = activitymanagernative.getdefault() .startactivity(whothread, who.getbasepackagename(), intent, intent.resolvetypeifneeded(who.getcontentresolver()), token, target != null ? target.membeddedid : null, requestcode, 0, null, options); ... } catch (remoteexception e) { } return null; } ... }
这里的activitymanagernative.getdefault
返回activitymanagerservice
的远程接口,即activitymanagerproxy
接口,有人可能会问了为什么会是activitymanagerproxy
,这就涉及到binder通信了,这里不再展开。通过binder驱动程序,activitymanagerproxy
与ams服务通信,则实现了跨进程到system进程。
3.2 ams响应launcher进程请求
从上面的流程我们知道,此时ams应该处理launcher进程发来的请求,请参看时序图及源码,此时我们来看activitystacksupervisor:startactivityuncheckedlocked
方法,目测这个方法已经超过600行代码,来看一些关键代码片段:
public final class activitystacksupervisor implements displaylistener { ... final int startactivityuncheckedlocked(activityrecord r, activityrecord sourcerecord, ivoiceinteractionsession voicesession, ivoiceinteractor voiceinteractor, int startflags, boolean doresume, bundle options, taskrecord intask) { final intent intent = r.intent; final int callinguid = r.launchedfromuid; ... final boolean launchsingletop = r.launchmode == activityinfo.launch_single_top; final boolean launchsingleinstance = r.launchmode == activityinfo.launch_single_instance; final boolean launchsingletask = r.launchmode == activityinfo.launch_single_task; int launchflags = intent.getflags(); ... // we'll invoke onuserleaving before onpause only if the launching // activity did not explicitly state that this is an automated launch. muserleaving = (launchflags & intent.flag_activity_no_user_action) == 0; ... activityrecord nottop = (launchflags & intent.flag_activity_previous_is_top) != 0 ? r : null; // if the onlyifneeded flag is set, then we can do this if the activity // being launched is the same as the one making the call... or, as // a special case, if we do not know the caller then we count the // current top activity as the caller. if ((startflags&activitymanager.start_flag_only_if_needed) != 0) { ... } ... // if the caller is not coming from another activity, but has given us an // explicit task into which they would like us to launch the new activity, // then let's see about doing that. if (sourcerecord == null && intask != null && intask.stack != null) { final intent baseintent = intask.getbaseintent(); final activityrecord root = intask.getrootactivity(); ... // if this task is empty, then we are adding the first activity -- it // determines the root, and must be launching as a new_task. if (launchsingleinstance || launchsingletask) { ... } ... } ... if (intask == null) { if (sourcerecord == null) { // this activity is not being started from another... in this // case we -always- start a new task. if ((launchflags & intent.flag_activity_new_task) == 0 && intask == null) { slog.w(tag, "startactivity called from non-activity context; forcing " + "intent.flag_activity_new_task for: " + intent); launchflags |= intent.flag_activity_new_task; } } else if (sourcerecord.launchmode == activityinfo.launch_single_instance) { // the original activity who is starting us is running as a single // instance... this new activity it is starting must go on its // own task. launchflags |= intent.flag_activity_new_task; } else if (launchsingleinstance || launchsingletask) { // the activity being started is a single instance... it always // gets launched into its own task. launchflags |= intent.flag_activity_new_task; } } ... // we may want to try to place the new activity in to an existing task. we always // do this if the target activity is singletask or singleinstance; we will also do // this if new_task has been requested, and there is not an additional qualifier telling // us to still place it in a new task: multi task, always doc mode, or being asked to // launch this as a new task behind the current one. if (((launchflags & intent.flag_activity_new_task) != 0 && (launchflags & intent.flag_activity_multiple_task) == 0) || launchsingleinstance || launchsingletask) { // if bring to front is requested, and no result is requested and we have not // been given an explicit task to launch in to, and // we can find a task that was started with this same // component, then instead of launching bring that one to the front. if (intask == null && r.resultto == null) { // see if there is a task to bring to the front. if this is // a single_instance activity, there can be one and only one // instance of it in the history, and it is always in its own // unique task, so we do a special search. activityrecord intentactivity = !launchsingleinstance ? findtasklocked(r) : findactivitylocked(intent, r.info); if (intentactivity != null) { ... } } } ... if (r.packagename != null) { // if the activity being launched is the same as the one currently // at the top, then we need to check if it should only be launched // once. activitystack topstack = getfocusedstack(); activityrecord top = topstack.toprunningnondelayedactivitylocked(nottop); if (top != null && r.resultto == null) { if (top.realactivity.equals(r.realactivity) && top.userid == r.userid) { ... } } } else{ ... } boolean newtask = false; boolean keepcurtransition = false; taskrecord tasktoaffiliate = launchtaskbehind && sourcerecord != null ? sourcerecord.task : null; // should this be considered a new task? if (r.resultto == null && intask == null && !addingtotask && (launchflags & intent.flag_activity_new_task) != 0) { ... if (reusetask == null) { r.settask(targetstack.createtaskrecord(getnexttaskid(), newtaskinfo != null ? newtaskinfo : r.info, newtaskintent != null ? newtaskintent : intent, voicesession, voiceinteractor, !launchtaskbehind /* totop */), tasktoaffiliate); ... } else { r.settask(reusetask, tasktoaffiliate); } ... } else if (sourcerecord != null) { } else if (!addingtotask && (launchflags&intent.flag_activity_reorder_to_front) != 0) { } else if (intask != null){ } else { } ... targetstack.startactivitylocked(r, newtask, doresume, keepcurtransition, options); ... return activitymanager.start_success; } ... }
函数经过intent的标志值设置,通过findtasklocked
函数来查找存不存这样的task,这里返回的结果是null,即intentactivity
为null,因此,需要创建一个新的task来启动这个activity
。现在处理堆栈顶端的activity
是launcher
,与我们即将要启动的mainactivity
不是同一个activity
,创建了一个新的task里面来启动这个activity
。
经过栈顶检测,则需要将launcher推入paused状态,才可以启动新的activity
。后续则调用至activitystack:startpausinglocked
,我们来看一下这个函数:
final class activitystack { ... final boolean startpausinglocked(boolean userleaving, boolean uisleeping, boolean resuming, boolean dontwait) { if (mpausingactivity != null) { ... } activityrecord prev = mresumedactivity; if (prev == null) { ... } ... mresumedactivity = null; mpausingactivity = prev; mlastpausedactivity = prev; mlastnohistoryactivity = (prev.intent.getflags() & intent.flag_activity_no_history) != 0 || (prev.info.flags & activityinfo.flag_no_history) != 0 ? prev : null; prev.state = activitystate.pausing; ... if (prev.app != null && prev.app.thread != null) { try { ... prev.app.thread.schedulepauseactivity(prev.apptoken, prev.finishing, userleaving, prev.configchangeflags, dontwait); } catch (exception e) { ... } } else { ... } ... } ... }
这里的prev.app.thread
是一个applicationthread
对象的远程接口,通过调用这个远程接口的schedulepauseactivity
来通知launcher进入paused状态。至此,ams对launcher的请求已经响应,这是我们发现又通过binder通信回调至launcher进程。
3.3 launcher进程挂起launcher,再次通知ams
这个流程相对会简单一些,我们来看activitythread
:
public final class activitythread { ... private void handlepauseactivity(ibinder token, boolean finished, boolean userleaving, int configchanges, boolean dontreport) { activityclientrecord r = mactivities.get(token); if (r != null) { ... performpauseactivity(token, finished, r.isprehoneycomb()); // make sure any pending writes are now committed. if (r.isprehoneycomb()) { queuedwork.waittofinish(); } // tell the activity manager we have paused. if (!dontreport) { try { activitymanagernative.getdefault().activitypaused(token); } catch (remoteexception ex) { } } ... } } ... }
这部分launcher的activitythread
处理页面paused并且再次通过activitymanagerproxy
通知ams。
3.4 ams创建新的进程
创建新进程的时候,ams会保存一个processrecord
信息,如果应用程序中的androidmanifest.xml配置文件中,我们没有指定application标签的process属性,系统就会默认使用package的名称。每一个应用程序都有自己的uid,因此,这里uid + process的组合就可以为每一个应用程序创建一个processrecord
。
public final class activitymanagerservice extends activitymanagernative implements watchdog.monitor, batterystatsimpl.batterycallback { ... private final void startprocesslocked(processrecord app, string hostingtype, string hostingnamestr, string abioverride, string entrypoint, string[] entrypointargs) { ... try { ... // start the process. it will either succeed and return a result containing // the pid of the new process, or else throw a runtimeexception. boolean isactivityprocess = (entrypoint == null); if (entrypoint == null) entrypoint = "android.app.activitythread"; process.processstartresult startresult = process.start(entrypoint, app.processname, uid, uid, gids, debugflags, mountexternal, app.info.targetsdkversion, app.info.seinfo, requiredabi, instructionset, app.info.datadir, entrypointargs); ... } catch () { ... } } ... }
这里主要是调用process:start
接口来创建一个新的进程,新的进程会导入android.app.activitythread
类,并且执行它的main
函数,这就是每一个应用程序都有一个activitythread
实例来对应的原因。
3.5 应用进程初始化
我们来看activity
的main
函数,这里绑定了主线程的looper,并进入消息循环,大家应该知道,整个android系统是消息驱动的,这也是为什么主线程默认绑定looper的原因:
public final class activitythread { ... public static void main(string[] args) { ... looper.preparemainlooper(); activitythread thread = new activitythread(); thread.attach(false); ... looper.loop(); ... } private void attach(boolean system) { ... if (!system) { ... final iactivitymanager mgr = activitymanagernative.getdefault(); try { mgr.attachapplication(mappthread); } catch (remoteexception ex) { // ignore } } else { ... } ... } ... }
attach函数最终调用了activitymanagerservice
的远程接口activitymanagerproxy的attachapplication
函数,传入的参数是mappthread
,这是一个applicationthread
类型的binder
对象,它的作用是ams与应用进程进行进程间通信的。
3.6 在ams中注册应用进程,启动启动栈顶页面
前面我们提到了ams负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,通过上一个流程我们知道应用进程创建后通过binder驱动与ams产生交互,此时ams则将应用进程创建后的信息进行了一次注册,如果拿windows系统程序注册到的注册表来理解这个过程,可能会更形象一些。
mmainstack.toprunningactivitylocked(null)
从堆栈顶端取出要启动的activity
,并在realstartactivitylockedhan
函数中通过applicationthreadproxy
调回app进程启动页面。
public final class activitystacksupervisor implements displaylistener { ... final boolean realstartactivitylocked(activityrecord r, processrecord app, boolean andresume, boolean checkconfig) throws remoteexception { ... r.app = app; ... try { ... app.thread.schedulelaunchactivity(new intent(r.intent), r.apptoken, system.identityhashcode(r), r.info, new configuration(mservice.mconfiguration), r.compat, r.launchedfrompackage, r.task.voiceinteractor, app.repprocstate, r.icicle, r.persistentstate, results, newintents, !andresume, mservice.isnexttransitionforward(), profilerinfo); ... } catch (remoteexception e) { ... } ... } ... }
此时在app进程,我们可以看到,经过一些列的调用链最终调用至mainactivity:oncreate
函数,之后会调用至onresume
,而后会通知ams该mainactivity
已经处于resume
状态。至此,整个启动流程告一段落。
4、总结
通过上述流程,相信大家可以有了一个基本的认知,这里我们忽略细节简化流程,单纯从进程角度来看下图: launch_app_sim
图上所画这里就不在赘述,activity启动后至resume状态,此时可交互。以上就是分析android中应用启动流程的全部内容了,如何有疑问欢迎大家指正交流。
上一篇: 交换两个数——C语言代码
推荐阅读
-
分析Android中应用的启动流程
-
Android 重力传感器在游戏开发中的应用
-
Android系统进程间通信Binder机制在应用程序框架层的Java接口源代码分析
-
Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析
-
Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析
-
Android中BaseAdapter的用法分析与理解
-
深入分析安卓(Android)中的注解
-
Android开发中ImageView的scaletype属性用法分析
-
Ubuntu中为Android系统实现内置Java应用程序测试Application Frameworks层的硬件服务
-
Android 应用中插入广告的实例