Activity
概览
1. Activity
Activity是整个Android app直接与用户进行交互的核心组件,是用户交互的第一接口,提供了一个用户完成指令的窗口,当开发者创建Activity之后,通过调用setContentView(View)方法来给Activity指定一个显示界面,并以此为基础给用户一个交互接口。系统采用Activity栈的方式来管理Activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
Activity的形态
Activity的最大特点是用有多种形态,他可以在此种切换,由此来控制自己的声明周期。
- Activity/Running
Activity位于Activity栈的最顶层,可见并与用户进行交互,并且获取了焦点 - Pause
当Activity失去焦点,被一个全新的非全屏的Activity或者一个透明的Activity或者一个透明的Activity放置在栈顶时,Activity就转化为Paused状态,但只是失去了与用户交互的能力,所有的状态信息、成员变量都还保持着,只有在系统内存极低的情况下才会被回收。此时Activity仍然是可见的。 - Stopped
与paused状态相似,stopped状态的Activity是完全不可见的,但是内存里面的成员变量,状态信息仍然存在,但是也没有被销毁 - Killed
当Activity被系统回收掉或者Activity从来没有被创建过,Acticity就处于Killed形态,由此可见,用户的不同动作,会让Activity四种形态来回切换,Activity可被控制生,但是无法被控制合适dead
生命周期
在Activity中只有三个状态是稳定的,而其他状态都是过渡状态,很快就会结束。
正常情况下的生命周期:
Activity启动-》onCreate-》onStart()-》onResume()
此时点击home键回桌面-》onPause()-》onStop()
再次回到原Activity-》onRestart()-》onStart()-》onResume()
退出当前的Activity-》onPause-》onStop-》onDestory()
接下来是生命周期的详解:
- 启动一个Activity,通常是Intent来完成。启动一个Activity首先要执行的回调函数时onCreate()通常是在代码中需要在此函数中绑定布局,绑定控件,初始化数据等一些列操作。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
final AppCompatDelegate delegate = getDelegate();
delegate.installViewFactory();
delegate.onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
}
- 接下来是执行Activity的onStart函数,执行之后Activity已经可见,但是还没有出现在前台,无法与用户进行交互。这个时候Activity已经在后台准备好了,就差执行onResume函数回到前台。
@Override
protected void onStart() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this);
mCalled = true;
mFragments.doLoaderStart();
dispatchActivityStarted();
if (mAutoFillResetNeeded) {
getAutofillManager().onVisibleForAutofill();
}
}
- 执行Activity的onResume函数,执行之后Activity可见,且还会出现在前台,可以与用户进行交互
@Override
protected void onResume() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
dispatchActivityResumed();
mActivityTransitionState.onResume(this);
enableAutofillCompatibilityIfNeeded();
if (mAutoFillResetNeeded) {
if (!mAutoFillIgnoreFirstResumePause) {
View focus = getCurrentFocus();
if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
// TODO: in Activity killed/recreated case, i.e. SessionLifecycleTest#
// testDatasetVisibleWhileAutofilledAppIsLifecycled: the View's initial
// window visibility after recreation is INVISIBLE in onResume() and next frame
// ViewRootImpl.performTraversals() changes window visibility to VISIBLE.
// So we cannot call View.notifyEnterOrExited() which will do nothing
// when View.isVisibleToUser() is false.
getAutofillManager().notifyViewEntered(focus);
}
}
}
notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_RESUME);
mCalled = true;
}
- 如果处于运行的Activity即将执行onPause函数,此时可能有两种情况:
启动了一个新的Activity
返回了上一个Activity
可以理解为当需要其他的Activity时,当前的Activity必须先把手头的工作停下来,再把当前页面的界面空间交给下一个需要界面的Activity,而onPause可以看做一个转接的过程,因为屏幕空间只有那么一个,每次只允许有一个Activity出现在前台进行工作。通常情况下onPause函数不会被单独执行,执行完onPause函数不会被单独执行,执行完的onPause方法会继续执行onStop方法,执行完onStop方法才真正意味着当前Activity已经退出了前台。存在于后台
protected void onPause() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this);
dispatchActivityPaused();
if (mAutoFillResetNeeded) {
if (!mAutoFillIgnoreFirstResumePause) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill notifyViewExited " + this);
View focus = getCurrentFocus();
if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
getAutofillManager().notifyViewExited(focus);
}
} else {
// reset after first pause()
if (DEBUG_LIFECYCLE) Slog.v(TAG, "autofill got first pause " + this);
mAutoFillIgnoreFirstResumePause = false;
}
}
notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_PAUSE);
mCalled = true;
}
- 当前的Activity即将执行onStop哈数,当次Activity要从前台切换至后台时才会执行。
protected void onStop() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this);
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
mActivityTransitionState.onStop();
dispatchActivityStopped();
mTranslucentCallback = null;
mCalled = true;
if (mAutoFillResetNeeded) {
getAutofillManager().onInvisibleForAutofill();
}
if (isFinishing()) {
if (mAutoFillResetNeeded) {
getAutofillManager().onActivityFinishing();
} else if (mIntent != null
&& mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
// Activity was launched when user tapped a link in the Autofill Save UI - since
// user launched another activity, the Save UI should not be restored when this
// activity is finished.
getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_CANCEL,
mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
}
}
mEnterAnimationComplete = false;
}
- 当前Activity即将执行onDestory函数,代表着这个Activity即将进入生命的终结点,这是Activity生命周期的最后一次回调生命周期,我们可以在onDestory函数中做一些回收工作和资源释放操作,比如广播寄售期的注销工作。执行完onDestory方法的Activity接下来面对的是GC回收,宣告生命终结。
异常情况下的生命周期
情况1: 资源相关的系统配置发生改变导致Activity被杀死并重建
情况2:内存资源不足导致低优先级的Activity被杀死
当Activity发生意外情况的时候,如配置系统发生改变,Activity会被销毁,其中onPause,onStop,onDestory函数均会被调用,同时由于Activity是在异常情况下被终止的,系统会调用onSaveInstance来保存当前Activity状态。调用onSaveInstanceState的时机总会发生在onStop之前,至于会不会调用时机发生在onPause之前,是不确定的。当Activity被重建,系统回调用onSaveInstanceState,并将onSaveInstance方法所保存的Bundle对象作为参数传递给onRestoreInstanceSate和onCreate方法。从时序上来看,onRestoreinstancestate理论上是应该发生在Start之后没因为oncreate是开始绘制,而start状态代表绘制完成,此时填入数据才能让Activity实现恢复工作。
关于保存和恢复View
当Activity别意外终止,Activity会调用onSaveInstanceState去保存数据,然后Activity会委托Window去保存数据,接着window再委托它内部的顶级容器去保存数据,这个容器就是ViewGroup,然后顶层容器在分发通知它的子元素来保存数据,这样数据的保存过程就完事了。
关于Activity的优先级:
优先级最高:与用户正在交互的Activity,就是前台Activity
优先级中等:可见但是非前台的Activity,比如一个弹出对话框的Activity,可见但是非前台运行
优先级最低:完全存在于后台的的Activity,比如执行了onStop
Android任务栈简介
一个Android应用程序功能通常被拆分为多个Activity,而且各个Activity之间通过Intent进行连接,而Android系统,通过栈结构来保存整个App的Activity,栈底的元素是整个任务栈的发起者。一个合理的任务调度栈不仅是性能的保证,更是提供性能的基础。
当一个App启动的时候,如果当前环境中不存在该App的任务栈,那么系统会创建一个任务栈。此后,这个App启动的Activity都将在这个任务栈中被管理,这个栈也被称为一个Task,即表示若干个Activity的集合,他们组合成一个Task。一个Task中的Activity可以来自不同的app,同一个App的Activity也可能不在同一个Task中。
关于栈结构,是一种先进后出的线性表。根据Activity在当前栈结构的位置,来决定该Activity的状态。正常情况下的Android任务栈,当一个Activity启动了另一个Activity时,新启动的Activity会置于任务栈的顶端,并处于活动状态,而启动它的Activity虽然功成身退,但依然保留在任务栈里,当用户调用返回时,系统会溢出顶部的Activity,让后面的Activity里恢复活动状态,当然也有例外,通过在AndroidMainfest中设置android:launchMode来设置或者通过Intent的flag来设置。
Android的启动模式
首先是问题:
- Activity为什么需要启动模式?
当启动一个Activity后,这个Activity的实例就会被放在任务栈中,当点击返回键的时候,位于任务栈的顶端的Activity就会被清理出去,当任务栈中不存在任何Activity实例后,,系统就会去回收这个任务栈,也就是任务退出了,假如没有启动模式,每次启动一个Activity就会把对应的要启动的Activity的实例放在任务栈中,假如这个Activity被频繁启动,或者A启动B,B启动A,A在启动B,在这叠千层饼,白白的浪费资源。所以有了启动模式。 - Activity的启动模式有哪些?
Activity共有四种模式:standard,singleTop,singleTask和singleInstance
系统默认的启动模式Standart:
标准模式,这也是系统的默认模式,每次启动一Activity都会重新创建一个新的实例,不管这个实例是否存在。被创建的实例的生命周期符合典型情况下的Activity的生命周期。在这种模式下,谁启动了这个Activity,那么这个Activity就在谁的任务栈中。比如A启动了B(B也是标准模式),那么B就会进入到A所运行的任务栈中。当我们用ApplicationContex去启东standard的Activity就会报错,因为standard模式的Activity默认会进入到启动它的所属的任务栈中,但是由于非Activity类型的Context如Application并没有所谓的任务栈,所以这就会出现错误。解决方法是为启动的Activity指定的FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候会为它创建一个新的任务栈,这个时候启动Activity实际上是以singleTask模式启动的
singleTop启动模式
在这种模式下,如果新的Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,同时它的onNewIntent方法会被回调,通过此方法的参数我们可以取出当前请求的信息。这个Activity的onCreat,onStart不会被系统调用,因为并没有发生改变如果新的Activity已经存在但不是位于栈顶,那么新的Activity仍然会重新重建。
例如:假设目前栈内的情况为ABCD,其中ABCD为四个Actvity,A位于栈底,D位于栈顶,这个时候假设要启动D,如果D的启动模式为singleTop,那么栈内的情况仍为ABCD,如果D的启动模式standart,那么由于D被重新创建,导致栈内为ABCDD
如果指定启动的Activity为singleTop模式,那么在启动时,系统会判断当前栈顶的Activity是不是要启动的Activity,如果是则不创建新的Activity而是直接引用这个Activity。这种模式常用于接收消息后显示页面,例如QQ接收到消息后弹出Activity。这种启动模式通常适用于接收到消息后显示的界面,例如QQ接受到消息后弹出的Activity,如果一次来10条消息,总不可能一次性弹出10个Activity。
singleTask
singleTask模式与singeTop模式类似,只是singleTop是检测栈顶元素是否是需要启动的Activity,而singleTask是检测整个Activity栈中是否存在当前需要启动的Activity,如果存在则将此Activity以上的Activity都销毁。并将Activity以上的Activity都销毁。这里是指在同一个App中启动这个singleTask的Activity,如果是其他程序来以singleTask模式来启动这个Activity,那么它将创建一个新的任务栈,如果启动模式的singleTask的Activity已经在后台的一个任务栈中了,那么启动后,这个后台的任务栈将一起被切换到前台。
当Activity2启动ActivityY(启动模式为singleTask)时,它所在的Task都被切换到前台,且范围键返回时,也会返回Activity所在的Task的Acticity。
使用这个模式创建的Activity不是在新的任务栈中被打开,就是将已打开的Activity切换到前台,所以这种启动模式通常可以用来退出整个应用,将主Activity设置为singleTask模式,然后再要退出的Activity中转到主Activity,从而将主Activity纸上的Activity都清除,然后重写Acitivity的onNewIntent方法,在方法中加入Finish,最后一个Activity结束掉。
本质上,栈内复用是一种单例模式,在这种模式下,只要Activity在一个栈中存在,那么多次启动此Activity都不会重新建立实例,和singleTop一样,系统也会回调其onNewIntent。
几个例子:
- 当前任务栈S1的情况为ABC,这个时候ActivityD以singleTask模式请求启动,其所需的任务栈为S2,由于S2和D的实例均不存在,所以系统会先创建任务栈S2,在将D投入到S2中。,如果所需的任务栈是S1,那么会直接投入到S1中
- 如果所需的任务栈是S1,但是S1中是ADBC,根据栈内复用的原理,D不会重新创建,系统会切换到栈顶并调用其onNewIntent方法,同时由于singleTask默认具有clearTop的效果,会导致栈内所有的D上面的Activity全部出栈,最终S1中的情况时AD.
singleInstance模式
加强化的singleTask模式,除了具有singleTask模式所具有的的所有的特性外,还加强了此模式下的Activity只能单独位于一个任务栈中,如果ActivityA是singleInstance模式,当A启动时,系统会为它创建一个新的任务栈。由于栈内复用的特性,后续的请求不会创建新的Activity,除非这个单独的任务栈被系统销毁。
- 以singleInstance模式启动的Activity具有全局唯一性,即整个系统中只会存在一个这样的实例
- 以singleInstance模式启动的Activity在整个系统中是单例的,如果启动这样的Activity,那么会把它的所在的任务栈调度到前台,重用这个实例
- 以singleInstance模式启动的Activity具有独占性,即它会独自占用一个任务栈,被他开启的的任何Activity都会运行在其他任务中
- 被singleInstance模式的Activity开启的其他Activity,能够在新的任务栈启动,但不一定开启新的任务栈,也可能存在一个已有的任务栈中开启
IntentFlag 启动
系统通过两种方式来设置一个Activity的启动模式,接下来是Intent的Flag模式来设置一个Activity的启动模式
- Intent.FLAG_ACTIVITY_NEW_TASK
- Intent.FLAG_ACTIVITY_SINGLE_TOP
- Intent.FLAG_ACTIVITY_CLER_TOP
- Intent.FLAG_ACTIVITY_NO_HISTORY
清空任务栈
系统提供了清空任务栈的方法来实现将一个任务Task全部清除。通常情况下可以在AndroidMainifest文件中的activity标签中使用以下几种属性来清理任务栈
- clearTaskLauch
顾名思义,就是在每次返回该Activity时,都将该Activity之上的所有Activity都清除,通过这个属性,可以让这个Task每次在初始化的时候,都只有一个Activity。 - finishOnTaskLaunch
finnishOnTaskLaunch属性与clearTaskOnLaunch属性类似,只不过clearTaskOnLauch作用在别人身上,而finishOnTaskLauch作用在自己身上,通过这个属性,当离开这个Activity所在的Task,那么用户再返回时,该Activity就会被finish - alwaysRetainTaskState
alwaysRetainTaskSatate属性给Task一道免死禁片,如果Activity的这个属性设置为True,那么该Acitity所在的Task将不接受任何清理命令,直至保持当前Task状态。
Activity组件之间的通信
-
Activity->Activity之间的通信
方法一:采用Intent/Bundle,最基本的方式
方式二:采用类静态变量的方式,用于少量数据通信。
方式三:全局变量,创建一个类,定义一批静态变量,Activity之间通信通过访问这个类变量来实现 -
Activity->Service之间的通信
方式一:绑定服务的方式:利用ServiceConnection这个接口
方式二:Intent,在启动和停止Service时所调用的方法都需要传入一个Intent实例对象,通过这个Intent对象,与Service进行通信。
方式三:CallBack+Handler,监听服务的进程变化 -
Activity->Fragment
方式一:Bundle在创建Fragment实例时,调用setArguments将一个Bundle对象传递给Fragment,然后在Fragment中先去判断是否和当前Activity绑定上,如果绑定上,就可以拿出Bundle中的数据
方式二:直接进行方法调用