深度剖析 Android Activity 启动与任务栈管理模块:源码级解读与实践洞察
本人掘金号,欢迎点击关注:掘金号地址
本人公众号,欢迎点击关注:公众号地址
一、引言
在 Android 应用开发的庞大体系中,Activity 启动与任务栈管理模块犹如中枢神经系统,掌控着用户界面切换与应用流程的关键走向。从用户轻轻点击应用图标,到在多个 Activity 间自如穿梭,背后是该模块复杂而精妙的运作机制。理解这一模块,不仅能让开发者优化应用启动速度、构建流畅的用户交互体验,还能深入把握 Android 系统的运行逻辑,解决开发中诸如 Activity 重复启动、任务栈混乱等棘手问题。本文将深入 Android 源码,以庖丁解牛之势,全面剖析 Activity 启动与任务栈管理模块的原理与实现细节,为开发者奉上一场技术盛宴。
二、Activity 启动与任务栈管理基础概念
2.1 Activity 启动概述
2.1.1 启动方式分类
在 Android 中,Activity 有多种启动方式,主要分为显式启动和隐式启动。
- 显式启动:开发者明确指定要启动的 Activity 的类名。例如:
java
java
// 创建一个意图,指定要启动的Activity类
Intent intent = new Intent(this, TargetActivity.class);
// 启动Activity
startActivity(intent);
这里this
代表当前 Activity 的上下文,TargetActivity.class
指明了目标 Activity,通过这种方式,系统能精准定位并启动对应的 Activity 实例。
- 隐式启动:不直接指定类名,而是通过在 Intent 中设置动作(Action)、数据(Data)、类别(Category)等属性,由系统根据这些信息在 Manifest 文件中匹配合适的 Activity 来启动。例如:
java
java
// 创建一个意图,设置动作和数据
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
// 启动Activity,系统会寻找能处理该动作和数据的Activity
startActivity(intent);
上述代码中,Intent.ACTION_VIEW
表示查看动作,Uri.parse("http://www.example.com")
提供了要查看的数据,系统会遍历 Manifest 文件中各 Activity 的<intent-filter>
标签,寻找能响应此动作和数据的 Activity 进行启动。
2.1.2 启动过程的基本流程
Activity 的启动过程涉及多个系统组件间的协作。当一个 Activity 发起启动另一个 Activity 的请求时,首先会创建一个 Intent 对象来封装启动信息。然后,该 Intent 被传递给 ActivityManagerService(AMS),AMS 是 Android 系统中负责管理 Activity 生命周期和任务栈的核心服务。AMS 接收到 Intent 后,会根据 Intent 的内容以及系统中已有的任务栈和 Activity 状态,决定如何启动目标 Activity。如果目标 Activity 尚未在系统中创建,AMS 会通知应用进程创建该 Activity 的实例,接着执行一系列初始化操作,包括加载布局、绑定数据等,最终将新创建的 Activity 显示在屏幕上。
2.2 任务栈概念解析
2.2.1 任务栈的定义与作用
任务栈(Task Stack)是 Android 系统用于管理 Activity 实例的一种数据结构,它按照后进先出(LIFO)的顺序存储 Activity。每个任务栈都有一个唯一的标识符,并且可以包含一个或多个 Activity。任务栈的主要作用是维护用户操作 Activity 的历史记录,确保用户在不同 Activity 之间切换时,能够按照合理的顺序返回。例如,用户依次启动了 ActivityA、ActivityB 和 ActivityC,此时任务栈中从栈底到栈顶的顺序为 ActivityA、ActivityB、ActivityC。当用户按下返回键时,ActivityC 会从栈顶弹出,ActivityB 成为栈顶 Activity,显示在屏幕上,这样用户就能回到之前的操作界面。
2.2.2 任务栈的特性
- 独立性:每个任务栈都是相对独立的,不同任务栈中的 Activity 之间相互隔离,除非通过特定的 Intent 标志或跨任务栈的启动方式,否则一个任务栈中的 Activity 无法直接访问另一个任务栈中的 Activity。
- 栈内复用 :默认情况下,当启动一个已经存在于任务栈中的 Activity 时,如果该 Activity 的启动模式不是
singleInstance
,系统会直接复用栈中已有的实例,将其置于栈顶,并调用其onNewIntent
方法传递新的 Intent。例如,在任务栈中有 ActivityA、ActivityB,再次启动 ActivityB 时,ActivityB 会被移动到栈顶,而不会创建新的 ActivityB 实例。 - 任务栈的启动模式 :任务栈也有不同的启动模式,如
standard
(标准模式)、singleTop
(栈顶复用模式)、singleTask
(栈内复用模式)和singleInstance
(单实例模式)。这些模式决定了 Activity 在任务栈中的创建、复用和任务栈的管理方式。例如,singleTask
模式下,当启动该模式的 Activity 时,如果任务栈中已经存在该 Activity 的实例,系统会将该实例之上的所有 Activity 弹出栈,使该实例成为栈顶 Activity。
三、Activity 启动流程的源码级解析
3.1 从 startActivity 到 AMS 的调用过程
3.1.1 startActivity 方法的源码剖析
当在一个 Activity 中调用startActivity
方法时,实际调用的是 Activity 类中的startActivityForResult
方法(startActivity
方法内部最终也是调用startActivityForResult
,只不过请求码为 - 1,表示不需要返回结果)。以下是startActivityForResult
方法的部分关键源码:
java
java
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
// 如果请求码小于0,抛出异常,因为请求码必须为非负数
if (requestCode < 0) {
throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
}
// 获取Instrumentation对象,Instrumentation负责监控应用与系统的交互
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
// 如果Instrumentation返回的结果不为空,且结果码为RESULT_CANCELED,
// 则调用onActivityResult方法并传入相应参数
if (ar!= null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
// 如果当前Activity处于暂停状态,重新启动Activity的状态管理流程
if (options!= null &&!ActivityOptionsCompat.fromBundle(options).isLaunchTask()) {
mStartedActivity = true;
}
}
在这段代码中,首先检查请求码是否合法,然后通过mInstrumentation.execStartActivity
方法开始启动 Activity 的核心流程。mInstrumentation
是 Activity 的一个重要成员变量,它在 Activity 与系统之间起着桥梁作用,负责执行一些与 Activity 生命周期相关的操作。
3.1.2 Instrumentation 的 execStartActivity 方法
Instrumentation
的execStartActivity
方法进一步将启动请求传递给系统服务。以下是该方法的关键源码:
java
java
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
Uri referrer = target!= null? target.onProvideReferrer() : null;
// 如果有referrer,更新Intent中的referrer信息
if (referrer!= null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
// 如果设置了ActivityOptions,且不是启动任务的情况,获取启动动画相关参数
if (options!= null) {
try {
ActivityOptions.abort(options);
intent.putExtra(ActivityManager.KEY_ACTIVITY_OPTIONS, options);
} catch (ParcelableException e) {
throw new RuntimeException(e);
}
}
try {
// 通过ActivityManagerNative.getDefault()获取ActivityManagerService的代理对象,
// 并调用其startActivity方法,将启动请求传递给AMS
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target!= null? target.mEmbeddedID : null,
requestCode, 0, null, referrer);
// 检查启动结果,如果启动失败,抛出异常
checkStartActivityResult(result, intent);
return null;
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}
这里通过ActivityManagerNative.getDefault()
获取到 ActivityManagerService 的代理对象,然后调用其startActivity
方法,将启动请求从应用进程传递到系统服务进程(通常是 system_server 进程)中的 ActivityManagerService,后续的启动流程将在 AMS 中继续进行。
3.2 ActivityManagerService 中的启动流程
3.2.1 AMS 的 startActivity 方法
ActivityManagerService 的startActivity
方法是启动流程在系统服务端的入口。以下是该方法的简化源码:
java
java
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
resultWho, requestCode, startFlags, profilerInfo, bOptions,
UserHandle.getCallingUserId());
}
该方法主要是对参数进行整理,然后调用startActivityAsUser
方法,将启动请求进一步处理。UserHandle.getCallingUserId()
用于获取发起启动请求的用户 ID,因为 Android 系统支持多用户,不同用户的应用和 Activity 有不同的管理策略。
3.2.2 startActivityAsUser 方法的执行逻辑
startActivityAsUser
方法是 AMS 处理 Activity 启动的核心方法之一,其执行逻辑较为复杂,涉及到任务栈查找、Activity 实例创建等多个步骤。以下是其关键源码和注释:
java
java
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
enforceNotIsolatedCaller("startActivity");
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
false, ALLOW_FULL_ONLY, "startActivity", null);
// 检查调用者是否有权限启动Activity
if (callingPackage == null || callingPackage.length() == 0) {
callingPackage = getCallingPackage(caller);
}
// 检查Intent是否合法
if (intent == null) {
throw new IllegalArgumentException("Intent must not be null");
}
// 获取当前用户的ActivityStackSupervisor,ActivityStackSupervisor负责管理Activity栈
ActivityStackSupervisor mStackSupervisor = mActivityStackSupervisor;
// 开始启动Activity的核心流程,返回启动结果
return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, 0, 0, null, null, startFlags, false, profilerInfo,
null, null, bOptions, userId, null, null);
}
在这段代码中,首先进行一系列的参数检查和权限验证,确保启动请求的合法性。然后获取当前用户的ActivityStackSupervisor
,并调用其startActivityMayWait
方法,将启动流程委托给ActivityStackSupervisor
继续处理。ActivityStackSupervisor
在 Activity 启动和任务栈管理中起着至关重要的作用,它负责协调不同任务栈和 Activity 之间的关系。
3.3 ActivityStackSupervisor 的处理流程
3.3.1 startActivityMayWait 方法详解
ActivityStackSupervisor
的startActivityMayWait
方法是 Activity 启动流程中的关键环节,它负责查找合适的任务栈,并决定如何启动目标 Activity。以下是该方法的部分源码和详细注释:
java
java
final int startActivityMayWait(IApplicationThread caller, int callingUid,
String callingPackage, Intent intent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, WaitResult outResult, int configChanges,
boolean waitResult, boolean onlyIfNeeded, ProfilerInfo inProfilerInfo,
Bundle bOptions, int userId, IActivityContainer iContainer,
TaskRecord inTask) {
// 检查调用者是否有权限启动Activity
enforceCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS,
"startActivity");
// 检查是否允许跨用户启动Activity
if (callingUid >= UserHandle.USER_MIN && callingUid <= UserHandle.USER_MAX) {
if (callingUid!= userId) {
enforceCrossUserPermission(callingUid, userId, false, "startActivity");
}
}
// 获取当前用户的ActivityStack
ActivityStack stack = getFocusedStack();
if (stack == null) {
throw new RuntimeException("no stack");
}
// 启动Activity的具体逻辑,返回启动结果
return startActivityLocked(caller, intent, resolvedType, null, null, resultTo,
resultWho, requestCode, startFlags, profilerInfo, null, null, null,
configChanges, onlyIfNeeded, userId, inTask, iContainer, bOptions);
}
该方法首先进行权限检查,确保调用者有足够的权限启动 Activity。然后获取当前用户聚焦的 ActivityStack,ActivityStack 是任务栈在系统中的具体实现类,负责管理任务栈中 Activity 的状态和顺序。最后调用startActivityLocked
方法,进入启动 Activity 的具体处理逻辑。
3.3.2 startActivityLocked 方法的核心操作
startActivityLocked
方法是ActivityStackSupervisor
中真正执行 Activity 启动操作的方法,它包含了复杂的任务栈查找、Activity 实例创建和状态管理逻辑。以下是该方法的关键源码片段及注释:
java
java
private int startActivityLocked(IApplicationThread caller, Intent intent,
String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, Bundle options, TaskRecord inTask,
ActivityRecord[] outActivity, int configChanges, boolean onlyIfNeeded,
int userId, TaskRecord taskToAffiliate, IActivityContainer iContainer,
Bundle bOptions) {
// 获取当前用户的ActivityStack
ActivityStack stack = getFocusedStack();
// 获取Activity启动模式
int launchMode = aInfo!= null? aInfo.launchMode : -1;
// 根据Intent查找目标Activity的信息
ActivityInfo targetInfo = findTargetActivity(intent, resolvedType, aInfo, rInfo, userId);
if (targetInfo == null) {
// 如果未找到目标Activity,返回错误结果
return ActivityManager.START_TARGET_NOT_FOUND;
}
// 创建ActivityRecord对象,用于记录Activity的相关信息
ActivityRecord r = new ActivityRecord(stack.mService, callerApp, callingUid,
callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
resultRecord, resultWho, requestCode, componentSpecified, voiceSession,
voiceInteractor, profilerInfo, clientVisible, clientForeground);
// 根据启动模式和任务栈状态处理Activity的启动
if (launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
&& r.resultTo == null &&!isDocumentActivity(r)) {
ActivityRecord top = stack.topRunningNonDelayedActivityLocked(null);
if (top!= null && top.realActivity.equals(r.realActivity)
&& top.userId == r.userId) {
// 如果目标Activity已经在栈顶,调用其onNewIntent方法
top.deliverNewIntentLocked(callerApp, r.intent);
return ActivityManager.START_DELIVERED_TO_TOP;
}
}
// 处理其他启动模式,如singleTask等
if (launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
|| launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
TaskRecord task = findTaskLocked(r, launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE);
if (task!= null) {
if (task.moveToFrontIfNeeded()) {
// 如果任务栈需要移动到前台,将目标Activity置于栈顶
stack.moveActivityToFront(task.getTopActivity(), true);
return ActivityManager.START_TASK_TO_FRONT;
}
}
}
// 将新的ActivityRecord添加到合适的任务栈中
stack.startActivityLocked(r, newTask, doResume);
return ActivityManager.START_SUCCESS;
}
在这段代码中,首先获取当前用户的 ActivityStack 和目标 Activity 的启动模式。然后通过findTargetActivity
方法查找目标 Activity 的详细信息,并创建ActivityRecord
对象来记录 Activity 的相关信息,如调用者信息、Intent、启动模式等。接着根据不同的启动模式进行相应的处理,对于LAUNCH_SINGLE_TOP
模式,如果目标 Activity 已经在栈顶,则调用其onNewIntent
方法;对于LAUNCH_SINGLE_TASK
和LAUNCH_SINGLE_INSTANCE
模式,查找是否已有包含该 Activity 的任务栈,并进行相应的任务栈操作。最后,将新的 ActivityRecord 添加到合适的任务栈中,完成启动流程的核心操作。
3.4 Activity 的创建与初始化
3.4.1 ActivityRecord 与 Activity 的关联
ActivityRecord
是 Android 系统中用于记录 Activity 相关信息的重要类,它在 Activity 启动过程中起着关键的信息承载和状态管理作用。在ActivityStackSupervisor
的startActivityLocked
方法中创建ActivityRecord
对象后,该对象就成为了 Activity 在系统层面的一个抽象表示。
java
java
// 在ActivityStackSupervisor的startActivityLocked方法中创建ActivityRecord对象
ActivityRecord r = new ActivityRecord(stack.mService, callerApp, callingUid,
callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
resultRecord, resultWho, requestCode, componentSpecified, voiceSession,
voiceInteractor, profilerInfo, clientVisible, clientForeground);
这里的参数包含了 Activity 的调用者信息(callerApp
、callingUid
、callingPackage
)、启动意图(intent
)、Activity 的信息(aInfo
)等。ActivityRecord
对象记录了这些信息后,后续的 Activity 启动、状态管理等操作都可以基于这些信息进行。
3.4.2 Activity 的实例化过程
在确定了要启动的 Activity 以及相关的任务栈和状态信息后,就需要创建 Activity 的实例。这一过程通常在应用进程中完成。AMS 通过Process.start
方法启动一个新的应用进程(如果需要),并通过ActivityThread
类来管理 Activity 的生命周期。
java
java
// ActivityManagerService中启动应用进程的代码片段
Process.start("android.app.ActivityThread",
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, null, null, null, null, null, null, null,
new String[] {PROC_START_SEQ_IDENT + app.startSeq});
这里调用Process.start
方法启动一个新的进程,指定了要启动的类为android.app.ActivityThread
。ActivityThread
是应用进程的主线程类,它负责处理 Activity 的创建、生命周期管理等操作。
在ActivityThread
的main
方法中,会进行一系列的初始化操作,然后进入消息循环,等待系统发送的启动 Activity 的消息。
java
java
// ActivityThread的main方法
public static void main(String[] args) {
// 初始化Looper,为消息循环做准备
Looper.prepareMainLooper();
// 创建ActivityThread实例
ActivityThread thread = new ActivityThread();
// 调用attach方法,将ActivityThread与系统服务进行关联
thread.attach(false, startSeq);
// 获取Looper实例
Looper looper = Looper.myLooper();
if (looper!= null) {
// 进入消息循环,处理各种消息
looper.loop();
}
// 如果消息循环退出,抛出异常
throw new RuntimeException("Main thread loop unexpectedly exited");
}
当ActivityThread
接收到启动 Activity 的消息时,会调用handleLaunchActivity
方法来创建和启动 Activity。
java
java
// ActivityThread的handleLaunchActivity方法
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
// 调用performLaunchActivity方法创建和启动Activity
Activity a = performLaunchActivity(r, customIntent);
if (a!= null) {
// 调用handleResumeActivity方法,将Activity置于恢复状态
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished &&!r.startsNotResumed, r.lastProcessedSeq, reason);
}
}
3.4.3 performLaunchActivity 方法的实现
performLaunchActivity
方法是 Activity 实例化和初始化的核心方法,它会完成 Activity 的类加载、实例创建、布局加载等操作。
java
java
// ActivityThread的performLaunchActivity方法
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// 获取ActivityInfo,包含Activity的各种信息
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
// 加载应用的包信息
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
// 获取Activity的类名
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
r.activityInfo.applicationInfo.targetSdkVersion = r.activityInfo.targetSdkVersion;
}
// 类加载器加载Activity类
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
Activity activity = null;
try {
// 使用类加载器创建Activity实例
java.lang.Class<?> aClass = cl.loadClass(aInfo.name);
activity = mInstrumentation.newActivity(cl, aInfo.name, r.intent);
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
// 创建Application对象(如果还未创建)
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity!= null) {
// 创建Context对象
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
// 调用Activity的attach方法,将Activity与上下文、窗口等进行关联
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
// 调用Activity的onCreate方法
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
mInstrumentation.callActivityOnCreate(activity, r.state);
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
// 调用Activity的onStart方法
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
if (r.state != null) {
// 恢复Activity的状态
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
if (!r.activity.mFinished) {
activity.mCalled = false;
// 调用Activity的onPostCreate方法
mInstrumentation.callActivityOnPostCreate(activity, r.state);
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onPostCreate()");
}
}
}
r.paused = true;
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
return activity;
}
在performLaunchActivity
方法中,首先通过类加载器加载 Activity 类,并使用反射机制创建 Activity 实例。然后创建Application
对象和Context
对象,调用 Activity 的attach
方法将 Activity 与上下文、窗口等进行关联。接着依次调用 Activity 的onCreate
、onStart
、onRestoreInstanceState
、onPostCreate
等方法,完成 Activity 的初始化操作。
3.5 Activity 启动过程中的任务栈管理
3.5.1 任务栈的查找与创建
在 Activity 启动过程中,需要根据 Activity 的启动模式和 Intent 信息来查找或创建合适的任务栈。ActivityStackSupervisor
的findTaskLocked
方法用于查找包含指定 Activity 的任务栈。
java
java
// ActivityStackSupervisor的findTaskLocked方法
TaskRecord findTaskLocked(ActivityRecord r, boolean singleInstance) {
// 获取当前用户的所有任务栈
ArrayList<TaskRecord> tasks = mTaskHistory;
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
TaskRecord task = tasks.get(taskNdx);
if (task.numActivities > 0) {
ActivityRecord top = task.getTopActivity();
if (top != null && top.userId == r.userId) {
if (singleInstance) {
if (task.rootAffinity != null && task.rootAffinity.equals(r.taskAffinity)) {
return task;
}
} else {
if (task.rootAffinity != null && task.rootAffinity.equals(r.taskAffinity)) {
if (task.affinityIntent != null && task.affinityIntent.filterEquals(r.intent)) {
return task;
}
}
}
}
}
}
return null;
}
该方法遍历当前用户的所有任务栈,根据 Activity 的亲和性(taskAffinity
)和启动模式来判断是否存在包含该 Activity 的任务栈。如果找到合适的任务栈,则返回该任务栈;如果未找到,则可能需要创建新的任务栈。
当需要创建新的任务栈时,ActivityStack
的createTaskRecord
方法会被调用。
java
java
// ActivityStack的createTaskRecord方法
TaskRecord createTaskRecord(int taskId, ActivityInfo aInfo, Intent intent,
int launchFlags, boolean fromRecents, boolean isHomeTask) {
// 创建新的TaskRecord对象
TaskRecord task = new TaskRecord(taskId, this, aInfo, intent, launchFlags,
fromRecents, isHomeTask);
// 将新的任务栈添加到任务栈列表中
mTaskHistory.add(task);
return task;
}
这里创建了一个新的TaskRecord
对象,并将其添加到ActivityStack
的任务栈列表中。
3.5.2 Activity 在任务栈中的入栈操作
当确定了要启动的 Activity 和对应的任务栈后,需要将 Activity 添加到任务栈中。ActivityStack
的startActivityLocked
方法会完成 Activity 的入栈操作。
java
java
// ActivityStack的startActivityLocked方法
final void startActivityLocked(ActivityRecord r, boolean newTask,
boolean doResume, boolean keepCurTransition, Bundle options) {
TaskRecord task = r.task;
if (newTask) {
// 如果是新任务,将任务栈移动到前台
boolean isTop = moveTaskToFrontLocked(task, r, newTask, doResume,
keepCurTransition, options);
if (isTop) {
// 如果任务栈已经在前台,将Activity添加到任务栈顶部
task.addActivityToTop(r);
r.putInHistory();
}
} else {
// 如果不是新任务,将Activity添加到当前任务栈的合适位置
ActivityRecord last = task.getTopActivity();
task.addActivityToTop(r);
r.putInHistory();
if (last != null) {
// 如果当前任务栈有栈顶Activity,暂停该Activity
mService.setActivityPausedLocked(last, true);
}
}
if (doResume) {
// 如果需要恢复Activity,调用resumeTopActivityLocked方法
resumeTopActivityLocked(null, r, options);
}
}
在这个方法中,如果是新任务,会调用moveTaskToFrontLocked
方法将任务栈移动到前台,并将 Activity 添加到任务栈顶部;如果不是新任务,直接将 Activity 添加到当前任务栈的合适位置。同时,如果当前任务栈有栈顶 Activity,会暂停该 Activity。最后,如果需要恢复 Activity,会调用resumeTopActivityLocked
方法。
3.5.3 任务栈的排序与调整
为了保证用户操作的连贯性和合理性,任务栈需要进行排序和调整。ActivityStack
的moveTaskToFrontLocked
方法用于将指定的任务栈移动到前台。
java
java
// ActivityStack的moveTaskToFrontLocked方法
boolean moveTaskToFrontLocked(TaskRecord task, ActivityRecord newTop,
boolean newTask, boolean doResume, boolean keepCurTransition, Bundle options) {
if (task == null) {
return false;
}
// 获取当前聚焦的任务栈
TaskRecord focusedTask = getFocusedTask();
if (task == focusedTask) {
// 如果指定的任务栈已经是聚焦的任务栈,直接返回
return true;
}
// 将指定的任务栈从任务栈列表中移除
mTaskHistory.remove(task);
// 将指定的任务栈添加到任务栈列表的顶部
mTaskHistory.add(task);
// 调整任务栈中Activity的顺序
task.moveToFront(newTop, newTask);
// 如果需要恢复Activity,调用resumeTopActivityLocked方法
if (doResume) {
resumeTopActivityLocked(null, newTop, options);
}
return true;
}
该方法首先检查指定的任务栈是否已经是聚焦的任务栈,如果是则直接返回。然后将指定的任务栈从任务栈列表中移除,并添加到列表的顶部,实现任务栈的排序。接着调用任务栈的moveToFront
方法调整任务栈中 Activity 的顺序。最后,如果需要恢复 Activity,调用resumeTopActivityLocked
方法。
3.6 Activity 启动过程中的状态管理
3.6.1 Activity 状态的定义与转换
在 Android 系统中,Activity 有多种状态,如CREATED
、STARTED
、RESUMED
、PAUSED
、STOPPED
、DESTROYED
等。这些状态之间的转换是 Activity 启动过程中的重要环节。
ActivityRecord
类中定义了 Activity 的状态,以下是部分状态的定义:
java
java
// ActivityRecord类中的状态定义
static final int STATE_INITIALIZING = 0;
static final int STATE_CREATED = 1;
static final int STATE_STARTED = 2;
static final int STATE_RESUMED = 3;
static final int STATE_PAUSED = 4;
static final int STATE_STOPPED = 5;
static final int STATE_FINISHING = 6;
static final int STATE_DESTROYED = 7;
在 Activity 启动过程中,Activity 的状态会随着不同的生命周期方法调用而转换。例如,在performLaunchActivity
方法中,调用onCreate
方法后,Activity 的状态变为CREATED
;调用onStart
方法后,状态变为STARTED
;调用onResume
方法后,状态变为RESUMED
。
3.6.2 状态转换的源码实现
以resumeTopActivityLocked
方法为例,该方法负责将任务栈顶部的 Activity 恢复到RESUMED
状态。
java
java
// ActivityStack的resumeTopActivityLocked方法
final boolean resumeTopActivityLocked(ActivityRecord prev, ActivityRecord next,
Bundle options) {
if (next == null) {
// 如果没有要恢复的Activity,返回false
return false;
}
if (mPausingActivity != null) {
// 如果有正在暂停的Activity,等待其暂停完成
return false;
}
// 获取当前聚焦的Activity
ActivityRecord top = getTopActivity();
if (top != null && top != next) {
// 如果当前聚焦的Activity不是要恢复的Activity,暂停当前聚焦的Activity
pauseTopActivityLocked(top, next, false);
return false;
}
// 检查是否需要等待其他操作完成
if (mService.isSleepingOrShuttingDownLocked()
&&!next.isHomeActivity()) {
return false;
}
// 将Activity的状态设置为RESUMED
next.state = ActivityState.RESUMED;
// 调用Activity的onResume方法
mService.startActivityResume(next, options);
return true;
}
在这个方法中,首先检查是否有正在暂停的 Activity,如果有则等待其暂停完成。然后检查当前聚焦的 Activity 是否是要恢复的 Activity,如果不是则暂停当前聚焦的 Activity。接着将 Activity 的状态设置为RESUMED
,并调用startActivityResume
方法来调用 Activity 的onResume
方法,完成状态转换。
四、任务栈管理模块的深入分析
4.1 任务栈的存储结构与实现
4.1.1 TaskRecord 类的作用与结构
TaskRecord
类是任务栈在 Android 系统中的具体实现,它负责存储和管理任务栈中的 Activity 信息。TaskRecord
类包含了任务栈的基本信息,如任务 ID、亲和性、栈中的 Activity 列表等。
java
java
// TaskRecord类的部分成员变量
public final int taskId; // 任务ID
public final ActivityStack stack; // 所属的ActivityStack
public final Intent affinityIntent; // 亲和性Intent
public final ArrayList<ActivityRecord> mActivities = new ArrayList<>(); // 栈中的Activity列表
taskId
是任务栈的唯一标识符,用于区分不同的任务栈。stack
指向所属的ActivityStack
,ActivityStack
负责管理多个任务栈。affinityIntent
用于表示任务栈的亲和性,用于判断 Activity 是否应该属于该任务栈。mActivities
是一个ArrayList
,用于存储任务栈中的ActivityRecord
对象,按照入栈的顺序排列。
4.1.2 ActivityStack 对任务栈的管理
ActivityStack
是管理任务栈的核心类,它负责任务栈的创建、销毁、排序等操作。ActivityStack
类中包含了一个任务栈列表mTaskHistory
,用于存储所有的任务栈。
java
java
// ActivityStack类的部分成员变量
final ArrayList<TaskRecord> mTaskHistory = new ArrayList<>(); // 任务栈列表
ActivityStack
提供了一系列方法来管理任务栈,如createTaskRecord
方法用于创建新的任务栈,moveTaskToFrontLocked
方法用于将指定的任务栈移动到前台,removeTaskLocked
方法用于移除指定的任务栈等。
java
java
// ActivityStack的removeTaskLocked方法
boolean removeTaskLocked(TaskRecord task, boolean oomAdj, boolean inDestroyingProcess,
boolean removeFromRecents) {
if (task == null) {
return false;
}
// 从任务栈列表中移除指定的任务栈
mTaskHistory.remove(task);
// 处理任务栈中的Activity
for (int i = task.mActivities.size() - 1; i >= 0; i--) {
ActivityRecord r = task.mActivities.get(i);
if (r.finishing) {
// 如果Activity正在销毁,调用destroyActivityLocked方法销毁Activity
mService.destroyActivityLocked(r, false, false, "task removed");
} else {
// 否则,将Activity从任务栈中移除
task.removeActivity(r);
}
}
// 更新任务栈的排序和状态
if (oomAdj) {
mService.updateOomAdjLocked();
}
if (removeFromRecents) {
// 如果需要从最近任务列表中移除,调用相应方法
mService.removeTaskFromRecents(task);
}
return true;
}
在removeTaskLocked
方法中,首先从任务栈列表中移除指定的任务栈。然后遍历任务栈中的 Activity,对于正在销毁的 Activity,调用destroyActivityLocked
方法销毁 Activity;对于其他 Activity,将其从任务栈中移除。最后更新任务栈的排序和状态,并根据需要从最近任务列表中移除该任务栈。
4.2 任务栈的启动模式详解
4.2.1 standard 模式
standard
模式是 Activity 的默认启动模式。在这种模式下,每次启动 Activity 都会创建一个新的 Activity 实例,并将其添加到启动它的任务栈的顶部。
java
java
// 在Activity中启动一个standard模式的Activity
Intent intent = new Intent(this, StandardActivity.class);
startActivity(intent);
当调用上述代码启动StandardActivity
时,无论StandardActivity
是否已经存在于任务栈中,都会创建一个新的StandardActivity
实例,并将其添加到当前任务栈的顶部。
4.2.2 singleTop 模式
singleTop
模式下,如果要启动的 Activity 已经位于任务栈的顶部,不会创建新的 Activity 实例,而是调用该 Activity 的onNewIntent
方法传递新的 Intent。
java
java
// 在Activity中启动一个singleTop模式的Activity
Intent intent = new Intent(this, SingleTopActivity.class);
startActivity(intent);
在ActivityStackSupervisor
的startActivityLocked
方法中,对于singleTop
模式的 Activity,会进行如下判断:
java
java
if (launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
&& r.resultTo == null &&!isDocumentActivity(r)) {
ActivityRecord top = stack.topRunningNonDelayedActivityLocked(null);
if (top != null && top.realActivity.equals(r.realActivity)
&& top.userId == r.userId) {
// 如果目标Activity已经在栈顶,调用其onNewIntent方法
top.deliverNewIntentLocked(callerApp, r.intent);
return ActivityManager.START_DELIVERED_TO_TOP;
}
}
这里通过topRunningNonDelayedActivityLocked
方法获取任务栈顶部的 Activity,然后判断其是否与要启动的 Activity 相同。如果相同,则调用deliverNewIntentLocked
方法,该方法会调用 Activity 的onNewIntent
方法。
4.2.3 singleTask 模式
singleTask
模式下,如果要启动的 Activity 已经存在于某个任务栈中,系统会将该任务栈移动到前台,并将该 Activity 之上的所有 Activity 弹出栈,使该 Activity 成为栈顶 Activity。
java
java
// 在Activity中启动一个singleTask模式的Activity
Intent intent = new Intent(this, SingleTaskActivity.class);
startActivity(intent);
在ActivityStackSupervisor
的startActivityLocked
方法中,对于singleTask
模式的 Activity,会进行如下处理:
java
java
if (launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
|| launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
TaskRecord task = findTaskLocked(r, launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE);
if (task != null) {
if (task.moveToFrontIfNeeded()) {
// 如果任务栈需要移动到前台,将目标Activity置于栈顶
stack.moveActivityToFront(task.getTopActivity(), true);
return ActivityManager.START_TASK_TO_FRONT;
}
}
}
这里通过findTaskLocked
方法查找包含该 Activity 的任务栈,如果找到则调用moveToFrontIfNeeded
方法将任务栈移动到前台,并将目标 Activity 置于栈顶。
4.2.4 singleInstance 模式
singleInstance
模式下,系统会为该 Activity 创建一个新的任务栈,并且该任务栈中只能有这一个 Activity 实例。当再次启动该 Activity 时,会直接复用该实例。
java
java
// 在Activity中启动一个singleInstance模式的Activity
Intent intent = new Intent(this, SingleInstanceActivity.class);
startActivity(intent);
在ActivityStackSupervisor
的startActivityLocked
方法中,对于singleInstance
模式的 Activity,会进行如下处理:
java
java
if (launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
TaskRecord task = findTaskLocked(r, true);
if (task != null) {
if (task.moveToFrontIfNeeded()) {
// 如果任务栈需要移动到前台,将目标Activity置于栈顶
stack.moveActivityToFront(task.getTopActivity(), true);
return ActivityManager.START_TASK_TO_FRONT;
}
} else {
// 如果没有找到包含该Activity的任务栈,创建新的任务栈
task = stack.createTaskRecord(r.task.taskId, r.activityInfo, r.intent,
r.launchFlags, false, false);
task.addActivityToTop(r);
r.putInHistory();
stack.moveTaskToFrontLocked(task, r, true, false, false, null);
return ActivityManager.START_SUCCESS;
}
}
这里通过findTaskLocked
方法查找包含该 Activity 的任务栈,如果找到则将任务栈移动到前台;如果未找到,则创建新的任务栈,并将该 Activity 添加到新的任务栈中。
4.3 任务栈的切换与管理操作
4.3.1 任务栈的切换过程
当用户在不同的应用或 Activity 之间切换时,会涉及到任务栈的切换。ActivityStack
的moveTaskToFrontLocked
方法负责将指定的任务栈移动到前台。
java
java
// ActivityStack的moveTaskToFrontLocked方法
boolean moveTaskToFrontLocked(TaskRecord task, ActivityRecord newTop,
boolean newTask, boolean doResume, boolean keepCurTransition, Bundle options) {
if (task == null) {
return false;
}
// 获取当前聚焦的任务栈
TaskRecord focusedTask = getFocusedTask();
if (task == focusedTask) {
// 如果指定的任务栈已经是聚焦的任务栈,直接返回
return true;
}
// 将指定的任务栈从任务栈列表中移除
mTaskHistory.remove(task);
// 将指定的任务栈添加到任务栈列表的顶部
mTaskHistory.add(task);
// 调整任务栈中Activity的顺序
task.moveToFront(newTop, newTask);
// 如果需要恢复Activity,调用resumeTopActivityLocked方法
if (doResume) {
resumeTopActivityLocked(null, newTop, options);
}
return true;
}
在这个方法中,首先检查指定的任务栈是否已经是聚焦的任务栈,如果是则直接返回。然后将指定的任务栈从任务栈列表中移除,并添加到列表的顶部,实现任务栈的排序。接着调用任务栈的moveToFront
方法调整任务栈中 Activity 的顺序。最后,如果需要恢复 Activity,调用resumeTopActivityLocked
方法。
4.3.2 任务栈的销毁与回收
当一个任务栈中的所有 Activity 都被销毁后,该任务栈可以被销毁和回收。ActivityStack
的removeTaskLocked
方法用于移除指定的任务栈。
java
java
// ActivityStack的removeTaskLocked方法
boolean removeTaskLocked(TaskRecord task, boolean oomAdj, boolean inDestroyingProcess,
boolean removeFromRecents) {
if (task == null) {
return false;
}
// 从任务栈列表中移除指定的任务栈
mTaskHistory.remove(task);
// 处理任务栈中的Activity
for (int i = task.mActivities.size() - 1; i >= 0; i--) {
ActivityRecord r = task.mActivities.get(i);
if (r.finishing) {
// 如果Activity正在销毁,调用destroyActivityLocked方法销毁Activity
mService.destroyActivityLocked(r, false, false, "task removed");
} else {
// 否则,将Activity从任务栈中移除
task.removeActivity(r);
}
}
// 更新任务栈的排序和状态
if (oomAdj) {
mService.updateOomAdjLocked();
}
if (removeFromRecents) {
// 如果需要从最近任务列表中移除,调用相应方法
mService.removeTaskFromRecents(task);
}
return true;
}
在removeTaskLocked
方法中,首先从任务栈列表中移除指定的任务栈。然后遍历任务栈中的 Activity,对于正在销毁的 Activity,调用destroyActivityLocked
方法销毁 Activity;对于其他 Activity,将其从任务栈中移除。最后更新任务栈的排序和状态,并根据需要从最近任务列表中移除该任务栈。
4.4 任务栈与最近任务列表的关系
4.4.1 最近任务列表的实现原理
最近任务列表是 Android 系统提供的一个功能,用于显示用户最近使用过的任务。ActivityManagerService
负责管理最近任务列表,它通过RecentTasks
类来存储和管理最近任务信息。
java
java
// ActivityManagerService类中的RecentTasks成员变量
final RecentTasks mRecentTasks = new RecentTasks(this);
RecentTasks
类中包含了一个ArrayList
,用于存储最近任务的信息。
java
java
// RecentTasks类的部分成员变量
final ArrayList<RecentTaskInfo> mTasks = new ArrayList<>(); // 最近任务列表
当一个任务栈被移动到前台时,ActivityManagerService
会更新最近任务列表,将该任务栈的信息添加到列表的顶部。
java
java
// ActivityManagerService的updateRecentTasksLocked方法
void updateRecentTasksLocked(TaskRecord task, boolean isTop) {
if (task == null) {
return;
}
// 从最近任务列表中移除该任务栈的信息
mRecentTasks.removeTask(task.taskId);
if (isTop) {
// 如果该任务栈是当前聚焦的任务栈,将其信息添加到最近任务列表的顶部
mRecentTasks.addTask(task, true);
}
}
在updateRecentTasksLocked
方法中,首先从最近任务列表中移除该任务栈的信息,然后如果该任务栈是当前聚焦的任务栈,将其信息添加到最近任务列表的顶部。
4.4.2 任务栈在最近任务列表中的显示与管理
当用户打开最近任务列表时,系统会根据RecentTasks
类中的信息显示最近使用过的任务。每个任务在最近任务列表中显示为一个卡片,用户可以通过滑动卡片来关闭任务。
当用户关闭一个任务时,ActivityManagerService
会调用removeTaskFromRecents
方法将该任务从最近任务列表中移除,并销毁该任务栈。
java
java
// ActivityManagerService的removeTaskFromRecents方法
void removeTaskFromRecents(TaskRecord task) {
if (task == null) {
return;
}
// 从最近任务列表中移除该任务栈的信息
mRecentTasks.removeTask(task.taskId);
// 销毁该任务栈
ActivityStack stack = task.getStack();
if (stack != null) {
stack.removeTaskLocked(task, true, false, false);
}
}
在removeTaskFromRecents
方法中,首先从最近任务列表中移除该任务栈的信息,然后调用ActivityStack
的removeTaskLocked
方法销毁该任务栈。
五、常见问题与解决方案
5.1 Activity 重复启动问题
5.1.1 问题分析
在某些情况下,可能会出现 Activity 重复启动的问题。例如,在快速点击按钮时,可能会多次触发startActivity
方法,导致同一个 Activity 被多次启动。这可能会导致任务栈中出现多个相同的 Activity 实例,影响用户体验。
5.1.2 解决方案
可以通过设置Intent
的标志位来避免 Activity 的重复启动。例如,使用FLAG_ACTIVITY_SINGLE_TOP
标志位可以确保如果要启动的 Activity 已经位于任务栈的顶部,不会创建新的实例。
java
java
// 在Activity中启动一个Activity并设置FLAG_ACTIVITY_SINGLE_TOP标志位
Intent intent = new Intent(this, TargetActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
另外,也可以在代码中添加点击防抖逻辑,避免快速点击多次触发startActivity
方法。
java
java
// 点击防抖逻辑示例
private long lastClickTime = 0;
private static final long CLICK_INTERVAL = 500; // 点击间隔时间,单位为毫秒
public void onButtonClick(View view) {
long currentTime = System.currentTimeMillis();
if (currentTime - lastClickTime > CLICK_INTERVAL) {
// 处理点击事件
Intent intent = new Intent(this, TargetActivity.class);
startActivity(intent);
lastClickTime = currentTime;
}
}
5.2 任务栈混乱问题
5.2.1 问题分析
任务栈混乱可能会导致 Activity 的显示顺序不符合预期,用户在返回操作时可能会出现异常。例如,在不同的启动模式和 Intent 标志位的组合下,可能会导致任务栈中 Activity 的排列顺序混乱。
5.2.2 解决方案
- 明确启动模式 :在 AndroidManifest.xml 文件中明确指定 Activity 的启动模式,避免使用默认的
standard
模式导致的问题。例如,对于需要保持单实例的 Activity,可以使用singleTask
或singleInstance
模式。
xml
java
<activity
android:name=".SingleTaskActivity"
android:launchMode="singleTask">
...
</activity>
- 合理使用 Intent 标志位 :在启动 Activity 时,合理使用 Intent 的标志位来控制任务栈的行为。例如,使用
FLAG_ACTIVITY_CLEAR_TOP
标志位可以清除任务栈中该 Activity 之上的所有 Activity。
java
java
Intent intent = new Intent(this, TargetActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
- 调试与日志记录 :在开发过程中,使用日志记录任务栈的状态和 Activity 的启动信息,以便及时发现和解决任务栈混乱的问题。可以使用
adb shell dumpsys activity activities
命令查看当前系统中所有任务栈和 Activity 的信息。
5.3 Activity 启动性能问题
5.3.1 问题分析
Activity 启动性能问题可能会导致应用启动缓慢,影响用户体验。常见的原因包括布局文件复杂、初始化操作耗时过长、内存泄漏等。
5.3.2 解决方案
- 优化布局文件 :避免在布局文件中使用过多嵌套的布局,尽量采用
ConstraintLayout
等扁平布局,减少视图层级。例如,将原本多层嵌套的LinearLayout
替换为ConstraintLayout
,可以有效减少布局测量和绘制的时间。
xml
java
<!-- 优化前:多层嵌套的LinearLayout -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Title" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/icon" />
</LinearLayout>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
<!-- 优化后:使用ConstraintLayout -->
<ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/icon_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/icon"
app:layout_constraintStart_toEndOf="@id/title_text"
app:layout_constraintTop_toTopOf="parent" />
<ListView
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/title_text"
app:layout_constraintBottom_toBottomOf="parent" />
</ConstraintLayout>
- 异步初始化操作 :将一些耗时的初始化操作放在异步线程中进行,避免阻塞主线程。例如,在 Activity 的
onCreate
方法中,如果需要从网络或数据库加载数据,可以使用AsyncTask
、Thread
或ExecutorService
等方式在后台线程中执行。
java
java
// 使用AsyncTask进行异步数据加载
private class LoadDataTask extends AsyncTask<Void, Void, List<String>> {
@Override
protected List<String> doInBackground(Void... voids) {
// 在后台线程中执行耗时操作,如从数据库或网络加载数据
return loadDataFromDatabase();
}
@Override
protected void onPostExecute(List<String> data) {
// 在主线程中更新UI
updateUI(data);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 启动异步任务
new LoadDataTask().execute();
}
private List<String> loadDataFromDatabase() {
// 模拟从数据库加载数据
try {
Thread.sleep(2000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
return new ArrayList<>();
}
private void updateUI(List<String> data) {
// 更新UI
ListView listView = findViewById(R.id.list_view);
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, data);
listView.setAdapter(adapter);
}
- 避免内存泄漏 :确保在 Activity 销毁时,及时释放资源,避免内存泄漏。例如,在 Activity 中注册的广播接收器、定时器等,需要在
onDestroy
方法中进行注销。
java
java
private BroadcastReceiver mReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 注册广播接收器
mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 处理广播事件
}
};
IntentFilter filter = new IntentFilter();
filter.addAction("com.example.MY_ACTION");
registerReceiver(mReceiver, filter);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 注销广播接收器
unregisterReceiver(mReceiver);
}
5.4 Activity 状态丢失问题
5.4.1 问题分析
在某些情况下,如屏幕旋转、系统内存不足等,Activity 可能会被销毁并重新创建,导致 Activity 的状态丢失。例如,用户在输入框中输入的内容、滚动位置等信息可能会丢失。
5.4.2 解决方案
- 使用
onSaveInstanceState
和onRestoreInstanceState
方法 :在 Activity 被销毁之前,系统会调用onSaveInstanceState
方法,开发者可以在该方法中保存 Activity 的状态信息。当 Activity 重新创建时,系统会调用onRestoreInstanceState
方法,开发者可以在该方法中恢复之前保存的状态信息。
java
java
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// 保存输入框中的文本内容
EditText editText = findViewById(R.id.edit_text);
String text = editText.getText().toString();
outState.putString("input_text", text);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// 恢复输入框中的文本内容
if (savedInstanceState != null) {
String text = savedInstanceState.getString("input_text");
EditText editText = findViewById(R.id.edit_text);
editText.setText(text);
}
}
- 使用 ViewModel :
ViewModel
是 Android 架构组件中的一个类,它可以在 Activity 或 Fragment 的生命周期变化时保持数据的一致性。通过使用ViewModel
,可以将一些与 UI 相关的数据存储在ViewModel
中,而不是在 Activity 中,这样即使 Activity 被销毁并重新创建,数据也不会丢失。
java
java
// 创建ViewModel类
public class MyViewModel extends ViewModel {
private MutableLiveData<String> inputText = new MutableLiveData<>();
public MutableLiveData<String> getInputText() {
return inputText;
}
public void setInputText(String text) {
inputText.setValue(text);
}
}
// 在Activity中使用ViewModel
public class MainActivity extends AppCompatActivity {
private MyViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获取ViewModel实例
viewModel = new ViewModelProvider(this).get(MyViewModel.class);
// 观察ViewModel中的数据变化
viewModel.getInputText().observe(this, new Observer<String>() {
@Override
public void onChanged(String text) {
// 更新UI
EditText editText = findViewById(R.id.edit_text);
editText.setText(text);
}
});
// 监听输入框的文本变化
EditText editText = findViewById(R.id.edit_text);
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
// 更新ViewModel中的数据
viewModel.setInputText(s.toString());
}
});
}
}
六、总结与展望
6.1 总结
通过对 Android Activity 启动与任务栈管理模块的深入分析,我们全面了解了该模块的核心原理和实现细节。Activity 启动过程涉及应用进程与系统服务进程(如 ActivityManagerService)之间的复杂交互,从startActivity
方法的调用开始,经过 Intent 的传递、任务栈的查找与创建、Activity 的实例化和初始化等多个步骤,最终将 Activity 显示在屏幕上。
任务栈管理模块则负责维护 Activity 的历史记录和显示顺序,通过不同的启动模式(如standard
、singleTop
、singleTask
、singleInstance
)和 Intent 标志位,开发者可以灵活控制 Activity 在任务栈中的创建、复用和销毁。同时,任务栈还与最近任务列表密切相关,系统会根据任务栈的状态更新最近任务列表的显示。
在开发过程中,我们也遇到了一些常见问题,如 Activity 重复启动、任务栈混乱、启动性能问题和状态丢失问题等。针对这些问题,我们可以通过合理设置启动模式和 Intent 标志位、优化布局文件、异步执行初始化操作、避免内存泄漏以及使用onSaveInstanceState
和ViewModel
等方法来解决。
6.2 展望
随着 Android 系统的不断发展和更新,Activity 启动与任务栈管理模块也可能会迎来一些改进和优化。例如,在性能方面,未来可能会进一步优化 Activity 的启动速度,减少系统资源的消耗,提高用户体验。在多窗口和分屏模式下,任务栈的管理可能会更加复杂,需要更加灵活和智能的算法来处理。
另外,随着 Android Jetpack 组件的不断完善,开发者可以更加方便地使用ViewModel
、LiveData
等组件来管理 Activity 的状态和数据,减少状态丢失的问题。同时,可能会有更多的工具和框架出现,帮助开发者更好地调试和优化 Activity 启动与任务栈管理的性能。
在未来的开发中,开发者需要不断关注 Android 系统的新特性和变化,合理运用各种技术手段,优化 Activity 启动与任务栈管理模块,为用户提供更加流畅、稳定的应用体验。同时,深入理解该模块的原理和实现细节,也有助于开发者解决开发过程中遇到的各种复杂问题,提升自己的技术水平。