如何启动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模型
一个binder服务实际上就是一个binder类的对象,该对象创建,内部会启动一个线程。该线程会接收Binder驱动发送的消息,然后执行onTransact()方法,根据不同的code执行不同的功能代码。这个code是客户端调用transact()方法时传入的参数。
当服务端binder对象创建时,同时会在binder驱动创建一个mRemote对象,该对象也是binder类型,客户端要访问服务端,获取binder驱动中的mRemote对象,通过mRemote对象来访问。
AMS的binder模型
了解AMS的binder模型是十分有必要的,因为接下来的startActivity流程都基于这个模型
startActivity流程
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模型对应起来,便于理解,把启动流程划分了一下,如图
startActivity流程的进程示意图
从进程的角度把启动流程划分一下,如图
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调用客户端的代理
上面这些类的关系如下图:
2. /android/frameworks/base/services/core/java/com/android/server/am (基于Android 6.0)
ActivityManagerService:四大组件管理的核心类,同时管理和调度用户进程
ActivityRecord:在AMS中用来保存一个activity的信息
TaskRecord:在AMS中用来保存一个任务的信息,一个任务就是当用户按recent键时显示的最近应用列表,如下图
ActivityStack:管理taskRecord。三星FW包含HOME_STACK_TYPE、NORMAL_APP_STACK_TYPE等,其中HOME_STACK_TYPE类型的ActivityStack一般包含systemUI和launcher所在的taskRecord
ActivityStackSupervisor:管理ActivityStack
上述这些类的大概关系图如下
startActivity简要流程
从startActivity的流程图看出,启动流程十分复杂,为了便于理解,把整个流程精简了一下,提炼出一些关键步骤,如下图
① 匹配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就是让用户选择启动哪个应用,如下图
②创建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
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实例
------------------------------------------------------------------------------------------------------------------------
(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的实例
------------------------------------------------------------------------------------------------------------------------
↓由于C与A的affinity不相同(前提),所以指定FLAG_ACTIVITY_NEW_TASK这个flag,当B启动C时,会创建新Task2(Task2在上面表示Task2在前台,Task1被切换到后台)
------------------------------------------------------------------------------------------------------------------------
↓B启动A,由于Task1的affinity本身就和A相同,所以再启动A,说明已经存在相同affinity的task,便不会创建新task,在task1的基础上添加A的新实例
------------------------------------------------------------------------------------------------------------------------
↓B与A的affinity相同(前提条件),Task1是A启动的(A处于底部),所以Task1的affinity和A的相同,B再启动B,B的affinity又与A的相同,即与Task1的相同,所以不会创建新task,在task1的上面添加新的B实例
------------------------------------------------------------------------------------------------------------------------
(3)设置FLAG_ACTIVITY_CLEAR_TOP。如果启动的activity已经存在,则把该activity带到前台(并调用onNewIntent),并把它前面的activity都弹出栈(相当于调用finish)。如果不存在,则创建新实例。
↓B启动A,由于已经存在A实例,所以直接调用最前面A的onNewIntent方法,并且把B给弹出栈了(finish)
------------------------------------------------------------------------------------------------------------------------
↓Task1的A启动B,由于没有指定FLAG_ACTIVITY_NEW_TASK的flag,所以不会寻找Task2的B实例,也就是会在Task1中创建B的新实例
------------------------------------------------------------------------------------------------------------------------
↓B启动A,由于没有指定FLAG_ACTIVITY_NEW_TASK的flag,所以不会寻找Task2的A实例,并且在Task1中实例A已经存在,就会调用A的onNewIntent,并把A上面的B弹出栈
------------------------------------------------------------------------------------------------------------------------
(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
------------------------------------------------------------------------------------------------------------------------
↓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的栈完全不受影响,只是切换到了后台。
------------------------------------------------------------------------------------------------------------------------
(5)设置FLAG_ACTIVITY_MULTIPLE_TASK。这个flag必须要和FLAG_ACTIVITY_NEW_TASK或者FLAG_ACTIVITY_NEW_DOCUMENT搭配使用。不管activity的affinity是否和task的affinity相同,都会创建多个task。一般不使用这个flag
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(6)设置FLAG_ACTIVITY_CLEAR_TASK。一定要和FLAG_ACTIVITY_NEW_TASK同时使用。如果启动的activity已经存在,则activity所在的task的所有activity都会被清除,然后把新的activity加入到清空后的task中。
------------------------------------------------------------------------------------------------------------------------
(7)设置FLAG_ACTIVITY_SINGLE_TOP。如果启动的activity已经位于task的栈顶,则不会创建一个activity,而是调用activity的onNewIntent方法。如果不存在或者不位于栈顶,便创建新实例。
↓B启动B,因为已经存在B,并且位于栈顶,所以只是调用B的onNewIntent
------------------------------------------------------------------------------------------------------------------------
↓B启动A,虽然已经存在A实例,但由于不是在Task1的栈顶,所以会添加一个新的实例到Task1。虽然Task2的A是在栈顶,但因为是在Task1中B启动A,并且没有指定FLAG_ACTIVITY_NEW_TASK,所以不会影响到Task2
------------------------------------------------------------------------------------------------------------------------
(8)设置FLAG_ACTIVITY_SINGLE_TOP和FLAG_ACTIVITY_NEW_TASK
------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------
(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实例
------------------------------------------------------------------------------------------------------------------------
↓B启动B,因为B在栈顶,所以只会调用B的onNewIntent
------------------------------------------------------------------------------------------------------------------------
(2)设置FLAG_ACTIVITY_NEW_TASK
↓B启动B,如果原来不存在和B affinity相同的task,则在当前B调用onNewIntent方法,不会新建task(比较特殊)
------------------------------------------------------------------------------------------------------------------------
↓B启动B,如果原来已经存在和B affinity相同的task,则在该task上调用onNewIntent,并把该task切换到前台,而不是当前task
------------------------------------------------------------------------------------------------------------------------
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至于其中。
------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------
launchMode为singleInstance
launchMode为singleInstance模式的activity具有系统唯一性,是singleTask的加强版,只能在系统中创建该activity的一个实例对象,同时activity位于一个单独的task中,该task中也只有一个activity。
------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------
③小结
第③步中,所谓的确定的task,就算通过上述flag和launchMode确定task后,然后和第②步创建的ActivityRecord关联起来。如果新建了task,则把task加到ActivityStack中
ActivityRecord.setTask(task,,,,);
stack.addTask(task,,,,);
其本质就是将ActivityRecord内部的task成员指向一个TaskRecord结构,如下图
④将ActivityRecord加入到task顶部
将第②步创建的ActivityRecord加入到第③步确定的task的mActivities这个list中
task.addActivityToTop(r);
task.setFrontOfTask();
其本质如下图所示
⑤暂停当前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来启动新进程。
最终切换到新进程的ActivityThread的main方法中,这也是新进程的入口。从这里看出,Android弱化了进程的概念,强化了组件的概念。当组件启动过程中,如果发现进程还没启动,才去启动进程。
⑦创建消息队列
调用Looper.prepareMainLooper()创建消息队列,所谓的消息队列,其实就是创建一个Looper对象,Looper对象里面包含MessageQueue这样一个消息队列,main thread就一直循环处理消息队列上的消息。通过handler去post或者send消息到消息队列上
从一般的调用栈也可以看出,很多消息都从looper上调用过来
⑧发送BIND_APPLICATION和LAUNCH_ACTIVITY消息
⑨处理BIND_APPLICATION消息
如下面的调用栈,处理BIND_APPLICATION消息,调用ActivityThread的handBindApplication,创建application实例,最终会调用到application.onCreate()。只有进程首次创建的时候会调用
⑩处理LAUNCH_ACTIVITY消息
处理LAUNCH_ACTIVITY消息,调用handleLaunchActivity,handleLaunchActivity主要分为两个部分,performLaunchActivity和handleResumeActivity。
(1)performLaunchActivity。期间会创建activity,最终会分别调用activity的attach、onCreate和onStart
(2)handleResumeActivity。最终调用activity的onResume。
按back键流程
按back键退出当前activity的finishActivity流程图
按home键流程
按power键灭屏流程
超时灭屏流程(自然锁屏)
解锁屏幕流程
Activity生命周期
学习了上面的知识,现在再看activity的生命周期是不是能看到更多的东西了呢