1、Activity的生命周期
生命周期我我在基础里面写过一部分,也写过一段demo,不过那个并不全面。
http://www.cnblogs.com/xxbbtt/p/7515024.html
Activity的生命周期分为两种,一种是有用户参与的情况下Activity所经过的生命周期的变化,另一种是指Activity被系统回收或者Configuration发生改变从而导致Activity被销毁重建。
还是一样的使用这张图,Activity是作为与用户交互的界面使用的,它就相当于一个网页,网页里面有文字、图片、视频。Activity同样也有,而一个应用程序可以类比为浏览器,在android中第一次启动一个“浏览器”会默认打开一个“网页”,“网页”打开会加载其中的内容,而想要实现这个加载功能就需要实现onCreate()方法,所以:
- onCreate()方法:表示Activity正在被创建,这是生命周期的第一个方法,在这个方法中做一些初始化的工作。
- onStart()方法:表示Activity正在被启动,即将开始,这时Activity已经可见了。但是还没有出现在前台,还无法和用户交互。
- onResume()方法:表示Activity已经可见,并且出现在前台并开始活动。
- onPause()方法:表示Activity正在停止,一般紧接着onStop()方法将会被调用,除非用户很快的又切回这个Activity,不过一般不存在,需要记住的就是不要在这个方法做太耗时的工作,不然会影响到新Activity的启动,因为让新活动重新启动的方法会在onPause()方法之后执行。
- onStop()方法:表示Activity即将停止,可以做一些稍微重量级的回收工作,但也不能太耗时。
- onRestart()方法:表示Activity正在被重新启动,一般是这个Activity被从不可见状态变为可见状态,一般是用户返回了桌面或者启动了新的活动,然后再返回这个Activity的时候会启动这个方法。
- onDestroy()方法:表示Activity即将被销毁,这是生命周期最终后一个方法。
实际上有下面几种情况:
- 一个Activity第一次被启动:onCreate->onStart->onResume
- 当一个新的Activity被启动或者切换到桌面的时候:onPause->onStop,这里要注意的时如果还能够看到这个Activity(比如新的Activity采用了透明主题),那么onStop并不会被调用。
- 当在此回到旧的Activity时:onRestart->onStart->onResume
- 当用户使用back键回退的时候:onPause->onStop->onDestroy
- 当Activity被系统回收后再次打开时:onCreate->onStart->onResume
- Activity的生命周期中只有一次调用onCreate和onDestroy。从Activity可不可见来看onStart和onStop是配对的,从Activity在不在前台onPause和onResume是配对的。
这里要针对onPause需要重点说一说:暂停当前Activity大概有四种情况:锁屏、切回桌面、切换到其他Activity、弹出一个对话框。这些情况都会导致当前的Activity从前台切换至后台,onPause会被调用。当切换到其他Activity的时候,这个其他Activity如果是一个新的Activity那么就会调用它的onCreate方法,如果是重新启动的就会调用它的onRestart方法,然后这个onPause方法会先执行,再执行Activity的onCreate或nRestart方法,这就是为什么不能在onPause做太耗时的操作的原因,太耗时会导致新的Activity启动卡顿。
2、异常情况下的生命周期分析
- 情况1:资源相关的系统配置发生改变导致Activity被杀死并重新创建
这里由一个简单的demo,有一个TextView、一个Button、一个EditText,当按下按钮时TextView会显示从EditText获取的数据。
public class MainActivity extends AppCompatActivity { private TextView textView; private EditText editText; public Button button1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.main_text); editText = (EditText) findViewById(R.id.main_edit); button1 = (Button) findViewById(R.id.main_button1); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { textView.setText(editText.getText()); } }); } }
比如当前Activity处于竖屏状态,TextView正在显示输入框中的内容,
当我们突然旋转屏幕,TextView的内容将会消失。
这是因为在默认情况下,如果我们的Activity不做特殊处理,那么当系统配置发生改变时Activity将会被销毁并重新创建。
//由不可见转换为可见时调用 @Override protected void onStart() { super.onStart(); Log.d("MainActivity","onStart()"); } //由可见转换为不可见时调用 @Override protected void onStop() { super.onStop(); Log.d("MainActivity","onStop()"); } //离开前台,但不是完全不可见 @Override protected void onPause() { super.onPause(); Log.d("MainActivity","onPause()"); } //返回前台,准备好和用户交易 @Override protected void onResume() { super.onResume(); Log.d("MainActivity","onResume()"); } //重启处于停止状态的活动时对用 @Override protected void onRestart() { super.onRestart(); Log.d("MainActivity","onRestart()"); } // 在完整生存期结束时调用 @Override protected void onDestroy() { // 清空所有的资源,包括结束线程、关闭数据库连接等。 super.onDestroy(); Log.d("MainActivity","onDestroy"); }
在这个demo中加入以上代码,这样我们就知道那些生命周期的方法被调用了
当屏幕旋转时的输出:
12-01 10:19:07.098 30088-30088/com.example.administrator.test D/MainActivity: onPause() 12-01 10:19:07.101 30088-30088/com.example.administrator.test D/MainActivity: onStop() 12-01 10:19:07.102 30088-30088/com.example.administrator.test D/MainActivity: onDestroy 12-01 10:19:07.154 30088-30088/com.example.administrator.test D/MainActivity: onCreate 12-01 10:19:07.155 30088-30088/com.example.administrator.test D/MainActivity: onStart() 12-01 10:19:07.163 30088-30088/com.example.administrator.test D/MainActivity: onResume()
可以看到Activity被销毁,其中onPause、onStop、onDestroy依次被调用,这里是销毁竖屏的Activity
还可以看到Activity被创建,其中onCreate、onStart、onResume依次被调用,这里是创建横屏的Activity。
这里由于Activity是在异常情况下终止的,所以系统会调用onSaveInstanceState方法来保存当前的Activity的状态。
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); }
这是我Ctrl+o直接插入的这个onSaveInstanceState方法,这个方法使用的是Bundle对象进行之前的状态保存的。
protected void onCreate(Bundle savedInstanceState) {
这个onCreate也是编译器一开始就编写好的,可以看到这里接收了一个Bundle对象,所以其实再创建Activity的时候是可以在onCreate进行重建的。
@Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); }
不过当Activity重建的时候,系统也会调用onRestoreInstanceState这个方法,onSaveInstanceState所保存的Bundle对象会同时传递给onCreate和onRestoreInstanceState方法。
12-01 10:32:54.836 31306-31306/com.example.administrator.test D/MainActivity: onPause() 12-01 10:32:54.838 31306-31306/com.example.administrator.test D/MainActivity: onSaveInstanceState() 12-01 10:32:54.840 31306-31306/com.example.administrator.test D/MainActivity: onStop() 12-01 10:32:54.840 31306-31306/com.example.administrator.test D/MainActivity: onDestroy 12-01 10:32:54.881 31306-31306/com.example.administrator.test D/MainActivity: onCreate 12-01 10:32:54.883 31306-31306/com.example.administrator.test D/MainActivity: onStart() 12-01 10:32:54.884 31306-31306/com.example.administrator.test D/MainActivity: onRestoreInstanceState() 12-01 10:32:54.889 31306-31306/com.example.administrator.test D/MainActivity: onResume()
这是销毁、重建过程中onSaveInstanceState、onRestoreInstanceState被调用的时间。onSaveInstanceState实际上肯定会在onStop之前,当时和onPause的先后就不一定。onRestoreInstanceState的调用时机实在onStart之后。这里要注意的是,onSaveInstanceState和onRestoreInstanceState都是在Activity被异常终止是才会调用的,正常情况下是不会调用的。
在onSaveInstanceState、onRestoreInstanceState中系统会默认的为我们做一些默认的保存工作,这些默认的保存操作是针对某一个特定的View的,每一个View都有onSaveInstanceState、onRestoreInstanceState方法。
@Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); Log.d("MainActivity","onRestoreInstanceState()"); textView.setText(savedInstanceState.getString("textView.getText()")); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.d("MainActivity","onSaveInstanceState()"); outState.putString("textView.getText()", textView.getText().toString()); }
这样就能保存之前TextView的状态并恢复。
- 情况2:资源内存不足导致低优先级的Activity被杀死
Activity按照优先级从高到低,可以分为如下三种:
(1) 前台Activity——正在和用户交互的Activity,优先级最高
(2)可见但非前台Activity——比如Activity中弹出一个对话框,导致Activity可见但是位于后台无法和用户直接交互
(3) 后台Activity——已经被暂停的Activity,优先级最低。
当系统内存不足时,系统会按照上面的优先级去杀死目标Activity所在的进程,并使用onSaveInstanceState、onRestoreInstanceState和方法。
为了防止当系统配置改变而发生的Activity重新创建,我们可以修改AndroidManifest中的onfigChanges属性。比如添加这样一行:
android:configChanges="orientation"
3、Activity的启动模式
standard:标准模式,每一次启动一个Activity都会创建一个新的实例,假设现在在Activity A,启动一个Activity A那么就会重新调用onCreate,这时栈中就有两个Activity A 。
singleTop:栈顶复用模式,如果新Activity已经位于任务栈的栈顶那么,该Activity不会被重复创建,同时onNewInrent方法会被调用,,通过此方法的参数我们可以取出当前请求的信息。
singleTaks:栈类复用模式,只要栈中有这个Activity,系统就会吧这个Activity调到栈顶并调用它的onNewIntent方法,如果不存在,就重新创建一个任务栈,并创建实例并放入其中。这里举几个例子:
- 比如目前任务栈S1中的情况为ABC,这个时候ActivityD以singleTask模式请求启动,其所需要的任务栈为S2,由于S2和D的实例均不存在,所以系统就会创建任务栈S2,再创建D的实例并将其入栈到S2
- 假设D所需的任务栈为S1,那么会直接创建D并入栈到S1
- 如果D所需的任务栈为S1,且当前任务栈S1的情况为ADBC,根据栈内复用的原则,系统会把D切换到栈顶并调用它的onNewIntent方法,但是同时由于singleTaks默认具有clearTop的效果,会导致所有在D上面的Activity全部出栈,S1就变成AD。
singleInstance:单实例模式,这是一种加强的singleTaks模式,加强的就是,具有这种模式的Activity只能单独地位于一个任务栈中,比如Activity A是singleInstance模式的,,当A 启动的时候,系统会为其建立一个新的任务栈,任何无论在启动多少次A都不会创建新的Activity,除非这个独特的任务栈被系统销毁 了
那什么是任务栈呢,首先要说一个参数:TaskAffinity,意为任务相关性,这个参数标识了一个Activity所需要的任务栈的名字,默认情况下,所有Activity属性的任务栈的名字为应用的包名,我们也可以为每一个Activity单独指定TaskAffinity属性,这个属性名不能和包名相同,否则就相当于没有指定。TaskAffinity这个属性通常与singleTask启动模式或者allowTaskReparenting属性配对使用。
当singleTask和allowTaskReparenting配对使用的时候,会产生特殊的效果。当一个应用A启动了应用B的Activity C的时候,如果allowTaskReparenting为true的话,那么当B被启动的的时候,此Activity C会直接从应用A的任务栈转移到应用B的任务栈中,因为刚开始A启动的B的这个Activity C是运行在A的任务栈中的,这时启动B会创建B的任务栈,但是C是被A启动的也运行在A的任务栈中,所以就会吧C转移到B的任务栈中。
给Activity指定启动模式有两种方法:第一种是在AndroidManifest中修改
<activity android:name=".MainActivity"
android:launchMode="standard" />
第二种是通过在Intent中设置标志位来为Activity指定启动模式
Intent intent = new Intent(); intent.setClass(MainActivity.this, SecondActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent);
两者的区别:优先级上第二种高于第一种,两种同时存在时,以第二种为准
第一种无法直接为Activity设定FLAG_ACTIVITY_CLEAR_TOP标识,第二种无法为Activity指定singleInstance模式
Activity的Flags
- FLAG_ACTIVITY_NEW_TASK
这个标记位的作用是为Activity指定singleTask启动模式,其效果和在清单文件中指定相同。
- FLAG_ACTIVITY_SINGLE_TOP
这个标记位的作用是为Activity指定singleTop启动模式,其效果和在清单文件中指定相同。
- FLAG_ACTIVITY_CLEAR_TOP
具有此标记位的Activity,当它启动时,在同一个任务栈中,所有位于它上面的Activity都要出栈。
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
具有这个标记的Activity不会出现在历史Activity列表中(这里点击手机除back和home键的另外一个键就会出现recent列表了),当某些情况下我们不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用。它等同于在清单文件xml中指定Activity的属性android:excludeFromRecents = "true";
4、IntentFilter的匹配规则
IntentFilter中过滤的信息有action、category、data.为同时匹配过滤列表,需要同时匹配过滤列表中的action、category、data信息,否则匹配失败。一个过滤列表中的action、category、data可以有多个,所有的action、category、data分别构成不同类别
- action的匹配规则
action是一个字符串,系统预定义了一些action,同时我们也可以在应用中定义自己的action,一个过滤规则可以有多个action,只要Intent中的action能够和过滤规则中的任何一个action相同即可匹配成功,需要注意的是,Intent中如果没有指定action那么匹配失败,另外action区分大小写。
- category的匹配规则
category同样是一个字符串,系统预定义了一些category,同时我们也可以在应用中定义自己的category,不同的是,它要求Intent中如果含有category,那么所有的category都必须和过滤规则中的其中一个category相同。
- data的匹配规则
data的匹配规则和和action类似。
data的语法如下
<data android:scheme="string" android:host="string" android:port="string/" android:path="string" android:pathPattern="string" android:pathPrefix="string" android:mimeType="string"/>
data由两部分组成,mimeType和URI。mimeType指媒体类型,比如image/jpg 、audio/mpeg4-generic和video/*等,而URI包含的就多了,下面是URI的结构
scheme://host:port/path or pathPrefix or pathPattern
scheme:URI的模式,比如http、file、content,这个是必须指定的。
host:URI的主机名,比如www.baidu.com,这个也是必须指定的。
por:URI中的端口号。
path、 pathPrefix、 pathPattern:这三个参数表述路径信息,其中path表示完整的路径, pathPattern也表示完整的路径信息,但是它里面可以包含通配符“*”,pathPrefix表示路径的前缀信息。
参考书籍:Android 开发艺术探索