如何启动activity

1. startActivity。在桌面点击应用图标或者通过intent来启动activity都是这种方式

2. startActivityForResult。

3. startActivityFromRecent。当退出应用后再从最近应用列表中启动app

 

startActivity后的一些疑问

1. startActivity的执行流程,运行的架构?

2. activity的启动模式

3. activity是运行于进程中的,所在进程何时创建?

4. 何时创建了主线程(main thread / UI thread)?

5. 按back键时发生了什么?

6. 按power键时发生了什么?

7. 按home键时发生了什么?

8. activity的生命周期

 

FW的binder模型

Android activity 启动慢 android启动activity方法_客户端

一个binder服务实际上就是一个binder类的对象,该对象创建,内部会启动一个线程。该线程会接收Binder驱动发送的消息,然后执行onTransact()方法,根据不同的code执行不同的功能代码。这个code是客户端调用transact()方法时传入的参数。

当服务端binder对象创建时,同时会在binder驱动创建一个mRemote对象,该对象也是binder类型,客户端要访问服务端,获取binder驱动中的mRemote对象,通过mRemote对象来访问。

 

AMS的binder模型

Android activity 启动慢 android启动activity方法_客户端_02

了解AMS的binder模型是十分有必要的,因为接下来的startActivity流程都基于这个模型

 

startActivity流程

Android activity 启动慢 android启动activity方法_包名_03

startActivity流程说明

① 1~5通过binder通信进入到AMS中,并调用AMS的startActivity。

② 6~11调用startActivityMayWait准备即将启动的activity的相关信息,比如匹配intent获取activity,创建activity的数据结构,确定activity所在task和activityStack。

③ 12~20,暂停当前的activity,调用WMS显示activity

④ 21~26,启动进程,并切换到新进程的main thread,创建looper

⑤ 33~37、46~48,bindApplication发送BIND_APPLICATION的消息给looper,处理消息的方法是handleBindApplication,最终调用application.onCreate()

⑥ 40~44、49~62 scheduleLaunchActivity发送LAUNCH_ACTIVITY消息给looper,处理对应消息的方法是handleLaunchActivity,最终调用activity各个生命周期的方法onCreate、onStart、onResume

 

startActivity流程的binder模型

为了让启动流程和binder模型对应起来,便于理解,把启动流程划分了一下,如图

Android activity 启动慢 android启动activity方法_客户端_04

 

startActivity流程的进程示意图

从进程的角度把启动流程划分一下,如图

Android activity 启动慢 android启动activity方法_包名_05

 

AMS中activity的相关类

1. /android/frameworks/base/core/java/android/app (基于Android 6.0)

Activity:所有用户activity的父类

ActivityManagerNative:客户端与AMS进行通信的binder调用

ActivityManagerProxy:AMS代理,供客户端调用

 

ActivityThread:应用程序主线程

 

ApplicationThreadNative:AMS与客户端进行通信的binder调用

ApplicationThreadProxy:AMS调用客户端的代理

上面这些类的关系如下图:

Android activity 启动慢 android启动activity方法_android_06

 

2. /android/frameworks/base/services/core/java/com/android/server/am (基于Android 6.0)

ActivityManagerService:四大组件管理的核心类,同时管理和调度用户进程

ActivityRecord:在AMS中用来保存一个activity的信息

TaskRecord:在AMS中用来保存一个任务的信息,一个任务就是当用户按recent键时显示的最近应用列表,如下图

Android activity 启动慢 android启动activity方法_包名_07

ActivityStack:管理taskRecord。三星FW包含HOME_STACK_TYPE、NORMAL_APP_STACK_TYPE等,其中HOME_STACK_TYPE类型的ActivityStack一般包含systemUI和launcher所在的taskRecord

ActivityStackSupervisor:管理ActivityStack

 

上述这些类的大概关系图如下

Android activity 启动慢 android启动activity方法_包名_08

 

startActivity简要流程

从startActivity的流程图看出,启动流程十分复杂,为了便于理解,把整个流程精简了一下,提炼出一些关键步骤,如下图

Android activity 启动慢 android启动activity方法_客户端_09

 

① 匹配intent获取ActivityInfo

AMS首先要解析客户端传递进来的intent,首先要知道启动哪个Activity,关键代码是

// Collect information about the target of the Intent.
        ActivityInfo aInfo =
                resolveActivity(/* { Dual-Screen */caller, /* Dual-Screen } */intent, resolvedType, startFlags, profilerInfo, userId);

 

返回来的ActivityInfo结构如下,包含一些activity的基本数据

ActivityInfo

applicationInfo

taskAffinity:默认为包名

processName: 进程名

className: application的类名

dataDir: 数据存放路径/data/user/0/com.bookstore.main

packageName: 包名

targetSdkVersion

……

launchMode: standard/singleTop/singleTask/singleInstance

name: activity的名字,在manifest中用android:name指定

packageName: 包名

processName: 进程名

taskAffinity:默认为包名

……

resolveActivity的具体实现在PackageManagerService.java中,大概就是从ArrayMap中查询出activity;如果匹配到一个activity,则返回目标activity的ActivityInfo;如果匹配到多个activity(比如使用intent-filter的startActivity),则返回ResolverActivity的ActivityInfo,进而启动ResolverActivity,这个activity就是让用户选择启动哪个应用,如下图

Android activity 启动慢 android启动activity方法_android_10

 

②创建ActivityRecord

ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
                intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
                requestCode, componentSpecified, this, container, options);

ActivityRecord结构如下

Class

Field

value

ActivityRecord

ActivityManagerService service


ActivityInfo info


Application appInfo


String launchedFromPackage

"com.sec.android.app.launcher"

Intent intent


ComponentName realActivity

包名/activity名字,com.bookstore.main/com.bookstore.main.MainActivity

String packageName

"com.bookstore.main"

String processName

"com.bookstore.main"

String taskAffinity

"com.bookstore.main"默认为包名

TaskRecord task

该activity所在的task

ActivityRecord resultTo

把结果返回给哪个activity

ProcessRecord app

进程数据

ActivityState state

activity状态(INITIALIZING/RESUME/PAUSING/PAUSED…)

int launchMode

standard/singleTop/singleTask/singleInstance

ComponentName sourceActivity

从哪里启动

……


 

③确定activity所在task


这个步骤是activity启动过程中的关键,也是难点。这里将详细分析。


此处主要是通过判断Intent的标志(如start Flag)和activity的属性(如launchMode、taskAffinity、allowTaskReparenting、clearTaskOnLaunch、alwaysRetainTaskState、finishOnTaskLaunch)来确定Activity的Task(是否需要新建Task)和ActivityStack,即确定activity属于哪个任务,并对task进行一些操作。


 


app中通过这种方法设置启动intent的flag


intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);


这些flag是常见的flag:FLAG_ACTIVITY_CLEAR_TASK、FLAG_ACTIVITY_CLEAR_TOP、FLAG_ACTIVITY_MULTIPLE_TASK、FLAG_ACTIVITY_NEW_TASK、FLAG_ACTIVITY_SINGLE_TOP


 


通过下面这种方法设置activity的launchMode




Android activity 启动慢 android启动activity方法_客户端_11


launchMode有下面这四种


android:launchMode="standard"(默认)


android:launchMode="singleTop"(常用)


android:launchMode="singleTask"(不常用)


android:launchMode="singleInstance"(不常用)


 


这个步骤主要确定activity所在的task,这有两种情况,一是把activity加入到当前的task中,二是创建新的task,把activity加入到该task。那么,什么情况需要创建新task?


必要条件:


(1)start Flag必须是FLAG_ACTIVITY_NEW_TASK。如果启动的源activity的launchMode是singleInstance,或者启动的activity的launchMode是singleTask或singleInstance,也会把flag置为new task


(2)不是从recent应用程序中启动。哪些算从recent启动的?把back键一直返回到桌面直到当前task结束,再从recent应用程序中进入,这样才算是从recent启动。从activity按home键退出再从recent中进入不算从recent启动,这种并不会重新start,只会onResume


 


注意:如果是通过startActivityForResult启动并且设置了FLAG_ACTIVITY_NEW_TASK,则在onActivityResult中返回来的resultCode一定是RESULT_CANCELED


 


上面介绍了start Flag和activity的launchMode属性,那么这两个东西是如何影响activity所在的task呢?下面就介绍flag和launchMode相结合的各种情况


 


launchMode为standard


launchMode为standard,每次启动activity时都会创建该activity的实例对象;同一个task中可以同时存在该activity的多个实例;一个activity的多个实例可以出现在多个task中。


当launchMode为standard,与flag配合使用,会出现什么情况?


(1)不设置flag。不会创建task,可创建多个实例。Task1中,Activity B(以下简称B)处于顶部,B启动B,在Task1的任务上新增了一个B实例



Android activity 启动慢 android启动activity方法_客户端_12


------------------------------------------------------------------------------------------------------------------------


(2)设置FLAG_ACTIVITY_NEW_TASK。如果activity的affinity属性与现有task的affinity相同,则不会创建新task。如果不同,则会创建新task。affinity是亲和度,是一个字符串,可以理解为task的名字。task的affinity与启动该task的第一个activity相同,以后加入这个task的activity,即使它们的affinity属性定义了一个不同的字符串,也不会改变task已有的affinity。activity的affinity属性在AndroidManifest.xml中指定。


 


↓B启动C,由于Task1的affinity和A的affinity相同,而C和A的affinity也相同,所以就算指定了FLAG_ACTIVITY_NEW_TASK,也不会创建新task,而是在Task1上添加了C的实例



Android activity 启动慢 android启动activity方法_包名_13


------------------------------------------------------------------------------------------------------------------------


 


↓由于C与A的affinity不相同(前提),所以指定FLAG_ACTIVITY_NEW_TASK这个flag,当B启动C时,会创建新Task2(Task2在上面表示Task2在前台,Task1被切换到后台)



Android activity 启动慢 android启动activity方法_包名_14


------------------------------------------------------------------------------------------------------------------------


 


↓B启动A,由于Task1的affinity本身就和A相同,所以再启动A,说明已经存在相同affinity的task,便不会创建新task,在task1的基础上添加A的新实例



Android activity 启动慢 android启动activity方法_客户端_15


------------------------------------------------------------------------------------------------------------------------


 


↓B与A的affinity相同(前提条件),Task1是A启动的(A处于底部),所以Task1的affinity和A的相同,B再启动B,B的affinity又与A的相同,即与Task1的相同,所以不会创建新task,在task1的上面添加新的B实例



Android activity 启动慢 android启动activity方法_客户端_16


------------------------------------------------------------------------------------------------------------------------


 


(3)设置FLAG_ACTIVITY_CLEAR_TOP。如果启动的activity已经存在,则把该activity带到前台(并调用onNewIntent),并把它前面的activity都弹出栈(相当于调用finish)。如果不存在,则创建新实例。


 


↓B启动A,由于已经存在A实例,所以直接调用最前面A的onNewIntent方法,并且把B给弹出栈了(finish)



Android activity 启动慢 android启动activity方法_客户端_17


------------------------------------------------------------------------------------------------------------------------


 


↓Task1的A启动B,由于没有指定FLAG_ACTIVITY_NEW_TASK的flag,所以不会寻找Task2的B实例,也就是会在Task1中创建B的新实例



Android activity 启动慢 android启动activity方法_客户端_18


------------------------------------------------------------------------------------------------------------------------


 


↓B启动A,由于没有指定FLAG_ACTIVITY_NEW_TASK的flag,所以不会寻找Task2的A实例,并且在Task1中实例A已经存在,就会调用A的onNewIntent,并把A上面的B弹出栈



Android activity 启动慢 android启动activity方法_客户端_19


------------------------------------------------------------------------------------------------------------------------


 


(4)设置FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP


 


↓A与B的affinity不相同,A启动B。由于设定了FLAG_ACTIVITY_NEW_TASK,并且B的affinity和Task1的(Task1的affinity就是A的affinity,因为A是Task1的第一个activity)不同,所以会先创建Task2



Android activity 启动慢 android启动activity方法_客户端_20


------------------------------------------------------------------------------------------------------------------------


 


↓A与B的affinity不相同,A启动B,由于已经存在Task2的affinity和B的affinity相同,所以当指定FLAG_ACTIVITY_NEW_TASK标志时,Task1上A启动B,会跳转到Task2,又因为指定了FLAG_ACTIVITY_CLEAR_TOP,所以会把Task2 B上面的A清除掉,并调用B的onNewIntent。注意:这里Task1的栈完全不受影响,只是切换到了后台。



Android activity 启动慢 android启动activity方法_android_21


------------------------------------------------------------------------------------------------------------------------


 


(5)设置FLAG_ACTIVITY_MULTIPLE_TASK。这个flag必须要和FLAG_ACTIVITY_NEW_TASK或者FLAG_ACTIVITY_NEW_DOCUMENT搭配使用。不管activity的affinity是否和task的affinity相同,都会创建多个task。一般不使用这个flag



Android activity 启动慢 android启动activity方法_包名_22


-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


 


(6)设置FLAG_ACTIVITY_CLEAR_TASK。一定要和FLAG_ACTIVITY_NEW_TASK同时使用。如果启动的activity已经存在,则activity所在的task的所有activity都会被清除,然后把新的activity加入到清空后的task中。



Android activity 启动慢 android启动activity方法_android_23


------------------------------------------------------------------------------------------------------------------------


 


(7)设置FLAG_ACTIVITY_SINGLE_TOP。如果启动的activity已经位于task的栈顶,则不会创建一个activity,而是调用activity的onNewIntent方法。如果不存在或者不位于栈顶,便创建新实例。


 


↓B启动B,因为已经存在B,并且位于栈顶,所以只是调用B的onNewIntent



Android activity 启动慢 android启动activity方法_android_24


------------------------------------------------------------------------------------------------------------------------


 


↓B启动A,虽然已经存在A实例,但由于不是在Task1的栈顶,所以会添加一个新的实例到Task1。虽然Task2的A是在栈顶,但因为是在Task1中B启动A,并且没有指定FLAG_ACTIVITY_NEW_TASK,所以不会影响到Task2



Android activity 启动慢 android启动activity方法_客户端_25


------------------------------------------------------------------------------------------------------------------------


 


(8)设置FLAG_ACTIVITY_SINGLE_TOP和FLAG_ACTIVITY_NEW_TASK



Android activity 启动慢 android启动activity方法_android_26


------------------------------------------------------------------------------------------------------------------------


 



Android activity 启动慢 android启动activity方法_android_27


------------------------------------------------------------------------------------------------------------------------


 


(9)其他flag和activity属性


*FLAG_ACTIVITY_BROUGHT_TO_FRONT不由app设置,如launchMode为singleTask系统自动设置该flag


*FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS,一般和FLAG_ACTIVITY_NEW_TASK结合使用,当退出当前task后,不保存在recent app中


 


*alwaysRetainTaskState属性,该属性只对task的根activity有效。用户在启动一个task后,如果一段时间内未访问task时,系统将会清除该task(从根activity之上的堆栈中移除所有activity)。如果希望保留这些activity,将该属性设为true


 


launchMode为singleTop


launchMode为singleTop模式,如果设置为singleTop模式的activity实例位于task的栈顶,则不会创建一个新实例,而是调用该activity的onNewIntent方法。如果activity不在task的栈顶,则和standard模式的activity一样,会创建新的activity实例。这和FLAG_ACTIVITY_SINGLE_TOP有点类似。


 


(1)不设置flag


 


↓B启动A,因为A不在栈顶,所以会新建一个A实例



Android activity 启动慢 android启动activity方法_android_28


------------------------------------------------------------------------------------------------------------------------


 


↓B启动B,因为B在栈顶,所以只会调用B的onNewIntent



Android activity 启动慢 android启动activity方法_android_29


------------------------------------------------------------------------------------------------------------------------


 


(2)设置FLAG_ACTIVITY_NEW_TASK


 


↓B启动B,如果原来不存在和B affinity相同的task,则在当前B调用onNewIntent方法,不会新建task(比较特殊)



Android activity 启动慢 android启动activity方法_android_30


------------------------------------------------------------------------------------------------------------------------


 


↓B启动B,如果原来已经存在和B affinity相同的task,则在该task上调用onNewIntent,并把该task切换到前台,而不是当前task



Android activity 启动慢 android启动activity方法_客户端_31


------------------------------------------------------------------------------------------------------------------------


 


launchMode为singleTask


launchMode为singleTask模式的activity具有系统唯一性,只能在系统中创建该activity的一个实例对象。启动时,如果系统已经存在该activity的实力对象,则将其所在task中排在它前面的activity都弹出栈,将该activity带到栈顶,并调用onNewIntent方法。如果该activity在系统中还没有实例对象,则会创建一个该activity的实例对象。如果存在task的affinity和该activity的affinity相同,则把该activity加入到这个task中。否则,就算启动该activity的Intent没有指定FLAG_ACTIVITY_NEW_TASK标志,也会启动新的task,将activity至于其中。


 



Android activity 启动慢 android启动activity方法_客户端_32


------------------------------------------------------------------------------------------------------------------------



Android activity 启动慢 android启动activity方法_android_33


------------------------------------------------------------------------------------------------------------------------



Android activity 启动慢 android启动activity方法_包名_34


------------------------------------------------------------------------------------------------------------------------



Android activity 启动慢 android启动activity方法_客户端_35


------------------------------------------------------------------------------------------------------------------------


 


launchMode为singleInstance


launchMode为singleInstance模式的activity具有系统唯一性,是singleTask的加强版,只能在系统中创建该activity的一个实例对象,同时activity位于一个单独的task中,该task中也只有一个activity。


 



Android activity 启动慢 android启动activity方法_android_36


------------------------------------------------------------------------------------------------------------------------



Android activity 启动慢 android启动activity方法_客户端_37


------------------------------------------------------------------------------------------------------------------------


 


③小结


第③步中,所谓的确定的task,就算通过上述flag和launchMode确定task后,然后和第②步创建的ActivityRecord关联起来。如果新建了task,则把task加到ActivityStack中


ActivityRecord.setTask(task,,,,);
stack.addTask(task,,,,);


其本质就是将ActivityRecord内部的task成员指向一个TaskRecord结构,如下图



Android activity 启动慢 android启动activity方法_包名_38


 


④将ActivityRecord加入到task顶部


将第②步创建的ActivityRecord加入到第③步确定的task的mActivities这个list中


task.addActivityToTop(r);
task.setFrontOfTask();


其本质如下图所示



Android activity 启动慢 android启动activity方法_android_39


 


⑤暂停当前activity


第⑤步的主要工作是暂停当前的activity,调用WindowManager处理activity的显示


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



⑥启动目标进程


调用AMS的startProcessLocked来启动新进程。



Android activity 启动慢 android启动activity方法_客户端_40


 


最终切换到新进程的ActivityThread的main方法中,这也是新进程的入口。从这里看出,Android弱化了进程的概念,强化了组件的概念。当组件启动过程中,如果发现进程还没启动,才去启动进程。


 


⑦创建消息队列


调用Looper.prepareMainLooper()创建消息队列,所谓的消息队列,其实就是创建一个Looper对象,Looper对象里面包含MessageQueue这样一个消息队列,main thread就一直循环处理消息队列上的消息。通过handler去post或者send消息到消息队列上
从一般的调用栈也可以看出,很多消息都从looper上调用过来



Android activity 启动慢 android启动activity方法_包名_41


 


 



Android activity 启动慢 android启动activity方法_包名_42


 


⑧发送BIND_APPLICATION和LAUNCH_ACTIVITY消息



Android activity 启动慢 android启动activity方法_包名_43


 


⑨处理BIND_APPLICATION消息


如下面的调用栈,处理BIND_APPLICATION消息,调用ActivityThread的handBindApplication,创建application实例,最终会调用到application.onCreate()。只有进程首次创建的时候会调用



Android activity 启动慢 android启动activity方法_客户端_44


 


⑩处理LAUNCH_ACTIVITY消息


处理LAUNCH_ACTIVITY消息,调用handleLaunchActivity,handleLaunchActivity主要分为两个部分,performLaunchActivity和handleResumeActivity。


(1)performLaunchActivity。期间会创建activity,最终会分别调用activity的attach、onCreate和onStart


(2)handleResumeActivity。最终调用activity的onResume。


 


 


按back键流程


按back键退出当前activity的finishActivity流程图

Android activity 启动慢 android启动activity方法_android_45


 


按home键流程



Android activity 启动慢 android启动activity方法_客户端_46


 


按power键灭屏流程



Android activity 启动慢 android启动activity方法_包名_47


 


超时灭屏流程(自然锁屏)



Android activity 启动慢 android启动activity方法_包名_48


 


解锁屏幕流程



Android activity 启动慢 android启动activity方法_包名_49


 


Activity生命周期


学习了上面的知识,现在再看activity的生命周期是不是能看到更多的东西了呢



Android activity 启动慢 android启动activity方法_android_50


 



Android activity 启动慢 android启动activity方法_包名_51