Activity相关学习(二)
Activity启动流程(基于android-13.0.0_r83)
整体流程
启动方式
Activity主要有三种方式
- 从 Launcher 桌面上点击 App 图标启动一个App。
- App 启动后,按 Home 键退回到 Launcher 界面,再点击 App 图标。
- 同个应用内启动,如从 Activity1 跳转到 Activity2。
启动过程涉及到的进程
- 要启动的Activity所在App 进程
- SystemServer进程
- Zygote
- 启动目标Activity的进程,如Launcher
Activity启动过程跟系统交互主要是通过Binder,其中主要涉及两个server,一个是SystemServer中注册的ActivityTaskManagerService,另外一个是app进程中的ApplicationThread。
ActivityTaskManagerService
- ActivityTaskManagerService(简称 ATMS)是 Android 系统中负责管理 Activity 和 Task 的核心服务之一,运行在system_server 进程中。
- 从 Android 10(Q)开始,Google 对 Activity 和 Task 的管理机制进行了重构,将原本在 ActivityManagerService(AMS)中的部分功能拆分到了 ActivityTaskManagerService 中。
- 负责管理 Activity 的生命周期、任务栈(Task)、多窗口模式(如分屏、自由窗口)等。
ATMS 的作用
- Activity 生命周期管理: 负责 Activity 的启动、暂停、恢复、销毁等生命周期回调。
- Task 管理: 管理任务栈(Task),包括 Task 的创建、销毁、重新排序等。
- 多窗口模式: 支持分屏、画中画、自由窗口等多窗口模式的实现。
- 启动模式(Launch Mode): 处理 Activity 的启动模式(如 standard、singleTop、singleTask、singleInstance)。
- 任务栈导航: 管理任务栈的导航行为(如返回栈、最近任务列表)。
- 与 WindowManagerService 协作: 与 WindowManagerService(WMS)协作,管理 Activity 的窗口显示。
ATMS 的相关架构
- ActivityManagerService(AMS): 负责进程管理、内存管理等,与 ATMS 协作完成 Activity 的管理。
- WindowManagerService(WMS): 负责窗口管理,与 ATMS 协作完成 Activity 的显示。
- ActivityStack: 表示一个任务栈(Task),管理栈中的 Activity。
- ActivityRecord: 表示一个 Activity 实例,包含 Activity 的状态信息。
ATMS 的核心功能
- Activity 生命周期管理
ATMS 负责处理 Activity 的生命周期回调,当应用启动一个 Activity 时,ATMS 会调用相应的生命周期方法,并确保 Activity 的状态正确切换。包括:
- onCreate()
- onStart()
- onResume()
- onPause()
- onStop()
- onDestroy()
- Task 管理
Task 是一个 Activity 的集合,通常表示一个用户任务(如打开一个应用)。ATMS 负责管理 Task 的创建、销毁和重新排序。
- 当用户点击桌面图标启动应用时,ATMS 会创建一个新的 Task。
- 当用户按下返回键时,ATMS 会销毁当前 Activity 并返回到上一个 Activity。
- 多窗口模式
ATMS 支持多种多窗口模式, ATMS 负责管理多窗口模式下 Activity 的布局和生命周期。包括:
- 分屏模式(Split-screen): 两个应用共享屏幕。
- 画中画模式(Picture-in-Picture): 视频播放时以小窗口形式显示。
- 自由窗口模式(Freeform): 类似于桌面操作系统的窗口模式。
-
启动模式(Launch Mode)
-
任务栈导航
ATMS 管理任务栈的导航行为,包括:
- 返回栈(Back Stack): 当用户按下返回键时,ATMS 会从返回栈中弹出 Activity。
- 最近任务列表(Recent Tasks): ATMS 负责维护最近任务列表,用户可以快速切换任务。
ATMS 的实现
ATMS是通过AIDL实现的
ApplicationThread
- ApplicationThread 是 ActivityThread 的内部类,扮演着应用进程与系统服务(如 ActivityManagerService 和 ActivityTaskManagerService)之间的桥梁角色。
- ApplicationThread 是一个 Binder 对象,负责接收系统服务的指令并转发给应用进程的主线程(即 ActivityThread),从而实现对应用生命周期、Activity 生命周期等的管理。
ApplicationThread 的作用
- 接收系统服务的指令: 接收来自 ActivityManagerService(AMS)和 ActivityTaskManagerService(ATMS)的指令,例如启动 Activity、启动 Service、绑定 Service、处理广播等。
- 转发指令到主线程: 将系统服务的指令转发到应用的主线程(即 ActivityThread),确保这些操作在主线程中执行。
- 与应用组件交互: 与应用的 Activity、Service、BroadcastReceiver 等组件交互,触发它们的生命周期方法(如 onCreate()、onStart()、onResume() 等)。
ApplicationThread 的核心方法
这些方法都是由系统服务(如 AMS 或 ATMS)调用,然后通过 Binder 机制传递到应用进程的 ApplicationThread,最终由 ActivityThread 在主线程中执行。
- 启动 Activity: scheduleLaunchActivity()
- 启动 Service: scheduleCreateService()
- 绑定 Service: scheduleBindService()
- 处理广播: scheduleReceiver()
- 暂停 Activity: schedulePauseActivity()
- 销毁 Activity: scheduleDestroyActivity()
ApplicationThread 的实现
ApplicationThread是基于AIDL实现
ActivityTaskManagerService和ApplicationThread调用关系
Launcher 界面中启动Activity的流程
流程
源码分析
- 点击桌面图标会触发ItemClickHandler.java的onClick()。
/*
* @param v 被点击的视图对象(如应用图标、文件夹图标等)
**/
//packages/apps/Launcher3/src/com/android/launcher3/touch/ItemClickHandler.java
private static void onClick(View v) {
...
if (v.getWindowToken() == null) return;
//Launcher 是 Launcher 应用的主 Activity,负责管理主屏幕、应用图标、小部件等。
Launcher launcher = Launcher.getLauncher(v.getContext());
if (!launcher.getWorkspace().isFinishedSwitchingState()) return;
//Launcher 中的每个视图(如应用图标、文件夹图标等)都关联了一个 ItemInfo 对象,用于存储该视图的元数据(如应用包名、组件名、位置信息等)。
Object tag = v.getTag();
//点击的是应用图标
if (tag instanceof WorkspaceItemInfo) {
onClickAppShortcut(v, (WorkspaceItemInfo) tag, launcher);
} else if (tag instanceof FolderInfo) {//点击文件夹图标
if (v instanceof FolderIcon) {
onClickFolderIcon(v);
}
} else if (tag instanceof AppInfo) {//快捷方式
startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher);
} else if (tag instanceof LauncherAppWidgetInfo) {//点击的是小部件
if (v instanceof PendingAppWidgetHostView) {
onClickPendingWidget((PendingAppWidgetHostView) v, launcher);
}
} else if (tag instanceof ItemClickProxy) {
((ItemClickProxy) tag).onItemClicked(v);
}
}
点击应用图标执行下面方法
/**
* Event handler for an app shortcut click.
*
* @param v The view that was clicked. Must be a tagged with a {@link WorkspaceItemInfo}.
*/
public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher) {
...
// Start activities 启动activity
startAppShortcutOrInfoActivity(v, shortcut, launcher);
}
private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
...
Intent intent;
...
//从 WorkspaceItemInfo 中获取 Intent,用于启动应用或显示应用信息。
if (item instanceof WorkspaceItemInfo) {
WorkspaceItemInfo si = (WorkspaceItemInfo) item;
if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)
&& Intent.ACTION_VIEW.equals(intent.getAction())) {
// make a copy of the intent that has the package set to null
// we do this because the platform sometimes disables instant
// apps temporarily (triggered by the user) and fallbacks to the
// web ui. This only works though if the package isn't set
intent = new Intent(intent);
intent.setPackage(null);
}
if ((si.options & WorkspaceItemInfo.FLAG_START_FOR_RESULT) != 0) {
launcher.startActivityForResult(item.getIntent(), 0);
InstanceId instanceId = new InstanceIdSequence().newInstanceId();
launcher.logAppLaunch(launcher.getStatsLogManager(), item, instanceId);
return;
}
}
...
// 启动普通应用
launcher.startActivitySafely(v, intent, item);
}
- 触发Launcher.java的startActivitySafely
//packages/apps/Launcher3/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@Override
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
// Only pause is taskbar controller is not present
mHotseatPredictionController.setPauseUIUpdate(getTaskbarUIController() == null);
//调用父类即Launcher.java
boolean started = super.startActivitySafely(v, intent, item);
if (getTaskbarUIController() == null && !started) {
mHotseatPredictionController.setPauseUIUpdate(false);
}
return started;
}
//packages/apps/Launcher3/src/com/android/launcher3/Launcher.java
@Override
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
//检查 Launcher 是否已经处于 resumed 状态(即完全恢复并可见)。
//如果 Launcher 未完全恢复,直接启动 Activity 可能会导致窗口管理器(WM)的启动动画被破坏。
if (!hasBeenResumed()) {
//如果 Launcher 未完全恢复,则延迟启动 Activity,直到 Launcher 下次恢复。
//使用 addOnResumeCallback 方法注册一个回调,在 Launcher 恢复时执行 startActivitySafely。
addOnResumeCallback(() -> startActivitySafely(v, intent, item));
if (mOnDeferredActivityLaunchCallback != null) {
mOnDeferredActivityLaunchCallback.run();
mOnDeferredActivityLaunchCallback = null;
}
return true;
}
//调用父类ActivityContext的 startActivitySafely 方法
boolean success = super.startActivitySafely(v, intent, item);
//BubbleTextView 是 Launcher 中用于显示应用图标和文件夹图标的视图。
//如果成功启动 Activity,并且触发视图是 BubbleTextView,则设置其按压状态为 true。
if (success && v instanceof BubbleTextView) {
BubbleTextView btv = (BubbleTextView) v;
btv.setStayPressed(true);
//使用 addOnResumeCallback 方法注册一个回调,在 Launcher 恢复时将按压状态重置为 false。
addOnResumeCallback(() -> btv.setStayPressed(false));
}
return success;
}
- 触发ActivityContext.java的startActivitySafely
主要包含以下几个步骤
- 主线程检查:确保方法在主线程中调用。
- 安全模式检查:在安全模式下阻止非系统应用的启动。
- 快捷方式启动:支持深度快捷方式的启动。
- 启动选项:根据视图和 ItemInfo 准备启动选项。
- 用户句柄:支持跨用户启动 Activity。
- 日志记录:记录应用启动日志。
- 异常处理:捕获并处理启动过程中的异常。
//packages/apps/Launcher3/src/com/android/launcher3/views/ActivityContext.java
/**
* 安全地启动一个 Activity,并处理一些特殊情况(如安全模式、快捷方式启动、跨用户启动等)
* @param v 触发启动操作的视图(如应用图标)。
* @param intent 要启动的 Activity 的 Intent。
* @param item 与视图关联的 ItemInfo 对象(如 WorkspaceItemInfo),可以为 null。
* @return RunnableList 用于监听动画结束的回调列表。如果启动失败,则返回 null。
*/
default RunnableList startActivitySafely(
View v, Intent intent, @Nullable ItemInfo item) {
//确保该方法在主线程(UI 线程)中调用,避免在后台线程中启动 Activity。
Preconditions.assertUIThread();
Context context = (Context) this;
//检查安全模式
//如果设备处于安全模式,并且目标应用不是系统应用,则阻止启动并显示提示信息。
if (isAppBlockedForSafeMode() && !PackageManagerHelper.isSystemApp(context, intent)) {
Toast.makeText(context, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
return null;
}
//如果 ItemInfo 是深度快捷方式(ITEM_TYPE_DEEP_SHORTCUT),并且不是占位符(isPromise() 返回 false),则标记为快捷方式启动。
boolean isShortcut = (item instanceof WorkspaceItemInfo)
&& item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
&& !((WorkspaceItemInfo) item).isPromise();
//GO_DISABLE_WIDGETS:如果为 true,则禁用快捷方式启动,并返回 null。
if (isShortcut && GO_DISABLE_WIDGETS) {
return null;
}
//准备启动选项
//如果视图 v 为 null,则调用 makeDefaultActivityOptions 方法创建默认启动选项。
ActivityOptionsWrapper options = v != null ? getActivityLaunchOptions(v, item)
: makeDefaultActivityOptions(item != null && item.animationType == DEFAULT_NO_ICON
? SPLASH_SCREEN_STYLE_SOLID_COLOR : -1 /* SPLASH_SCREEN_STYLE_UNDEFINED */);
//设置用户句柄
UserHandle user = item == null ? null : item.user;
Bundle optsBundle = options.toBundle();
// 准备 Intent
//设置 Intent 的标志位,确保目标 Activity 在新的任务栈中启动。
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (v != null) {
//用于动画过渡效果。
intent.setSourceBounds(Utilities.getViewBounds(v));
}
//启动Activity
try {
//快捷方式启动
if (isShortcut) {
String id = ((WorkspaceItemInfo) item).getDeepShortcutId();
String packageName = intent.getPackage();
((Context) this).getSystemService(LauncherApps.class).startShortcut(
packageName, id, intent.getSourceBounds(), optsBundle, user);
} else if (user == null || user.equals(Process.myUserHandle())) {
// Could be launching some bookkeeping activity
context.startActivity(intent, optsBundle);//普通启动
} else {
//如果用户句柄不是当前用户,跨用户启动 Activity。
context.getSystemService(LauncherApps.class).startMainActivity(
intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
}
...
return options.onEndCallback;
} catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
...
}
return null;
}
Activity启动流程
- 执行Activity的startActivity()
//frameworks/base/core/java/android/app/Activity.java
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
getAutofillClientController().onStartActivity(intent, mIntent);
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
startActivityForResult(intent, -1);
}
}
startActivityForResult主要包含以下几个步骤
- 启动目标 Activity:
- 通过 Instrumentation.execStartActivity 方法启动目标 Activity。
- 处理返回结果:
如果目标 Activity 立即返回结果,则通过 mMainThread.sendActivityResult 方法将结果发送给当前 Activity。 - 标记 Activity 已启动:
如果 requestCode 大于等于 0,则标记当前 Activity 已启动目标 Activity。 - 取消输入并启动退出过渡:
取消当前 Activity 的输入事件,并启动退出过渡动画。 - 支持子 Activity:
如果当前 Activity 有父 Activity,则通过父 Activity 启动目标 Activity。
/*
* @param intent 要启动的 Activity 的 Intent。
* @param requestCode 请求代码,用于标识启动请求。当目标 Activity 返回结果时,该代码会传递给 onActivityResult 方法。
* @param options 启动选项(如动画、过渡效果等),可以为 null。
* @see #startActivity
*/
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
// 检查是否有父 Activity
if (mParent == null) {
//将启动选项转换为适合当前 Activity 的格式。例如,处理与启动动画相关的选项。
options = transferSpringboardActivityOptions(options);
//通过 Instrumentation 执行实际的 Activity 启动操作。
//参数包括当前 Activity 的上下文、应用线程、Activity 的 IBinder 令牌、目标 Intent、请求代码和启动选项。
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
//如果目标 Activity 立即返回结果(如 RESULT_CANCELED),则通过主线程将结果发送给当前 Activity。
//结果会传递给 onActivityResult 方法。
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// 如果 requestCode 大于等于 0,则标记当前 Activity 已启动目标 Activity。
// 这可以避免在目标 Activity 返回结果之前显示当前 Activity,从而减少闪烁。
mStartedActivity = true;
}
//取消输入并启动退出过渡
//取消当前 Activity 的输入事件(如键盘输入)。
//启动退出过渡动画(如 Activity 切换动画)。
cancelInputsAndStartExitTransition(options);
// TODO Consider clearing/flushing other event sources and events for child windows.
} else {
//调用父 Activity 的方法
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
Instrumentation阶段
Instrumentation 用于监控应用程序与系统的交互。它在应用程序的整个生命周期中扮演着重要角色,尤其是在 Activity 的启动、生命周期回调、以及测试框架中。
Instrumentation 的作用
- 监控应用程序的生命周期:如 Activity 的启动、停止、销毁等。
- 注入测试代码:在 Android 测试框架中,Instrumentation 用于注入测试代码,监控应用程序的行为。
- 提供上下文和环境:为应用程序提供测试所需的上下文和环境。
- 处理系统交互:如启动 Activity、发送广播、处理 Intent 等。
Instrumentation 的核心方法
- execStartActivity 启动一个 Activity。
- callActivityOnCreate 调用 Activity 的 onCreate 方法。
- callActivityOnResume 调用 Activity 的 onResume 方法。
- newActivity 创建一个新的 Activity 实例。
- sendKeySync 同步发送一个按键事件。
- runOnMainSync 在主线程中同步执行一个 Runnable。
execStartActivity 执行启动 Activity
execStartActivity 主要包括以下几个步骤
- IApplicationThread:用于与系统服务通信。
- Referrer:处理 Activity 的来源信息。
- ActivityMonitor:监控和拦截 Activity 启动。
- Intent 准备:确保 Intent 可以跨进程传递。
- ActivityTaskManager:与系统服务交互,启动 Activity。
- 异常处理:捕获并处理启动过程中的异常。
//frameworks/base/core/java/android/app/Instrumentation.java
/*
* @param who 启动 Activity 的上下文(通常是当前 Activity)。
* @param contextThread 应用的主线程(IApplicationThread 类型)。
* @param token 当前 Activity 的 IBinder 令牌。
* @param target 目标 Activity(可以为 null)。
* @param intent 要启动的 Activity 的 Intent。
* @param requestCode 请求代码(用于 startActivityForResult)。
* @param options 启动选项(如动画、过渡效果等)。
*
* @return 返回一个 ActivityResult 对象,包含目标 Activity 的结果(如结果代码和返回数据)。如果启动失败,则返回 null。
* {@hide}
*/
@UnsupportedAppUsage
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
//获取 IApplicationThread,表示应用的主线程,用于与系统服务(如 ActivityTaskManager)通信。
IApplicationThread whoThread = (IApplicationThread) contextThread;
//处理 Referrer,将 Referrer 添加到 Intent 的 EXTRA_REFERRER 字段中。
Uri referrer = target != null ? target.onProvideReferrer() : null;
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
//处理 ActivityMonitor,用于监控和拦截 Activity 启动。
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
//遍历所有的 ActivityMonitor。
for (int i=0; i<N; i++) {
final ActivityMonitor am = mActivityMonitors.get(i);
ActivityResult result = null;
//如果 ActivityMonitor 忽略特定的 Intent,则调用 onStartActivity 方法处理。
if (am.ignoreMatchingSpecificIntents()) {
if (options == null) {
options = ActivityOptions.makeBasic().toBundle();
}
result = am.onStartActivity(who, intent, options);
}
//如果 ActivityMonitor 匹配当前的启动请求,则根据其配置决定是否拦截启动。
if (result != null) {
am.mHits++;
return result;
} else if (am.match(who, null, intent)) {
am.mHits++;
if (am.isBlocking()) {
return requestCode >= 0 ? am.getResult() : null;
}
break;
}
}
}
}
try {
//将 Intent 中的 EXTRA_STREAM 数据迁移到 ClipData 中。
intent.migrateExtraStreamToClipData(who);
//准备 Intent,确保它可以跨进程传递。
intent.prepareToLeaveProcess(who);
//启动 Activity
//获取 ActivityTaskManager 的系统服务。
//调用 ActivityTaskManager 的 startActivity 方法,启动目标 Activity。
int result = ActivityTaskManager.getService().startActivity(whoThread,
who.getOpPackageName(), who.getAttributionTag(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()), token,
target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
//通知 Activity 启动结果。
notifyStartActivityResult(result, options);
//检查启动结果,如果失败则抛出异常。
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
execStartActivity执行后启动过程就会进入到服务端 SystemServer的 ATMS Binder 服务中去了。