知识就是力量,时间就是生命。这篇文章主要讲述Android 7.0 ActivityManagerService 启动Activity的过程:一相关的知识,希望能为你提供帮助。
从这一篇博客开始,
我们将阅读AMS启动一个Activity的代码流程。
自己对Activity的启动过程也不是很了解,
这里就初步做一个代码阅读笔记,
为以后的迭代打下一个基础。
一、基础知识
在分析Activity的启动过程前,
有必要先了解一下Activity相关的基础知识。
1、Task和Activity的设计理念
关于android中Task和Activity的介绍,
个人觉得《深入理解Android》中的例子不错。
我们就借鉴其中的例子,
进行相应的说明:
文章图片
上图列出了用户在Android系统上想干的三件事, 分别用A、B、C表示。
在Android中, 每一件事可以被看作是一个Task; 一个Task可以被细分成多个子步骤, 每个子步骤可以被看作是一个Activity。
从上图可以看出, A、B两个Task使用了不同的Activity来完成相应的任务, 即A、B两个Task的Activity之间没有复用。
但是在Task C中, 分别使用了Task A中的A1、Task B中的B2。
这么设计的原因是: 用户想做的事情( Task) 即使完全不同, 但是当细分Task为Activity时, 就可能出现Activity功能类似的情况。
当Task A和Task B中已经有能满足需求的Activity时, Task C就会优先复用而不是重新创建Activity。
通过重用Activity可以节省一定的开销, 同时为用户提供一致的界面和用户体验。
对Android的设计理念有一定的了解后, 我们看看Android是如何组织Task及它所包含的Activity。
文章图片
上图为一个比较经典的示例: 图中的Task包含4个Activity。用户可以单击按钮跳转到下一个Activity。同时, 通过返回键可以回到上一个Activity。
图中虚线下方为Activity的组织方式。从图中可以看出, Android是以Stack的方式来管理Activity的。
先启动的Activity成为栈底成员, 被启动的Activity将作为栈顶成员显示在界面上。
当按返回键时, 栈顶成员出栈, 前一个Activity成为栈顶显示在界面上。
以上是一个Task的情况。当有多个Task时, Android系统只支持一个处于前台的Task, 其余的Task均处于后台。
这些后台Task内部Activity保持顺序不变。用户可以一次将整个Task挪到后台或置为前台, 如下图所示:
文章图片
在AMS中, 将用ActivityRecord来作为Activity的记录者、TaskRecord作为Task的记录者, TaskRecord中有对应的ActivityStack专门管理ActivityRecord。
2、启动模式
Android定义了4种Activity的启动模式, 分别为Standard、SingleTop、SingleTask和SingleInstance。
Standard模式
我们平时直接创建的Activity都是这种模式。
这种模式的Activity的特点是: 只要你创建并启动了Activity实例, Android就会向当前的任务栈中加入新创建的实例。退出该Activity时, Android就会在任务栈中销毁该实例。
因此, 一个Task中可以有多个相同类型的Activity( 类型相同, 但不是同一个对象) 。
Standard模式启动Activity的栈结构如下图所示:
文章图片
SingleTop模式
这种模式会考虑当前要激活的Activity实例在任务栈中是否正处于栈顶。
如果处于栈顶则无需重新创建新的实例, 将重用已存在的实例,
否则会在任务栈中创建新的实例。
SingleTop模式启动Activity的栈结构如下图所示:
文章图片
注意: 当用SingleTop模式启动位于栈顶的Activity时, 并不会创建新的Activity, 但栈顶Activity的onNewIntent函数将被调用。
SingleTask模式
在该种模式下, 只要Activity在一个栈中存在, 那么多次启动此Activity都不会重新创建实例。和SingleTop一样, 系统也会回调其onNewIntent。
具体一点, 当一个具有singleTask模式的Activity A请求启动后, 系统先会寻找是否存在A想要的任务栈。
如果不存在对应任务栈, 就重新创建一个任务栈, 然后创建A的实例后, 把A放到任务栈中。
如果存在A所需的任务栈, 那么系统将判断该任务栈中是否有实例A。
如果有实例A, 那么系统就将A调到栈顶并调用其onNewIntent方法( 会清空A之上的Activity) 。
如果没有实例A, 那么系统就创建实例A并压入栈中。
SingleTask模式启动Activity的栈结构如下图所示:
文章图片
SingleInstance模式
SingleInstance模式是一种加强版的SingleTask模式, 它除了具有SingleTask所有的特性外, 还加强了一点, 那就是具有此模式的Activity只能单独地位于一个任务栈中。
3、Intent Flags
启动模式主要是配置在xml文件中的, 例如:
<
activity android:name=
"
.TestActivity"
android:launchMode=
"
singleTask"
>
除了启动模式外, Android在用Intent拉起Activity时, 还可以使用Intent Flags控制Activity及Task之间的关系。
Intent Flags数量非常多, 这里只列举其中的一部分:
Intent.FLAG_ACTIVITY_NEW_TASK
默认的跳转类型, 将目标Activity放到一个新的Task中。
Intent.FLAG_ACTIVITY_CLEAR_TASK
当用这个FLAG启动一个Activity时, 系统会先把与该Activity有关联的Task释放掉, 然后启动一个新的Task, 并把目标Activity放到新的Task。
该标志必须和Intent.FLAG_ACTIVITY_NEW_TASK一起使用。
FLAG_ACTIVITY_SINGLE_TOP
这个FLAG就相当于启动模式中的singleTop。
例如:原来栈中结构是A B C D。现在, 在D中启动D, 那么栈中的结构还是A B C D。
FLAG_ACTIVITY_CLEAR_TOP
这个FLAG类似于启动模式中的SingleTask。
这种FLAG启动的Activity会其之上的Activity全部弹出栈空间。
例如: 原来栈中的结构是A B C D , 从D中跳转到B, 栈中的结构就变为了A B了。
FLAG_ACTIVITY_NO_HISTORY
用这个FLAG启动的Activity, 一旦退出, 就不会存在于栈中。
例如: 原来栈中的结构是A B C, 现在用这个FLAG启动D。然后在D中启动E, 栈中的结构为A B C E。
对这些基础知识有了一定的了解后, 我们来看看AMS启动Activity的代码级流程。
在这一篇博客中, 我们对代码流程的分析, 将截止于启动Activity对应的进程。
于是, 这部分流程中大部分的内容, 将围绕Activity如何选择对应的Task来展开,
由于Task的选择还要涉及对启动模式、Intent Flags等的判断,
因此整个代码将极其的琐碎, 需要很有耐心才能较仔细地看完。
二、am命令
我们将看看利用am命令如何启动一个Activity。
之所以选择从am命令入手, 是因为当我们从一个Activity拉起另一个Activity时,
当前Activity对应的进程需要和AMS进行交互,
这就要求我们需要对进程中与AMS交互的对象比较了解时, 才比较容易分析。
而从am入手分析, 当被启动Activity被创建后, 代码流程自然就会涉及到这个进程与AMS的交互,
整个逻辑的顺序很容易理解。
当我们利用adb shell进入到手机的控制台后, 可以利用am命令启动Activity、Service等。
具体的格式类似于:
am start -W -n 包名(package)/包名.activity名称
例如, 启动浏览器的命令是:
am start -W -n com.android.browser/com.android.browser.BrowserActivity
上面命令中的-W是一个可选项, 表示等待目标activity启动后, am才返回结果;
-n , 表示后接COMPONENT。
am命令可接的参数有很多种, 有兴趣可以研究一下, 此处不再一一列举。
如同之前介绍pm安装apk的流程中提及的, pm命令是一个执行脚本。
am与pm一样, 同样是定义于手机中的执行脚本。
am脚本的文件路径是frameworks/base/cms/am, 其内容如下:
#!/system/bin/sh
#
# Script to start "
am"
on the device, which has a very rudimentary
# shell.
#
base=
/system
export CLASSPATH=
$base/framework/am.jar
exec app_process $base/bin com.android.commands.am.Am "
$@
"
与调用pm命令类似, 调用am命令同样最终会调用到Am.java(frameworks/base/cmds/am/src/com/android/commands/am)的main函数。
这里的调用过程可以参考Android7.0 PackageManagerService (3) APK安装的第二部分。
现在我们直接看看Am.java的main函数:
/**
* Command-line entry point.
*
* @
param args The command-line arguments
*/
public static void main(String[] args) {
//创建一个Am对象,
然后执行run函数
(new Am()).run(args);
}
Am继承自BaseCommand, 上面的run函数定义于BaseCommand中:
/**
* Call to run the command.
*/
public void run(String[] args) {
..........
//将字符串封装到对象中,
mArgs的类型为ShellCommand
mArgs.init(null, null, null, null, args, 0);
..........
try {
//子类实现
onRun();
} catch (IllegalArgumentException e) {
......
} catch (Exception e) {
......
}
}
现在进入到Am.java的onRun函数:
public void onRun() throws Exception {
mAm =
ActivityManagerNative.getDefault();
..............
mPm =
IPackageManager.Stub.asInterface(ServiceManager.getService("
package"
));
...............
//从父类的mArgs中中取出第一个参数
String op =
nextArgRequired();
if (op.equals("
start"
)) {
runStart();
} else if (op.equals("
startservice"
)) {
runStartService();
} .........
...........
}
从代码可以看出, am命令的功能很多, 此处我们主要看看start相关的runStart函数:
private void runStart() throws Exception {
//makeIntent会解析参数,
得到对应的Intent
//主要是结合Intent的parseCommandArgs函数和Am内部定义的CommandOptionHandler解析字符串
//比较简单,
不做深入分析
Intent intent =
makeIntent(UserHandle.USER_CURRENT);
..........
//获取mimeType
String mimeType =
intent.getType();
if (mimeType =
=
null &
&
intent.getData() !=
null
&
&
"
content"
.equals(intent.getData().getScheme())) {
//如果是"
content"
类型的数据,
那么利用AMS获取对应的mimeType
mimeType =
mAm.getProviderMimeType(intent.getData(), mUserId);
}
..........
do {
if (mStopOption) {
//处理-S选项,
即先停止对应的Activity,
再启动它
//这些变量,
均是makeIntent函数解析参数得到的
...............
}
............
//通过am命令启动的Activity,
附加了标志FLAG_ACTIVITY_NEW_TASK
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
............
if (mProfileFile !=
null) {
//处理-P选项,
用于性能统计
.............
}
.............
//通过添加参数--stack,
可以指定Activity加入到特定的Task中
//此处就是将对应的Stack Id将被写入到options中
//与脚本命令中的 -W 一样,
--stack是一个可选项
ActivityOptions options =
null;
if (mStackId !=
INVALID_STACK_ID) {
options =
ActivityOptions.makeBasic();
options.setLaunchStackId(mStackId);
}if (mWaitOption) {
//如果有-W选项,
进入该分支
result =
mAm.startActivityAndWait(null, null, intent, mimeType,
null, null, 0, mStartFlags, profilerInfo,
options !=
null ? options.toBundle() : null, mUserId);
res =
result.result;
} else {
//不等待activity启动,
直接返回
res =
mAm.startActivityAsUser(null, null, intent, mimeType,
null, null, 0, mStartFlags, profilerInfo,
options !=
null ? options.toBundle() : null, mUserId);
}
//判断am命令是否执行成功,
成功时会break
..........
mRepeat--;
.........
}while (mRepeat >
1);
}
从上面的代码可以看出, am最终将调用AMS的startActivityAndWait或startActivityAsUser函数, 来启动参数指定的Activity。
我们以startActivityAndWait为例进行分析。
三、startActivityAndWait流程
startActivityAndWait的参数比较多, 先来大致看一下参数的含义:
public final WaitResult startActivityAndWait(
//在多数情况下,
一个Activity的启动是由一个应用进程发起的
//IApplicationThread是应用进程和AMS交互的通道
//通过am启动Activity时,
该参数为null
IApplicationThread caller, //应用进程对应的pacakge
String callingPackage,//启动使用的Intent和resolvedType
Intent intent, String resolvedType, //均是给Activity.java中定义的startActivityForResult使用的
//resultTo用于接收返回的结果,
resultWho用于描述接收结果的对象
//requestCode由调用者定义
IBinder resultTo, String resultWho, int requestCode,//Intent携带的start activity对应的flag
int startFlags,//性能统计有关
ProfilerInfo profilerInfo, //用于指定Activity的一些选项
//从前面调用的代码来看,
应该是指定Activity需要加入的Task
Bundle bOptions,//表示调用的用户ID
int userId) {
..................
}
现在我们看看startActivityAndWait函数的具体内容:
public final WaitResult startActivityAndWait(....) {
//进行权限检查相关的工作
.............//用于存储处理结果
WaitResult res =
new WaitResult();
//进入ActivityStarter中的流程
mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
null, null, resultTo, resultWho, requestCode, startFlags, profilerInfo, res, null,
bOptions, false, userId, null, null);
return res;
}
上面代码中的ActivityStarter初始化于AMS的构造函数中, 专门负载启动Activity相关的工作。
当我们通过am命令启动一个Activity时, 假设系统之前没有启动过该Activity, 那么从功能的角度来看, ActivityStarter调用artActivityMayWait函数后, 系统将完成以下工作:
1、上文提及在Am.java中, 为Intent增加了标志位FLAG_ACTIVITY_NEW_TASK, 因此系统将为Activity创建ActivityRecord和对应的TaskRecord。
2、系统需要启动一个新的应用进程以加载并运行该Activity。
3、还需要停止当前正在显示的Activity。
接下来, 我们跟进一下ActivityStarter的startActivityMayWait函数。
我们可以将该函数分为三部分进行分析:
1 第一部分
final int startActivityMayWait(............) {
...............
//判断是否指定了组件名
boolean componentSpecified =
intent.getComponent() !=
null;
...............//利用PKMS解析满足Intent等参数要求的信息
ResolveInfo rInfo =
mSupervisor.resolveIntent(intent, resolvedType, userId);
...............// Collect information about the target of the Intent.
// mSupervisor的类型为ActivityStackSupervisor,
负责管理Activity和对应Task之间的关系
// 此处,
ActivityStackSupervisor实际仅从ResolveInfo中取出对应的ActivityInfo
ActivityInfo aInfo =
mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
//得到options,
其中可能指定了Activity需要加入的Task
ActivityOptions options =
ActivityOptions.fromBundle(bOptions);
ActivityStackSupervisor.ActivityContainer container =
(ActivityStackSupervisor.ActivityContainer)iContainer;
synchronized (mService) {
//从am启动时,
container为null
if (container !=
null &
&
container.mParentActivity !=
null &
&
container.mParentActivity.state !=
RESUMED) {
// Cannot start a child activity if the parent is not resumed.//如果从一个Activity启动另一个Activity,
从此处代码可以看出,
//要求父Activity已经执行过onResume
return ActivityManager.START_CANCELED;
}final int realCallingPid =
Binder.getCallingPid();
final int realCallingUid =
Binder.getCallingUid();
....................//以下代码是决定启动Activity时的Task
final ActivityStack stack;
if (container =
=
null || container.mStack.isOnHomeDisplay()) {
//am启动,
或Home来启动Activity
//stack为前台栈
stack =
mSupervisor.mFocusedStack;
} else {
//当从一个Activity启动另一个Activity时,
//启动栈为父Activity的Task
stack =
container.mStack;
}//am启动时config =
=
null
stack.mConfigWillChange =
config !=
null &
&
mService.mConfiguration.diff(config) !=
0;
.................//正常情况下,
当一个Application退到后台时,
系统会为它保存状态;
当调度其到前台时,
恢复它之前的状态,
以保证用户体验的连续性
//AndroidManifest.xml中的Application标签可以申明一个CANT_SAVE_STATE属性
//设置了该属性的Application将不享受系统提供的状态保存/恢复功能,
被称为heavy-weight process
if (aInfo !=
null &
&
(aInfo.applicationInfo.privateFlags
&
ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) !=
0) {
............................
}
...................
}
}
【Android 7.0 ActivityManagerService 启动Activity的过程(一)】从上面的代码来看, startActivityMayWait在第一阶段最主要的工作其实就是:
1、解析出与Intent相匹配的ActivityInfo。
2、得到启动该Activity的Task, 即父Activity的Task或前台Task。
2 第二部分
..................
//用于保存启动Activity后,
对应的ActivityRecord
final ActivityRecord[] outRecord =
new ActivityRecord[1];
//调用startActivityLocked函数,
进行实际的启动工作
int res =
startActivityLocked(...............);
..................
这一部分中, 涉及到了启动Activity的核心函数startActivityLocked。该函数比较复杂, 我们在后面单独分析。
当该函数成功执行完毕后, Activity将会被启动, 并形成对应的ActivityRecord被AMS统一管理。
我们先看看startActivityMayWait函数第三部分的工作。
3 第三部分
...................
//outResult不等于null,
表示等待启动结果
//目标Activity要运行在一个新的应用进程中,
因此需要等待应用进程正常启动并处理相关请求
if (outResult !=
null) {
outResult.result =
res;
if (res =
=
ActivityManager.START_SUCCESS) {
mSupervisor.mWaitingActivityLaunched.add(outResult);
do {
try {
//一直等待,
直到outResult显示Activity对应的Task成为front task
mService.wait();
} catch (InterruptedException e) {
}
} while (outResult.result !=
START_TASK_TO_FRONT
&
&
!outResult.timeout &
&
outResult.who =
=
null);
if (outResult.result =
=
START_TASK_TO_FRONT) {
res =
START_TASK_TO_FRONT;
}
}if (res =
=
START_TASK_TO_FRONT) {
//Activity对应的task拉到前台后,
一直要等到该界面被加载
ActivityRecord r =
stack.topRunningActivityLocked();
if (r.nowVisible &
&
r.state =
=
RESUMED) {
outResult.timeout =
false;
outResult.who =
new ComponentName(r.info.packageName, r.info.name);
outResult.totalTime =
0;
outResult.thisTime =
0;
} else {
outResult.thisTime =
SystemClock.uptimeMillis();
mSupervisor.mWaitingActivityVisible.add(outResult);
do {
try {
mService.wait();
} catch (InterruptedException e) {
}
} while (!outResult.timeout &
&
outResult.who =
=
null);
}
}
}
...............
从上面的代码可以看出, 第三阶段的工作就是根据返回值做一些处理。
由于我们在输入的命令时, 指定了-W选项, 因此将进入wait状态等待Activity界面被显示。
四、startActivityLocked流程
接下来, 我们看看上面提及到的核心函数startActivityLocked:
final int startActivityLocked(..............) {
//err用于保存错误信息
int err =
ActivityManager.START_SUCCESS;
//用于保存启动Activity对应的进程信息
ProcessRecord callerApp =
null;
//如果参数中的调用者不为空,
则从AMS中找到对应的ProcessRecord,
目的是得到调用者的pid和uid
//当利用am命令启动时,
caller等于null
if (caller !=
null) {
callerApp =
mService.getRecordForAppLocked(caller);
if (callerApp !=
null) {
callingPid =
callerApp.pid;
callingUid =
callerApp.info.uid;
} else {
................
}
}final int userId =
aInfo !=
null ? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
.........................
//sourceRecord用于保存父Activity的信息
ActivityRecord sourceRecord =
null;
//resultRecord用于保存接收启动结果的Activity
ActivityRecord resultRecord =
null;
//对于startActivityForResult才有意义
if (resultTo !=
null) {
//利用ActivityStackSupervisor判断是否有resultTo对应的ActivityRecord
//这里的隐含条件是,
resultTo的对象就是父Activity
sourceRecord =
mSupervisor.isInAnyStackLocked(resultTo);
.................
if (sourceRecord !=
null) {
if (requestCode >
=
0 &
&
!sourceRecord.finishing) {
resultRecord =
sourceRecord;
}
}
}//得到启动Activity使用的标志位
final int launchFlags =
intent.getFlags();
if ((launchFlags &
Intent.FLAG_ACTIVITY_FORWARD_RESULT) !=
0 &
&
sourceRecord !=
null) {
// Transfer the result target from the source activity to the new
// one being started, including any failures.
//以这个标签启动的Activity,
将接收原本发往父Activity的result
//这部分代码没细看,
感觉没什么用吧。。。
....................
}//检查一些条件是否满足,
修改err的状态
.....................//得到接收启动结果的Task
final ActivityStack resultStack =
resultRecord =
=
null ? null : resultRecord.task.stack;
if (err !=
START_SUCCESS) {
if (resultRecord !=
null) {
//如果存在err,
需要返回错误信息
resultStack.sendActivityResultLocked(
-1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null);
}
ActivityOptions.abort(options);
return err;
}//检查权限
boolean abort =
!mSupervisor.checkStartAnyActivityPermission(.............);
//根据IntentFirewall判断Intent是否满足要求
abort |=
!mService.mIntentFirewall.checkStartActivity(.............);
//通过接口,
可以为AMS设置一个IActivityController类型的监听者;
AMS进行操作时,
将会回调该监听者
//例如进行Monkey测试的时候,
Monkey会设置该回调对象
if (mService.mController !=
null) {
try {
Intent watchIntent =
intent.cloneFilter();
//交给回调对象处理,
判断能否进行后续流程
//进行Monkey测试时,
可以设置黑名单,
处于黑名单中的Activity将不能启动
abort |=
!mService.mController.activityStarting(watchIntent,
aInfo.applicationInfo.packageName);
} catch (RemoteException e) {
mService.mController =
null;
}
}
................
//以上任一条件不满足时,
进行通知
if (abort) {
if (resultRecord !=
null) {
resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
RESULT_CANCELED, null);
}
// We pretend to the caller that it was really started, but
// they will just get a cancel result.
ActivityOptions.abort(options);
return START_SUCCESS;
}// If permissions need a review before any of the app components can run, we
// launch the review activity and pass a pending intent to start the activity
// we are to launching now after the review is completed.
//在必要时,
再检查一下权限,
代码未细看,
暂时觉得没有必要看
if (Build.PERMISSIONS_REVIEW_REQUIRED &
&
aInfo !=
null) {
................
}
..............
//创建一个ActivityRecord对象
ActivityRecord r =
new ActivityRecord(.........);
if (outActivity !=
null) {
outActivity[0] =
r;
}
.........................
final ActivityStack stack =
mSupervisor.mFocusedStack;
if (voiceSession =
=
null &
&
(stack.mResumedActivity =
=
null
|| stack.mResumedActivity.info.applicationInfo.uid !=
callingUid)) {
//检查调用进程是否有权限切换Activity
if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
realCallingPid, realCallingUid, "
Activity start"
)) {//如果调用进程没有权限进行切换,
则将本次Activity的启动请求保存起来
//后续有机会再进行启动
PendingActivityLaunch pal =
new PendingActivityLaunch(r,
sourceRecord, startFlags, stack, callerApp);
mPendingActivityLaunches.add(pal);
ActivityOptions.abort(options);
return ActivityManager.START_SWITCHES_CANCELED;
}
}//用于控制app switch
if (mService.mDidAppSwitch) {
mService.mAppSwitchesAllowedTime =
0;
} else {
mService.mDidAppSwitch =
true;
}//启动处于pending状态的Activity
doPendingActivityLaunchesLocked(false);
try {
//WindowManager延迟绘制
//个人觉得可能是为了优化性能,
比如当前界面还有细节未绘制完,
但要拉起一个新的界面,
那么此时就不需要绘制了)
mService.mWindowManager.deferSurfaceLayout();
//调用startActivityUnchecked
err =
startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
true, options, inTask);
} finally {
//WindowManager重新开始绘制(
绘制当前的前台界面)
mService.mWindowManager.continueSurfaceLayout();
}
//此处将通知ActivityStarter, Activity对应的Task被移动到前台
postStartActivityUncheckedProcessing(r, err, stack.mStackId, mSourceRecord, mTargetStack);
return err;
}
startActivityLocked函数比较长, 但主干比较清晰, 只是添加许多条件判断。
从代码来看主要工作包括:
1、处理sourceRecord和resultRecord。
sourceRecord表示发起本次请求的Activity, 即父Activity对应的信息;
resultRecord表示接收处理结果的Activity。
在一般情况下, sourceRecord和resultRecord应指向同一个Activity。
2、处理app switch。
如果AMS当前禁止app switch, 那么AMS会将本次请求保存起来, 以待允许app switch时再进行处理。
从代码可以看出, 当AMS可以进行app switch时, 在处理本次的请求前, 会先调用doPendingActivityLaunchesLocked函数。
doPendingActivityLaunchesLocked函数将启动之前因系统禁止app switch而保存的请求。
3、调用startActivityUnchecked处理本次Activity的启动请求。
在分析接下来的流程前, 我们先看看app switch相关的内容。
在AMS中, 提供了两个函数stopAppSwitches和resumeAppSwitches, 用于暂时禁止App切换及恢复切换。
这种需求的考虑是: 当某些重要的Activity处于前台时, 不希望系统因为用户操作之外的原因切换Activity。
1、stopAppSwitches
先来看看stopAppSwitches:
public void stopAppSwitches() {
//检查调用进程是否有STOP_APP_SWITCHES权限
..............synchronized(this) {
//设置了一个超时时间,
目前为5s
//过了该时间,
AMS可以重新切换App
mAppSwitchesAllowedTime =
SystemClock.uptimeMillis()
+
APP_SWITCH_DELAY_TIME;
mDidAppSwitch =
false;
//发送一个延迟消息,
触发允许App Switch的操作
mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
Message msg =
mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG);
mHandler.sendMessageDelayed(msg, APP_SWITCH_DELAY_TIME);
}
}
对于上面的代码, 需要注意两点:
1、此处的控制机制名为app switch, 而不是Activity switch。
这是因为如果从受保护的Activity中启动另一个Activity, 那么这个新的Activity的目的应该是针对同一个任务。
于是这次的启动就不应该受app switch的制约。
2、执行stopAppSwitches后, 应用程序应该调用resumeAppSwitches以允许app switch。
为了防止应用程序有意或者无意没调用resumeAppSwitches, 在stopAppSwitches中设置了一个超时时间, 过了此超时时间, 系统会发送一个消息触发App Switch的操作。
2、resumeAppSwitches
现在我们看看resumeAppSwitches的代码:
public void resumeAppSwitches() {
//同样是进行权限检查
.............synchronized(this) {
// Note that we don'
t execute any pending app switches... we will
// let those wait until either the timeout, or the next start
// activity request.
mAppSwitchesAllowedTime =
0;
}
}
从代码可以看出, resumeAppSwitches只设置了mAppSwitchesAllowedTime的值为0, 它并不处理在stop和resume这段时间内积攒起的Pending请求。
根据前面startActivityLocked函数, 我们知道如果在执行resume app switch后, 又有新的请求需要处理, 则先调用doPendingActivityLaunchesLocked处理那些pending的请求。
此外, resumeAppSwitches函数中并没有撤销stopAppSwitches函数中设置的超时消息, 所以当该消息被处理时, 同样会触发处理pending请求的流程。
五、startActivityUnchecked流程
顺着请求的处理流程, 我们接下来看看startActivityUnchecked函数。
startActivityUnchecked函数比较长, 我们分段看一下。
Part-I
第一部分如下代码所示, 主要用于判断是否需要为新的Activity创建一个Task。
private int startActivityUnchecked(.......) {
//根据参数重新设置类的成员变量
//将存储当前Activity对应的启动模式等信息
setInitialState(............);
computeLaunchingTaskFlags();
computeSourceStack();
mIntent.setFlags(mLaunchFlags);
...................
我们依次看看上述代码中的几个函数:
1、setInitialState
private void setInitialState(.........) {
//重置当前类的成员变量
reset();
//用于保存当前准备启动的Activity
mStartActivity =
r;
mIntent =
r.intent;
...........
mSourceRecord =
sourceRecord;
...........mLaunchSingleTop =
r.launchMode =
=
LAUNCH_SINGLE_TOP;
mLaunchSingleInstance =
r.launchMode =
=
LAUNCH_SINGLE_INSTANCE;
mLaunchSingleTask =
r.launchMode =
=
LAUNCH_SINGLE_TASK;
................// We'
ll invoke onUserLeaving before onPause only if the launching
// activity did not explicitly state that this is an automated launch.
//判断是否需要调用因本次Activity启动,
而被系统移到后台的当前Activity的onUserLeaveHint函数
mSupervisor.mUserLeaving =
(mLaunchFlags &
FLAG_ACTIVITY_NO_USER_ACTION) =
=
0;
................if (mOptions !=
null &
&
mOptions.getLaunchTaskId() !=
-1 &
&
mOptions.getTaskOverlay()) {
r.mTaskOverlay =
true;
final TaskRecord task =
mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
final ActivityRecord top =
task !=
null ? task.getTopActivity() : null;
if (top !=
null &
&
!top.visible) {// The caller specifies that we'
d like to be avoided to be moved to the front, so be
// it!
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Android通知Notification全面剖析
- 安卓 Android题目大全
- Android adb常用指令
- Android开发---MediaPlayer简单音乐播放器
- Android艺术开发探索第四章——View的工作原理(下)
- Android 数据库读取数据显示 [5]
- Android关于Theme.AppCompat相关问题的深入分析(转)
- Android ImageView 正确使用姿势
- Android笔记——Application的作用