Android四大组件——Activity
Posted <天各一方>
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android四大组件——Activity相关的知识,希望对你有一定的参考价值。
文章目录
Activity
1.什么是Activity
Activity是android四大组件之一,是负责展示界面、与用户交互的一种组件,Activity是四大组件中唯一用户可以感知的组件。
2.进程优先级
Android平台的App,通常都是单进程的,根据App栈顶的活动状态,可将App分为以下五大进程模式:
- 前台进程:当前进程Activity正在与用户交互;当前Service正在与Activity进行交互,或者调用了
startForground()
或者此Service正在执行生命周期。 - 可见进程:此Activity失去焦点,但是处于可见状态;此Service与一个可见的Activity进行绑定。
- 服用进程:当前开启
startService()
启动一个Service服务就可以认为此进程是一个服务进程。 - 后台进程:Activity的onStop()被调用,但是onDestroy()没有调用的状态。该进程属于后台进程。
- 空进程:该进程没有任何数据,但是保留了内存空间,并没有被系统kill掉。
进程的优先级就是根据这五大进程模式逐一递减。当系统内存资源不足时,会根据此优先级进行回收操作。回收针对的是App,而不是App中的某个组件。
3.Activity的启动模式
在了解启动模式之前,我们有必要对Activity的两个概念拎清楚,稍后对启动模式的分析也会基于这两个概念:任务、返回栈。
这两个概念我在刚接触Android开发时接触过,当时看郭神那本第一行代码在讲解启动模式时提到过返回栈的概念,书中只是粗浅的介绍了一个Activity的启动(standard),伴随在入栈操作,这个栈就是返回栈。现在看来,介绍的不是很完整,当然对于当初学Android的我来说,介绍的很浅显易懂了。接下来就谈一谈对其的补充。
当我们按下手机中查看最近运行应用时,其实看到的是一个一个的任务(Task),任务是用户在执行某项工作时与之互动的一系列 Activity 的集合。这些 Activity 按照每个 Activity 打开的顺序排列在一个返回堆栈中(Google官方介绍)。每个任务都对应这一个返回堆栈,而任务是可以叠加的,这个叠加只针对于前台App,当App进入后台时,或者当按下查看最近任务列表键,前台App中Task的叠加就会被拆开。(稍后介绍如何叠加)。
Activity总共有四大启动模式:
- 标准模式(standard)
- 栈顶复用(singleTop)
- 栈内复用(singleTask)
- 单例模式(singleInstance)
前两种模式是最好理解的,网上许多博客也介绍的相当清楚:
标准模式
是Activity默认的启动模式,每当启动一个Activity时,就会在返回栈栈顶创建一个Activity的实例,不论这个Activity在返回栈中是否有实例。我们在开发中大多都用这种模式。
栈顶复用
如果这个Activity已经存在于返回栈的栈顶,那么当重新打开这个Activity时,并不会重新创建它的实例,而是去回调onNewIntent方法,接着执行Activity的onRestart->onStart->onResume。
这两种启动模式都不涉及两个App之间调用对方Activity的情况。即使打开了另一个App的Activity,也还是在当前App的Task里,返回栈还是当前Task的返回栈。如果多个App同时打开了一个App中的Activity,他们是不会相互影响的。
接下来介绍的两种启动模式,会更加有针对性的去考虑在一个App里打开另一个App中的Activity的情况。
栈内复用
在郭神的第一行代码中,是这样介绍这种启动模式的:每次启动Activity时,都会在返回栈内检查是否有该Activity的实例,如果发现有则直接使用该实例,并把在这个Activity之上的所有Activity统统出栈。如果没有,就会新创建一个Activity。
在我们的测试中发现情况确实如此,但是这个描述是不太准确的。我们看singleTask这个名字,一定是与Task有一定关系的。我们考虑一种情况。在App-B中有一个ActivityB,它的启动模式是singleTask,那么,我们在App-A中启动这个ActivityB,这个ActivityB在哪里创建?在App-A上面吗?我们在App-A上面确实看到了这个ActivityB。但是,如果我们按下回退键,如果App-B之前被你启动过,里面还有其他Activity,我们会看到回退到了App-B的Activity上。那…这个ActivityB是在App-B上?事实确实如此,当我们从App-A启动App-B的ActivityB时(前提是singleTask模式),ActivityB会在App-B的任务中入栈,然后将App-B的任务压在App-A的上面,这样,给我们造成的体验就是App-A上面打开了App-B的ActivityB。在这种情况下,一旦我们按home键返回桌面或者查看最近任务列表,两个压在一起的Task就会被拆开,这时再回到App-A就不会看到ActivityB了。(当作者使用微信向QQ转发文件时,发现QQ选择联系人页面使用的就是这个启动逻辑,有兴趣的小伙伴可以试一试)。
那么问题来了,为什么App-B的ActivityB会在App-B上启动,我们明明是在App-A里启动Activity的呀?这是因为每个Activity都会有一个taskAffinity属性,这个属性可以在AndroidManifest.xml中设置(设置的字符串中必须加.
,否则会报错),这个属性时Task相似、相近的意思,taskAffinity属性相同的Activity会归属同一个Task栈。(如过没设置这个属性,Activity的taskAffinity默认取自Application的taskAffinity,而Application的taskAffinity默认取自包名,而Task的taskAffinity的值取自Task栈底Activity的taskAffinity)。如果启动模式是standard或singleTop,会忽略taskAffinity属性,而singleTask或singleInstance则会检查这个属性,把启动的Activity归属于属于它的Task中。这样,我们遇到的问题就迎刃而解了。
同时这个启动模式有ClearTop属性,它会清空此Activity上的所有Activity,以便此Activity的复用。
单例模式
单例模式和栈内复用的工作机制是相同的,但是它比栈内复用做的更绝,它不仅要求该Task中仅有此Activity的一个实例,还要此Activity独占一个Task。那么这种启动模式就具有很强的跨App交互意图。
总结:在同一个App中,往往使用前两种启动模式,当你得App想要对外提供服务,可以考虑单例模式,而栈内复用是兼容派,既可以在一个App中使用,也可以跨App启动。总之,根据具体场景具体分析。
启动模式的设置方式
<activity
android:name=".MainActivity"
android:launchMode="standard" />
4.Activity生命周期
针对Activity的生命周期,我们可以分两类进行讨论:一种是正常情况下的生命周期,一种是异常情况下的生命周期。
正常情况下的生命周期:
分析生命周期,先要了解Activity的生命周期函数:
- onCreate:表示Activity正在被创建,这时可以进行一些初始化工作,如我们的界面布局的加载,控件的绑定。
onCreate
方法不是一个常驻方法,完成工作后就会进入onStart
方法。 - onRestart:Activity由onStop重新变为可见状态时调用。
- onStart:在Activity由不可见变为即将可见时调用。
- onResume:在Activity准备好与用户进行交互时调用。此时的Activity一定位于返回栈的栈顶,并且处于运行状态。
- onPause:当系统准备去启动或者恢复另一个Activity时调用,在这个方法中将一些消耗CPU的资源都释放掉,以及保存一些关键的数据,这个方法执行的速度一定要快,不然会影响栈顶活动的使用。
- onStop:在Activity完全不可见时调用。
- onDestroy:在Activity被销毁前调用,之后活动的状态变为销毁状态。
情景分析:
Activity A 是 singleTask 模式,Activity B 启动模式为常规模式时 , 多种不同情况下执行方法:
-
Activity A 跳转到 Activity B:
A.onCreat() -> A.onStart() -> A.onResume() -> A.onPause() -> B.onCreat() -> B.onStart() -> B.onResume() -> A.onStop()
-
Activity B 跳转到 Activity A:
B.onPause() -> A.onRestart() -> A.onStart() -> A.onResume() -> B.onStop()
-
Activity B 点 击 back 键 :
B.onPause() -> A.onRestart() -> A.onStart() -> A.onResume() ->B.onStop() -> B.onDestroy()
-
Activity B 按 home 键,然后打开应用:
B.onPause() -> B.onStop() -> B.onRestart() ->B.onStart() -> B.onResume()
-
Activity A 启动一个完全透明的 Activity B 时会执行:
A.onCreat() -> A.onStart() -> A.onResume()-> A.onPause() -> B.onCreat() -> B.onStart() -> B.onResume()
Activity A 是常规模式,Activity B 启动模式为常规模式时,多种不同情况下执行方法:
-
Activity A 跳转到 Activity B:
A.onCreat() -> A.onStart() -> A.onResume() -> A.onPause() ->B.onCreat() -> B.onStart() -> B.onResume() -> A.onStop()
-
Activity B 跳转到 Activity A:
B.onPause() -> A.onCreat() -> A.onStart() -> A.onResume() -> B.onStop()
-
Activity B 点 击 back 键 :
B.onPause() -> A.onRestart() -> A.onStart() -> A.onResume() -> B.onStop() -> B.onDestroy()
-
Activity B 按 home 键,然后打开应用:
B.onPause() -> B.onStop() -> B.onRestart() -> B.onStart() -> B.onResume()
异常情况下的生命周期
为什么会有异常情况?
当系统资源回收或者配置发生改变时,就会导致Activity重建。系统资源回收通常发生在内存不足,配置发生变化通常为屏幕方向发生变化、系统语言更改等情况。
为什么要重建?
对于资源回收的情况,保存状态到使用时再恢复,远比后台保留进程所占的资源小的多。
对于配置情况的改变,只有重建,才能加载不同的布局。
重建机制:onSaveInstanceState & onRestoreInstanceState
重建机制就是当Activity因为异常情况需要重建时,通过onSaveInstanceState方法保留活动当前的状态,如:EditText文本内容、CheckBox选项框状态等,当重建Activity时,通过onRestoreInstanceState方法恢复其状态。
onSaveInstanceState方法在onStop方法之前被调用,但是和onPause方法没有时序关系。
onRestoreInstanceState方法执行在onStart之后。
重建流程
Activity 在 onSaveInstanceState 时,会 自动收集 View Hierachy 中每一个 “实现了状态保存和恢复方法” 的 View 的状态,这些状态数据会在 onRestoreInstanceState 时回传给每一个 View。并且回传时是依据 View 的 id 来逐一匹配的。
状态保存和恢复的注意事项
-
为了成功保存状态,要求在view内部实现保存和恢复方法
原生的 View 都有做到;如果是自定义 View,务必记住这一点;如果第三方 View 没做到,可以通过继承其来实现保存和恢复方法。
-
为了成功的恢复状态,需要给View赋予对应的ID
-
如果需要保存Activity的成员变量,需要手动重写onSaveInstanceState 和 onRestoreInstanceState 方法。
重写仅仅是为了额外地保存成员变量。重写方法时,记得要保留基类(super)的实现,Activity 正是在基类中完成 View 状态的保存和恢复。
5.Activity启动流程
分析Activity的启动流程,我们先看如何启动一个Activity
Intent intent = new Intent(ActivityA.this,ActivityB.class);
startActivity(intent);
那我们就进入startActivity这个方法一探究竟。
通过对源码的阅读,我发现通过方法调用链最终调用startActivityForResult这个方法:
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
//重点
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
核心逻辑就是调用Instrumentation.execStartActivity:
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
......
try {
intent.migrateExtraStreamToClipData(who);
intent.prepareToLeaveProcess(who);
//重点
int result = ActivityTaskManager.getService().startActivity(whoThread,
who.getBasePackageName(), who.getAttributionTag(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()), token,
target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
看这个方法名,猜测是得到ActivityTaskManagerService,然后调用其的startActivity方法:
注:网上大部分博客讲解Activity启动流程中这里介绍为AMS,这是Android10之前的写法,从Android10,将Activity启动服务的逻辑转移到ActivityTaskManagerService中,没有本质差别。
public final int startActivity(IApplicationThread caller, String callingPackage,
String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
Bundle bOptions) {
return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
UserHandle.getCallingUserId());
}
startActivityAsUser:
private int startActivityAsUser(IApplicationThread caller, String callingPackage,
@Nullable String callingFeatureId, Intent intent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
assertPackageMatchesCallingUid(callingPackage);
enforceNotIsolatedCaller("startActivityAsUser");
userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
// TODO: Switch to user app stacks here.
return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
.setCaller(caller)
.setCallingPackage(callingPackage)
.setCallingFeatureId(callingFeatureId)
.setResolvedType(resolvedType)
.setResultTo(resultTo)
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setProfilerInfo(profilerInfo)
.setActivityOptions(bOptions)
.setUserId(userId)
.execute();
}
这里通过工厂模式创建ActivityStarter实例。
ActivityStarter obtainStarter(Intent intent, String reason) {
return mFactory.obtain().setIntent(intent).setReason(reason);
}
通过obtain进行内存复用,避免频繁发创建ActivityStarter对象,导致内存不足触发GC造成的内存抖动。
ActivityStarter.execute:
int execute() {
try {
if (mRequest.intent != null && mRequest.intent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
final LaunchingState launchingState;
synchronized (mService.mGlobalLock) {
final ActivityRecord caller = ActivityRecord.forTokenLocked(mRequest.resultTo);
launchingState = mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(
mRequest.intent, caller);
}
if (mRequest.activityInfo == null) {
mRequest.resolveActivity(mSupervisor);
}
int res;
synchronized (mService.mGlobalLock) {
final boolean globalConfigWillChange = mRequest.globalConfig != null
&& mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0;
final ActivityStack stack = mRootWindowContainer.getTopDisplayFocusedStack();
if (stack != null) {
stack.mConfigWillChange = globalConfigWillChange;
}
if (DEBUG_CONFIGURATION) {
Slog.v(TAG_CONFIGURATION, "Starting activity when config will change = "
+ globalConfigWillChange);
}
final long origId = Binder.clearCallingIdentity();
res = resolveToHeavyWeightSwitcherIfNeeded();
if (res != START_SUCCESS) {
return res;
}
//开始请求
res = executeRequest(mRequest);
Binder.restoreCallingIdentity(origId);
if (globalConfigWillChange) {
mService.mAmInternal.enforceCallingPermission(
android.Manifest.permission.CHANGE_CONFIGURATION,
"updateConfiguration()");
if (stack != null) {
stack.mConfigWillChange = false;
}
if (DEBUG_CONFIGURATION) {
Slog.v(TAG_CONFIGURATION,
"Updating to new configuration after starting activity.");
}
mService.updateConfigurationLocked(mRequest.globalConfig, null, false);
}
mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res,
mLastStartActivityRecord);
return getExternalResult(mRequest.waitResult == null ? res
: waitForResult(res, mLastStartActivityRecord));
}
} finally {
onExecutionComplete();
}
}
executeRequest:
private int executeRequest(Request request) {
......
//每个Activity都对应一个ActivityRecord,存放于ActivityStack中
final ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
callingPackage, callingFeatureId, intent, resolvedType, aInfo,
mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode,
request.componentSpecified, voiceSession != null, mSupervisor, checkedOptions,
sourceRecord);
mLastStartActivityRecord = r;
if (r.appTimeTracker == null && sourceRecord != null) {
r.appTimeTracker = sourceRecord.appTimeTracker;
}
final ActivityStack stack = mRootWindowContainer.getTopDisplayFocusedStack();
if (voiceSession == null && stack != null && (stack.getResumedActivity() == null
|| stack.getResumedActivity().info.applicationInfo.uid != realCallingUid)) {
if (!mService.checkAppSwitchAllowedLocked(以上是关于Android四大组件——Activity的主要内容,如果未能解决你的问题,请参考以下文章