什么是Activity?Activity有什么作用?
Activity是应用中负责与用户进行交互的组件,我们可以简单地把Activity理解为应用中的一个个界面。一般我们可在Activity中处理与控件,视图等与用户交互的逻辑。
Activity的生命周期?
想要熟练使用Activity,必须要了解它的生命周期,也就是Activity从创建到销毁的整个过程。Activity的生命周期如下图:
Activity内部有一些回调方法,它们会在Activity的生命周期的各个阶段被调用:
当我们启动一个Activity时,会按如下顺序执行方法:onCreate() -> onStart() -> onResume() 。
首先在创建Activity时会调用onCreate()方法,我们可以在onCreate()里面进行初始化变量,初始化控件,初始化数据等一些操作。然后当Activity创建完成开始显示时,就会执行onStart()方法,也就是说在onStart()时,我们就可以看到该Activity了。当Activty能够与用户进行交互时,也就是用户可以进行点击等操作时,就会执行onResume()方法,这时的Activity必定是处于栈顶的(关于栈的问题,会在下面讨论)。
当我们按Back键关闭当前 Activity ,返回到上一个界面时,会按顺序调用如下方法:onPause() -> onStop() -> onDestory()。
首先 Activity 会执行onPause()方法,在onPause()时,Activity处于无法与用户进行交互的状态,但是这时的Activity还是可见的,只不过我们无法对其进行操作。接着Activity由可见变为不可见状态时,就会调用onStop()方法,这是我们完全看不到该Activity。最后Actiivty会清空内部的变量缓存和数据等,销毁自身,这时就会调用onDestory()方法,我们可以在onDestory()方法中进行一些资源回收操作和解注册操作,以免造成内存泄漏。(内存泄漏是指Activity或其他组件被销毁时,由于被别的类持有了它的引用,导致其在销毁时无法释放内存,内部的数据仍被保留着,这时就会造成内存资源浪费的现象)
还有一点要注意的是,当我们按 Home 按钮返回到手机主界面时,Activity并不一定就会被销毁。它会调用onPause() -> onStop()方法,但是不一定会调用onDestory()方法,所以当我们重新打开 App 时,它会回到之前的那个Activity界面,而不是重新启动一遍。这时的 Activity 就会调用onRestart() -> onStart() -> onResume() 方法,onRestart()方法表示这个Activity从不可见状态重新回到可见状态。当然,也不是所有情况下 Activity 都不会被销毁,具体是否调用onDestory()方法,这要视手机的内存状况来决定,这里需要了解进程的优先级的相关概念。
Android中进程的优先级
Android中将进程的优先级分为了五个等级:前台进程 / 可见进程 / 服务进程 / 后台进程 / 空进程。其优先级也是按该顺序从高到低。
前台进程:该优先级为最高级,处于该优先级的进程,就算内存严重不足,系统也不会对其进行强制的终止回收操作,即使程序ANR。
前台进程的场景:1.进程拥有一个正处于交互状态(即执行了onResume()方法)的Activity。 2.进程拥有一个Service,且该Service与一个处于交互状态的Activity所绑定。 3.进程拥有一个前台Service,即调用了startForeground()方法的Service。 4.进程拥有一个Service,且该Service正在执行生命周期中的某个方法。 5.进程拥有一个Broadcast,且该Broadcast在执行onReceive()方法。
可见进程:该优先级稍低于前台进程,处于该优先级的进程是处于可见的状态,但不可以和用户进行交互。一般该优先级的进程也不会轻易的被系统回收,因为停止该进行会带来界面的变化,从而给用户造成不好的体验效果。
可见进程的场景:1.进程拥有一个调用了onPause()但未调用onStop()方法的 Activity,该Activity可见却无法与用户交互。 2.进程拥有一个Service,且该Service与一个前台Service绑定。
服务进程:处于该优先级的进程一般都是在后台运行的Service,它的优先级低于前台进行和可见进程。当App的内存严重不足时,且空进程和后台进程都已被回收时,系统可能会结束该进程,回收内存。
后台进程:处于该优先级的进程,可能会因为系统内存不足而被回收。典型的后台进程,就是我们执行了onStop()但未执行onDestory()的Activity。比如我们按Home键回到主界面,但之前的Activity还并未被销毁,此时进程处于后台,点击App可以随时返回到原来的界面,但当系统内部不足时,该进程就会被结束。
空进程:不含任何活动应用组件的进程,保留这种进程的的唯一目的是用作缓存,以加快进程启动的速度。当内存不足时,系统往往会终止这些进程。
异常情况下的 Activity的生命周期:
Activity在两种情况下,会被异常结束:
1.当资源相关的系统配置发送改变时,典型的例子就是当应用从竖屏切换到横屏时,Activity会被异常销毁再创建。
2.当系统内存不足时,会杀死低优先级的Activity。前面在介绍Activity优先级时有说到这种情况。
Activity 在异常状况下被销毁之前会先调用 onSaveInstanceState() 方法,该方法会创建一个 Bundle 对象,并且将 Activity 中的部分数据保存到该 bundle 对象中。当我们再次重启该 Activity 时,会调用 onRestoreInstanceState()方法从中取出 Bundle 中的数据,进行一些数据恢复操作。当然,我们也可以在 onCreate() 中获得该 Bundle 对象的数据(只限异常销毁情况下)。
onSaveInstanceState() 方法和 onRestoreInstanceState() 方法只会在 Activity 异常结束的情况下被调用。onSaveInstanceState() 的执行时机在 onStop() 之前, onRestoreInstanceState() 方法的执行时间在 onCreate() 方法之后。
当我们有些 Activity 没有设置 ConfigChanges 属性,在屏幕横竖切换时,就会导致 Activity 异常销毁并重建。当然我们可以在 AndroidManifest 中给 Activity 设置属性,使其不会销毁。设置过后,当横竖屏切换时就不会异常销毁重建 Activity 以及执行 onSaveInstanceState()方法和 onRestoreInstanceState()方法,而是会调用 onConfigurationChanged()方法。
android:configChanges="orientation|screenSize|keyboardHidden"
Activity的栈
Android中的Activity是以栈结构来进行存储的,每当启动一个Activity,系统会创建它并放入到堆栈(返回栈)中。当Activity启动另一个Activity时,会将新的Activity放入栈中,这时之前的Activity就会处于停止状态(并未被销毁),新入栈的Activity处于之前的Activity的上面。处于栈的顶部的Activity可以与用户进行交互,它处于最高优先级,但任何阶段一个栈都只会有一个Activity处于栈顶。当我们点击了返回按钮或调用了Activity的finish()方法时,都会将当前Activity出栈,这时上一个Activity就会处于栈顶,并可与用户交互。
要注意的是,一个App中并不一定只存在一个栈,它可能会有多个栈存在。一般Activity在启动时会创建一个和当前包名相同的栈,并把Activity加入栈中。如果我们想要为Activity创建一个新的单独的栈,可以在AndroidManifest中添加taskAffinitys属性,taskAffinity中传入的名字就是新建的栈的名字。
<application> ... <activity android:name=".sample.SampleListActivity" android:taskAffinity="com.example.mymodeldemos1"/> ... </application>
上面的代码就是为SampleListActivity创建了一个新的栈,栈名就是com.example.mymodeldemos1。
Activity的启动模式
Activity的启动模式有四种:Standard标准模式,SingleTop栈顶复用模式,SingleTask栈内复用模式,SingleInstance单例模式。每种启动模式都有不同的功能效果,根据实际需求为Activity设置启动模式,可以对Activity的整体跳转流程进行很好的管理,还可以避免Activity的重复创建,节省内存。
Standard(标准模式):Activity 默认的启动模式。该模式下允许在同一个栈中存在多个相同Activity的实例,即在同一个栈中启动相同的Activity,会重复创建该Activity。被创建的Activity会进入到启动它的那个Activity所在的栈中。
SingleTop(栈顶复用模式):当启动该Activity时,只要该Activity位于栈顶,就不会被重新创建,同时会调用onNewIntent()方法。如果Activity不处于栈顶或者不在栈中,则会被创建。该模式和ClearTop标志位搭配使用可以达到SingleTask模式的效果。
SingleTask(栈内复用模式):如果栈中已存在该Activity,则会自动ClearTop,清除该Activity上面的所有其他的Activity,使该Activity位于栈顶,此时会调用onNewIntent()方法。如果栈内不存在该Activity,则创建该Activity,并且入栈。设置为SingleTask模式的Activity如果在未被销毁的情况下启动自身,只会执行 onPause() -> onResume()方法,同时也会调用onNewIntent()方法。
SingleIntance(单例模式):和SingleTask模式效果类似,不过该模式的Activity被创建时,系统会单独为它创建一个栈,并将其入栈,并且该栈中只允许着一个Actiivty存在,其他的Activity不能进该栈。该Activity在整个系统中都是唯一的,也就是说即使是不同的应用调用该Activity,也不会创建新的Activity对象。
至于如何给Activity设置启动模式,有如下两种方法:
我们可以在AndroidMenifest中设置Activity的launchMode来指定启动模式
<activity android:name=".HomeActivity"
android:launchMode="singleTask"
android:configChanges="orientation|screenSize|keyboardHidden">
...
</activity>
也可以在代码中通过为Intent设置Flag来指定Activity的启动模式
Intent intent=new Intent(HomeActivity.this,ButterTestActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
注意:如果你先创建了一个Activity,然后在代码中通过给intent设置FLAG_ACTIVITY_NEW_TASK(即SingleTask模式),那么之前创建的Activity是不会受影响的,仍然会存在于任务栈中。
Activity的常用标志位(Flag):
FLAG_ACTIVITY_NEW_TASK:该标记位作用和设置Activity启动模式为SingleTask的效果一致。
FLAG_ACTIVITY_SINGLE_TOP:该标记为作用和设置Actvity启动模式为SongleTop的效果一致。
FLAG_ACTIVITY_CLEAR_TOP:如果Actvity设置了此标记,那么当这个Activity启动时,会使它上面的所有Actvity出栈,该Activity将处于栈顶位置。SingleTask模式的Activity在启动时会自动ClearTop,而不需要设置该Flag。如果是Standard模式的Actvity使用该标记,那么它会将包括自身以及它之上的Actvity都出栈,并重新创建该 Actvity实例。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:如果Activity使用了此标记,那么当该Actvity跳转到其他Actvity后,该Actvity自身就会出栈,按Back键无法再返回到该Activity页面(就相当于调用了finish()方法)。 一般注册,登录等页面会用到此标记位。
了解了Activity的启动模式和标志位后,我们来看看它的实际应用。我们假设这样一个状况,一个App中先启动了Activity1,然后Activity1启动了Activity2,然后Activity2启动了Activity3。这时我们想要从Actiivty3回到Activity1界面,应该怎样做呢?
首先最简单的方法就是按两次Back键,将Activity3,Actiivty2先后出栈,这时处于栈顶的就是Activity1了。这种方法虽然简单,但是当Activity层级过多时就比较麻烦了,多次的Back也会让用户感到不耐烦。那么我们直接在Activty3中调用startActivity()方法启动Activity1又如何?这样确实可以跳转到Activity1,但其实这个Activity1是被重新创建出来的Activity,也就是说在栈中存在着两个Activity1,这样多次的创建同一个Activity无疑为造成资源的浪费,所以我们可以考虑为Activity1设置其他的启动模式来解决这个问题。比如将该Activity设置为SingleTask启动模式,则当Activity3启动Activity1时,Activity3和Activity2会被自动出栈,然后回到Actiivty1界面,不需要我们去点击Back按钮。
问题:假如 ActivityA 要启动 ActivityB ,这时是先执行A的 onPause 方法还是先执行B的 onResume 方法?
其实仔细思考一下就能得出答案,因为不可能有两个Activity 同时在栈顶,所以必然是 ActivityA 的 onPause 方法最先执行,然后在执行 ActivityB 的 onResume 方法。其实 ActivityA 启动 ActivityB 的详细过程如下:
A(onPause)> B(onCreate)> B(onStart)> B(onResume)> A(onStop)
所以我们应该尽量避免在 onPause() 中执行耗时操作,不然当启动一个新的 Activity 时会影响 Activity 的显示和前台的切换。
问题:Activity 的finish()方法和onDestory()方法的区别:
finish()方法的内部会调用onDestory()方法,执行finish()方法,Activity就会出栈,下一个Activity即使按Back键,也回不到该Activity 。而如果一个Activity被销毁了即执行了onDestory()方法,它的栈信息很可能还会保留着。
文章参考:https://developer.android.com/guide/components/activities.html