一、Activity的生命周期
1.1 生命周期图
1.2 可见状态图
1.3 周期图
1.4 状态解释
| 生命周期方法 | 作用 | 说明 |
|---|---|---|
| onCreate | 正在被创建 | activity 被创建时调用,一般在这个方法中进行活动的初始化工作,如设置布局工作、加载数据、绑定控件等。 |
| onRestart | 正在重新启动 | 这个回调代表了 Activity 由完全不可见重新变为可见的过程,当 Activity 经历了 onStop() 回调变为完全不可见后,如果用户返回原 Activity,便会触发该回调,并且紧接着会触发 onStart() 来使活动重新可见。 |
| onStart | 正在被启动 | 经历该回调后,Activity 由不可见变为可见,但此时处于后台可见,还不能和用户进行交互。 |
| onResume | 已经可见 | 已经可见的 Activity 从后台来到前台,可以和用户进行交互。 |
| onPause | 正在停止 | 当用户启动了新的 Activity ,原来的 Activity 不再处于前台,也无法与用户进行交互,并且紧接着就会调用 onStop() 方法,但如果用户这时立刻按返回键回到原 Activity ,就会调用 onResume() 方法让活动重新回到前台。而且在官方文档中给出了说明,不允许在 onPause() 方法中执行耗时操作,因为这会影响到新 Activity 的启动。 |
| onStop | 即将停止 | 这个回调代表了 Activity 由可见变为完全不可见,在这里可以进行一些稍微重量级的操作。需要注意的是,处于 onPause() 和 onStop() 回调后的 Activity 优先级很低,当有优先级更高的应用需要内存时,该应用就会被杀死,那么当再次返回原 Activity 的时候,会重新调用 Activity 的onCreate()方法。 |
| onDestroy | 即将被销毁 | 来到了这个回调,说明 Activity 即将被销毁,应该将资源的回收和释放工作在该方法中执行。 |
二、 生命周期分析
2.1 常见情况下生命周期的回调
| 情况 | 回调 |
|---|---|
| 第一次启动 | onCreate() -> onStart() -> onResume() |
| 从 A 跳转到 B | A_onPause() -> B_onCreate() -> B_onStart() -> B_onResume() -> A_onStop() |
| 从 B 再次回到 A | B_onPause() -> A_onRestart() -> A_onStart() -> A_onResume() -> B_onStop() |
| 用户按 home 键 | onPause() -> onStop() |
| 按 home 键后回到应用 | onRestart() -> onStart() -> onResume() |
| 用户按电源键屏保 | onPause() -> onStop() |
| 用户按电源键亮屏 | onRestart() -> onStart() -> onResume() |
| 用户按 back 键回退 | onPause() -> onStop() -> onDestroy() |
2.2 关于生命周期常见问题
| 问题 | 回调 |
|---|---|
| 由活动 A 启动活动 B时,活动 A 的 onPause() 与 活动 B 的 onResume() 哪一个先执行? | 活动 A 的 onPause() 先执行,活动 B 的 onResume() 方法后执行 |
| 标准 Dialog 是否会对生命周期产生影响 | 没有影响 |
| 全屏 Dialog 是否会对生命周期产生影响 | 没有影响 |
| 主题为 Dialog 的 Activity 是否会对生命周期产生影响 | 有影响,与跳转 Activity 一样 |
2.3 异常情况:资源配置相关
- 资源相关的系统配置发生改变导致
Activity被杀死并重新创建
分析:当系统配置发生更改后,Activity会被销毁,其onPause、onStop、onDestroy都会被调用,由于Activity是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态(这个方法只会出现在Activity异常终止的情况下,正常情况下不会调用这个方法)。
当Activity被重新创建后,系统会调用onRestoreInstanceState,并且把Activity销毁时onSaveInstanceState方法所保存的Bundle对象作为参数传递给onRestoreInstanceState和onCreate方法。
因此,可以通过onRestoreInstanceState和onCreate方法来判断Activity是否被重建了,如果被重建了,我们就可以取出之前保存的数据并恢复,从时序上来说,onRestoreInstanceState的调用时机在onStart之后。
2.4 异常情况:资源内存不足
- 资源内存不足导致优先级低的Activity被杀死
Activity优先级从高到低可以分为以下三种:
前台Activity ——正在和用户交互的Activity,优先级最高。
可见但并非前台Activity——比如Activity中弹出一个对话框,导致Activity可见,但是位于后台无法和用户交互。
后台Activity——已经被暂停的Activity,比如执行了onStop,优先级最低。
当系统内存不足时,系统就会按照上述优先级去杀死目标Activity所在的进程,并在后续onSaveInstanceState和onRestoreInstanceState来存储和恢复数据。
2.5 异常情况下的处理
在发生异常情况后,用户再次回到 Activity,原 Activity 会重新建立,原已有的数据就会丢失,比如用户操作改变了一些属性值,重建之后用户就看不到之前操作的结果,在异常的情况下如何给用户带来好的体验,有两种办法。
2.5.1 数据保存
第一种就是系统提供的 onSaveInstanceState 和 onRestoreInstanceState 方法,onSaveInstanceState 方法会在 Activity 异常销毁之前调用,用来保存需要保存的数据,onRestoreInstanceState 方法在 Activity 重建之后获取保存的数据。
在活动异常销毁之前,系统会调用 onSaveInstanceState,可以在 Bundle 类型的参数中保存想要的信息,之后这个 Bundle 对象会作为参数传递给 onRestoreInstanceState 和 onCreate 方法,这样在重新创建时就可以获取数据了。
关于 onSaveInstanceState 与 onRestoreInstanceState 方法需要注意的一些问题:
1. onSaveInstanceState 方法的调用时机是在 onStop 之前,与 onPause 没有固定的时序关系。而 onRestoreInstanceState 方法则是在 onStart 之后调用。
2. 正常情况下的活动销毁并不会调用这两个方法,只有当活动异常销毁并且有机会重现展示的时候才会进行调用,除了资源配置的改变外,activity 因内存不足被销毁也是通过这两个方法保存数据。
3. 在 onRestoreInstanceState 和 onCreate 都可以进行数据恢复工作,但是根据官方文档建议采用在 onRestoreInstanceState 中去恢复。
4. 在 onSaveInstanceState 和 onRestoreInstanceState 这两个方法中,系统会默认为我们进行一定的恢复工作,具体地讲,默认实现会为布局中的每个 View 调用相应的 onSaveInstanceState() 方法,让每个视图都能提供有关自身的应保存信息。Android 框架中几乎每个小部件都会根据需要实现此方法,以便在重建 Activity 时自动保存和恢复付 UI 所做的任何可见更改。例如 EditText 中的文本信息、ListView 中的滚动位置等。也可以通过 android:saveEnabled 属性设置为 “false” 或通过调用 setSaveEnabled() 方法显式阻止布局内的视图保存其状态,通常不会将该属性停用,除非想要以不同方式恢复 Activity IU 的状态。
5. onSveInstanceState() 常见的触发场景有:横竖屏切换、按下电源键、按下菜单键、切换到别的 Activity 等;onRestoreInstanceState() 常见的触发场景有:横竖屏切换、切换语言等等。
2.5.2 防止重建
在默认情况下,资源配置改变会导致活动的重新创建,但是可以通过对活动的 android:configChanges 属性的设置使活动防止重新被创建。
| 属性值 | 含义 |
|---|---|
| mcc | SIM 卡唯一标识IMSI(国际移动用户标识码)中的国家代码,由三位数字组成,中国为:460,这里标识 mcc 代码发生了变化 |
| mnc | SIM 卡唯一标识 IMSI(国际移动用户标识码)中的运营商代码,有两位数字组成,中国移动 TD 系统为 00 ,中国联通为 01,电信为 03,此项标识 mnc 发生了改变 |
| locale | 设备的本地位置发生了改变,一般指的是切换了系统语言 |
| touchscreen | 触摸屏发生了改变 |
| keyboard | 键盘类型发生了改变,比如用户使用了外接键盘 |
| keyboardHidden | 键盘的可访问性发生了改变,比如用户调出了键盘 |
| navigation | 系统导航方式发生了改变 |
| screenLayout | 屏幕布局发生了改变,很可能是用户激活了另外一个显示设备 |
| fontScale | 系统字体缩放比例发生了改变,比如用户选择了个新的字号 |
| uiMode | 用户界面模式发生了改变,比如开启夜间模式 -API8 新添加 |
| orientation | 屏幕方向发生改变,比如旋转了手机屏幕 |
| screenSize | 当屏幕尺寸信息发生改变(当编译选项中的 minSdkVersion 和 targeSdkVersion 均低于 13 时不会导致 Activity 重启 ) API 13 新添加 |
| smallestScreenSize | 设备的物理尺寸发生改变,这个和屏幕方向没关系,比如切换到外部显示设备 -API13 新添加 |
| layoutDirection | 当布局方向发生改变的时候,正常情况下无法修改布局的 layoutDirection 的属性 -API17 新添加 |
可以在属性中声明多个配置值,方法使用 “|” 字符分割这些配置值。
三、启动模式
3.1 区别
3.2 设置
1. 在 AndroidMainifest 设置
<activity
android:launchMode="启动模式"
//属性
//standard:标准模式
//singleTop:栈顶复用模式
//singleTask:栈内复用模式
//singleInstance:单例模式
//如不设置,Activity的启动模式默认为**标准模式(standard)**
</activity>
2.通过Intent设置标志位
Intent inten = new Intent (ActivityA.this,ActivityB.class);
intent,addFlags(Intent,FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
3.3 standard
- 简单的栈的进栈和出栈,先进后出
3.4 singleTop
- 栈顶复用模式意思就是说如果这个activity是在栈顶,再次启动这个activity就直接复用,不创建新的activity
3.5singleTask
- 与singleTop类似,只不过singleTop只是复用栈顶的元素,而singleTask可以复用栈内的所有元素,当启动的activity在栈内已经存在时,singleTask模式直接弹出目标activity之上的所有元素,讲目标元素置于栈顶。
activity1是singleTask模式,当再次启动activity1的时候,栈内已经存在目标activity(activity1),则弹出activity1之上的所有activity,讲目标置于栈顶。
应用场景:
程序主界面,我们肯定不希望主界面被多创建,而且在主界面退出的时候退出整个App是最好的设想。
耗费系统资源的Activity:对于那些及其耗费系统资源的Activity,我们可以考虑将其设为singleTask模式,减少资源耗费(在创建阶段耗费资源的情况,个人理解-。+)
3.6 singleInstance
在该模式下,我们会为目创建一个新的Task栈,将目标Activity放入新的Task,并让目标Activity获得焦点。新的Task有且只有这一个Activity实例。 如果已经创建过目标Activity实例,则不会创建新的Task,而是将以前创建过的Activity唤醒(对应Task设为Foreground状态)。
为了方便理解,也为了与standard模式融会贯通,我在下图中activity1和activity2使用的是standard模式,而activity3使用的是singleInstance模式
四、Intent
在Android中,Activity的启动是通过Intent来表达的,Intent是组件之间通信的媒介,专门提供组件互相调用的相关信息
4.1 显示启动
第一种:class 跳转
// 1. 实例化显式Intent & 通过构造函数接收2个参数
// 参数1 = Context:启动活动的上下文,一般为当前Activity
// 参数2 = Class:是指定要启动的目标活动
Intent intent = new Intent(Activity.this,Activity2.class);
// 2. 通过Activity类的startActivity()执行该意图操作(接收一个Intent对象)
// 将构建好的Intent对象传入该方法就可启动目标Activity
startActivity(intent);
第二种:包名.类名跳转
Intent intent = new Intent();
intent.setClassName(FirstActivity.this,"com.xiaozeng.launchapplication.SecondActivity");
startActivity(intent);
第三种:ComponentName跳转
Intent intent = new Intent()
ComponentName componentName = new ComponentName(FirstActivity.this,SecondActivity.class);
intent.setComponent(componentName);
startActivity(intent);
4.2 隐式启动
隐式启动并不明确指出想要启动的哪一个活动,而是指定了一系列的action和category等信息,然后由系统去分析这个Intent,并帮我们找出合适的活动去启动
第一步:在AndroidManifest.xml文件中 定义action和category属性
第二步:在java 文件中写入逻辑代码
详细说明
声明条件含:动作(Action)、类型(Category)、数据(Data)
五、 数据传递
5.1 使用方式
- startActivity();
- startActivityForResult();
- 自定义方法 actionStart(), 最佳数据传值方法
- 通过 Bundle 传递数据
5.2 可传递的数据类型
- 8 种基本数据类型(boolean、 byte、 char、 short、 int、 long、 float、 double)、String
- Intent、Bundle
- Serializable对象、Parcelable及其对应数组、CharSequence 类型
- ArrayList,泛型参数类型为:、<? Extends Parcelable>
5.3 startActivity
// 在MainActivity中定义如下代码
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new
Intent(MainActivity.this,TwoActivity.class);
//在Intent对象当中添加一个键值对
intent.putExtra("key","value");
startActivity(intent);
}
});
// 在TwoActivity中定义如下代码
public class TwoActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
button2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//取得从上一个Activity当中传递过来的Intent对象
Intent intent = getIntent();
//从Intent当中根据key取得value
if (intent != null) {
String value = intent.getStringExtra("key");
}
}
});
}
}
5.4 startActivityForResult
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,TwoActivity.class);
intent.putExtra("key", "value");
// 第二个参数是请求码,只要是一个唯一值
startActivityForResult(intent, 1234);
}
});
// 由于我们是使用startActivityForResult()方法来启动TwoActivity的,在TwoActivity被销毁之后会回调上一个活动的onActivityResult()方法
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
switch (requestCode) {
case 1234:
if (resultCode == RESULT_OK) {
//接收对象
//Bundle bundle = data.getExtras();
//AddressBean addressBean = (AddressBean) bundle.getSerializable(Constant.ADDRESSBEAN);
String returnedData = intent.getStringExtra("key1");
}
break;
default:
}
}
public class TwoActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//接收从MainaActivity传递的数据
Intent intent = getIntent();
if (intent != null) {
String value = intent.getStringExtra("key");
}
button2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//传递对象
//Intent intent = new Intent();
//Bundle bundle = new Bundle();
//bundle.putSerializable(Constant.ADDRESSBEAN, addressArray.get(position));
//intent.putExtras(bundle);
//setResult(RESULT_OK, intent);
//finish();
Intent intent = new Intent();
intent.putExtra("key1","value two activity");
// 专门用于向上一个活动返回数据。第一个参数用于向上一个活动返回结果码,一般只使用RESULT_OK或RESULT_CANCELED这两个值
setResult(RESULT_OK, intent);
finish();
}
});
}
}
5.5 自定义方法 actionStart(); 最佳数据传值方法
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
/*最佳数据传值方法:调用在下个activity自定义的方法*/
TwoActivity.actionStart(MainActivity.this, "data1", "data2");
}
});
public class TwoActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
//最佳数据传值方法
public static void actionStart(Context context, String data1, String data2) {
Intent intent = new Intent(context, TwoActivity.class);
intent.putExtra("param1", data1);
intent.putExtra("param2", data2);
context.startActivity(intent);
}
}
5.6 通过 Bundle 传递数据
// 1. 数据传递
// a. 创建Intent对象(显示Intent)
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
// b. 创建bundle对象
Bundle bundle = new Bundle();
// c. 放入数据到Bundle
bundle.putString("name", "carson");
bundle.putInt("age", 28);
// d. 将Bundle放入到Intent中
intent.putExtras(bundle);
// e. 启动Activity
startActivity(intent);
// 2. 数据取出(在被启动的Activity中)
// a. 获取用于启动SecondActivit的Intent
Intent intent = getIntent();
// b. 通过Intent获取bundle
Bundle bundle = intent.getExtras();
// c. 通过bundle获取数据传入相应的键名,就可得到传来的数据
// 注意数据类型 与 传入时保持一致
String nameString = bundle.getString("name");
int age = bundle.getInt("age");
5.7 putExtra和Bundle
Bundle 意为 捆绑 的意思,更多适用于:
-
连续传递数据
若需实现连续传递:Activity A -> B -> C;若使用putExtra(),则需写两次intent = A->B先写一遍 + 在B中取出来 & 再把值重新写到Intent中再跳到C;若使用 Bundle,则只需取出 & 传入 Bundle对象即可 -
可传递的值:对象
putExtra()无法传递对象,而Bundle则可通过putSerializable传递对象
// 如传递User类的对象
public class User implements Serializable {
...
}
// 传递时
User user = new User();
Intent intent = new Intent(MyActivity.this,OthereActivity.class);
Bundle bundle = new Bundle();
bundle.putSerializable("user", user);
intent.putExtras(bundle);
putExtra() 更多使用于单次传递、传递简单数据类型的应用场景
- 引用