1. Activity的生命周期
Activity说的通俗一点,一个Activity对应手机屏幕上的一个页面,就像一个网页一样。Activity什么时候能被看到?是不是一锁屏,Activity就要被销毁释放?要回答这些问题,就涉及到Activity的生命周期。
1. Activity任务栈
android系统用任务栈来管理Activity。当创建一个新的Activity后,此Activity将被加入到任务栈的栈顶,从而先前在栈顶的Activity就被新建Activity压到下一层,其他Activity依次往下移动一层;反之,当栈顶的Activity被销毁移出任务栈后,任务栈下一层的Activity就会依次上升一层。
2. Activity四种状态
1) 运行状态(Running):当Activity位于栈顶时,此时Activity对用户可见并且可交互;
2) 暂停状态(Paused):当Activity不在栈顶,但对用户仍然可见,此时Activity界面失去了焦点无法与用户进行交互,例如,栈顶的Activity是透明的或者栈顶Activity并不是铺满整个手机屏幕;
3) 停止状态(Stopped):此时Activity界面对用户完全不可见也不可交互;
4) 销毁状态(Destroyed):此时Activity界面被销毁,等待资源被回收。
3. Activity生命周期
在每个不同的状态阶段,Android系统会自动对Activity内相应的方法进行回调,调用顺序看下图:
说明:
1. 从Activity创建到运行态,系统会依次回调如下方法:onCreate->onStart->onResume。
2. onCreate和onDestroy配对,onStart和onStop配对,onResume和onPause配对。
1)onCreate和onDestroy:在回调onCreate之后,系统会为Activity分配资源,在调用onDestroy之后系统才能回收这些资源。因此,界面初始化工作一般都放在onCreate里完成,而释放资源的工作应该放在onDestroy里。
2)onStart和onStop:当Activity执行onCreate后,界面对用户依然是不可见的,只有执行onStart回调方法后,Activity才对用户可见;直到系统回调onStop方法后,Activity对用户才不可见了。
3)onResume和onPause:当Activity执行onResume回调方法时,Activity就可以响应用户交互;当回调onPause方法后,Activity就不再响应用户交互。
3. 其它状态回到运行态:
1) 当前如果处于onPause状态,那么要回到运行状态系统就只会回调:onResume。
2) 当前如果处于onStop状态,那么要回到运行状态系统就会依次回调:onRestart->onStart->onResume
3) 当前如果处于onDestory状态,那么要回到运行状态系统就会依次回调:onCreate->onStart->onResume
4. 打开新的Activity2的时候,先前在栈顶的Activity1,这二者回调方法执行顺序如下
Activity1: onPause
Activity2: onCreate
Activity2: onStart
Activity2: onResume
Activity1: onStop
2. Activity启动模式
我们可能希望跳转到原来创建过的某个Activity实例,但是这个Activity实例如果不在任务栈的栈顶,这时就要再创建一个新的Activity实例并将其压入栈顶,这样就可能产生大量重复的 Activity,有时我们不希望这样。这时就需要我们为 Activity 配置特定的启动模式,而不是使用默认的启动模式。设置启动模式的位置在AndroidManifest.xml 文件中 Activity的android:launchMode 属性
1. standard模式
这是默认模式,无需设置,每次激活Activity时都会创建Activity实例,并放入任务栈中。相当于入栈,按back键返回到前一个Activity相当于退栈。
2. singleTop模式
如果在任务的栈顶正好存在该Activity的实例,就重用该实例( 会调用实例的 onNewIntent()),否则就会创建新的实例并放入栈顶,即使栈中已经存在该Activity的实例,只要不在栈顶,都会创建新的实例。可用来解决栈顶多个重复相同的Activity的问题
3. singleTask模式
如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的 onNewIntent() )。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移出栈。如果栈中不存在该实例,将会创建新的实例放入栈中。singleTask 模式可以用来退出整个应用。将主Activity设为SingTask模式,然后在要退出的Activity中转到主Activity,然后重写主Activity的onNewIntent函数,并在函数中加上一句finish。
4. singleInstance模式
在一个新栈中创建该Activity的实例,并让多个应用共享该栈中的该Activity实例。一旦该模式的Activity实例已经存在于某个栈中,任何应用再激活该Activity时都会重用该栈中的实例( 会调用实例的 onNewIntent() )。其效果相当于多个应用共享一个应用,不管谁激活该 Activity 都会进入同一个应用中。
3. Activity之间数据传递
3.1 通过Intent传递
这是一种比较规范的数据传递方式。首先,在要发送数据的Activity对象中创建一个Intent(意图)对象,然后可以将Integer、String、Float、Double等简单数据类型或可序列化对象(用户自己定义的序列化对象,记住自定义对象必须是序列化的)保存在Intent对象中,然后启动另一个,在另一个Activity中先获取Intent对象,再通过Intent对象来获得这些数据。示例代码如下:
1. 发送数据
1. 在当前要发送数据的Activity对象(即CurrentActivity)中,创建Intent对象
Intent intent =new Intent(CurrentActivity.this,OtherActivity.class);//CurrentActivity.this代表发送者,OtherActivity.class代表接收者
2. 将要发送的数据放入Intent对象,有两种方式:
a. 直接将数据放入Intent对象,以key-value对方式存入。获取数据时,是用key来获取value。有多个数据可以全部用这种方式逐个存入Intent对象。
intent.putExtra("age", 5); //前面的"age"是个key,5代表存入一个整数的value
intent.putExtra("price",1.5f); //再存入一个Float类型的数据
b. 先创建Bundle对象,将数据放入Bundle对象,然后将Bundle对象再放入Intent对象
Bundle bundle =newBundle();// 创建Bundle
bundle.putBoolean("male", true);//放入一个布尔型数据
bundle.putString("name","Jack"); //再放入一个字符串数据
intent.putExtras(bundle); //将Bundle对象存入Intent对象中
3. 启动新Activity(就是要接收数据的Activity)
startActivity(intent);
2. 获取数据
在新开启的Activity对象(对应上面的OtherActivity)中,获取Intent对象,然后通过Intent获取数据,也有两种方式:
a. 直接通过Intent对象来获取
Intent intent =getIntent(); // 获取上一个Activity传递过来的Intent对象,getIntent方法是Activity自带的
int age = intent.getIntExtra("age",0); //"age"这个key要与发送的一致才能接收到数据,后面的0表示如果读取失败时,就使用这个默认值,保证获取到的数据不为空。
boolean sex = intent.getBooleanExtra("male",true); //后面的true表示如果读取失败时的默认值
b. 先获取Bundle对象,在通过Bundle对象获取数据
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
String name = bundle.getString("name");
float price = bundle.getFloat("price", 0f);
注:不论前面用何种方式发送数据,接受数据时都可以用上面这两种方式的任意一种。
3. 传递自定义对象
首先,自定义对象必须是可序列化的,就是自定义对象必须是实现Serializable或者Parcelable(序列化细节,在此不展开了)。如下定义一个序列化对象User:
public class User implements Serializable{
private String name;
public User(String name){
this.name = name;
}
public String getName() {
return name;
}
publicvoid setName(String name) {
this.name = name;
}
}
发送对象时,可以这样:
User user = new User("Tom");
Intent intent = new Intent(SendActivity.this, ReceiveActivity.class);
Bundle bundle = new Bundle();
bundle.putSerializable("user", user);
intent.putExtras(bundle);
startActivity(intent);
接收时,可以这样获取User对象:
Intent intent = getIntent();
User user= (User)intent.getSerializableExtra("user");
4. 返回Activity的数据传递
上面的方法都是说从Activity1跳到新建的Activity2怎么传递数据,如果现在要关闭Activity2并返回到Activity1,这时要带数据回Activity1怎么办呢?android给Activity提供了一套方法来实现这个目的。
1. 从Activity1跳转到Activity2:
Intent intent =new Intent();
intent = intent.setClass(Activity1.this, Activity2.class);
Bundle bundle =new Bundle();
bundle.putString("name", "GG");
intent.putExtras(bundle);
startActivityForResult(intent,1249); //只有这里不同。这里的1249表示请求码,可以随意设置一个整数,如果有多个跳转到不同Activity的startActivityForResult方法被调用时,为了区分,每个跳转都要设置不同的请求码。
2. 从Activity2返回数据到Aactivity1:
Intent intent =new Intent(Activity2.this, Aactivity1.class);
Bundle bundle =new Bundle();
bundle.putInt("resultData","Activity2返回的数据");
intent.putExtras(bundle);
this.setResult(RESULT_OK, intent); //RESULT_OK是返回代表处理结果的状态码
this.finish(); //关闭Activity2
3. 在Activity1中重写Activity的onActivityResault方法,并在方法里接收数据:
@override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == 1249){//先判断请求码是否匹配
switch(resultCode) {//根据状态码,处理返回结果
case RESULT_OK:
Bundle bundle =data.getExtras();//获得数据
String result = bundle.getInt("resultData"); // 这个key要与Activity2中设置的一致
break;
......
default:break;
}
} else if(requestCode==xxxx){ //xxxx表示别的请求码
......
}
}
3.2 通过全局对象传递数据
如果某些数据需要经常使用,要长时间驻留内存(例如用户的基本信息,比如用户id、用户名、头像等,在程序运行时要经常用到),一般这种数据定义为全局对象,将该对象定义在Application类中。因为Application代表App本身,在App初始化时就会被创建,并且在程序运行过程中一直都在,可以保证数据对象不会被轻易释放。首先需要自定义一个类,该类继承Android的Application类,例如命名为MyApplication类;然后在MyApplication中定义数据对象,在编写完自定义的MyApplication类后,还需要打开AndroidManifest配置文件,在<application>标签中指定全局类名name为MyApplication,这样App启动时系统会使用我们自定义的MyApplication类来创建Application对象。
public class User{
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name= name;
}
}
public class MyApplication extends Application{
private User user;
public String getUser(){
return this.user;
}
public void setUser(String name){
this.user = new User();
this.user.setName(name);
}
}
public class Activity1 extends Activity{
private MyApplication application;
public void onCreate(Bundle savedInstanceState){
......
application = (MyApplication) getApplication();
application.setUser("Mary");
}
}
public class Activity2 extends Activity{
private MyApplication application;
private User user;
protected void onCreate(Bundle savedInstanceState){
......
application = (MyApplication) getApplication();
user= application.getUser();
}
}
这个在Activity1中初始化过的User对象,在任何可以获取Application对象的地方都可以再次获得这个User对象。
有些人说可以在Activity中使用静态变量来传递数据,个人建议尽量不要在此使用静态变量,这种操作很可能导致数据不一致,静态变量会打破对象的封装性。
5. Activity现场保存状态
一般情况下,调用Activity的onPause()和onStop()方法后,Activity实例其实仍然存在于内存中,Activity实例的所有信息和状态数据不会被销毁,当Activity重新回到运行态后,实例的状态数据都会得到保留。但是当系统内存不足时, 调用onPause()和onStop()方法后的Activity可能会在某个时刻被系统销毁,此时内存中没有该Activity的实例了,当用户重新打开这个Activity,就要新建一个实例,而这个实例就没有先前实例的状态数据。为了解决这个问题,我们可以在Activity被杀掉之前保存实例的当前状态,可以通过覆写Activity的onSaveInstanceState()方法来实现这个目的。
如果开发者没有覆写onSaveInstanceState()方法,此方法的默认实现会自动保存activity中的某些状态数据,比如activity中各种UI控件的状态。android应用框架中定义的几乎所有UI控件都恰当的实现了onSaveInstanceState()方法,因此当activity被摧毁和重建时,这些UI控件会自动保存和恢复状态数据。比如EditText控件会自动保存和恢复输入的数据,而CheckBox控件会自动保存和恢复选中状态。开发者只需要为这些控件指定一个唯一的ID(通过设置android:id属性即可),剩余的事情就可以自动完成了。如果没有为控件指定ID,则这个控件就不会进行自动的数据保存和恢复操作。所以复写onSaveInstanceState方法,多用来保存Activity中的一些成员变量值。
如果我们复写了onSaveInstanceState方法,调用过程大致如下:
1. onSaveInstanceState(Bundle outState)方法有一个Bundle类型的参数outState,可以将Activity的状态数据存储到这个Bundle对象中,onSaveInstanceState方法会在系统销毁Activity前被系统回调,并且在回调onPause()或onStop()方法之前调用;
2. 当该activity在将来某个时刻被重建时,系统在回调onCreate(Bundle savedInstanceState)或者onRestoreInstanceState(Bundle savedInstanceState)方法时,我们就可以用传进来的Bundle参数savedInstanceState来恢复Activity的状态。
public class MyActivity extends Activity{
private String restoreData;
//恢复数据
@Override public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
if(savedInstanceState !=null) {
restoreData= savedInstanceState.getString("restoreData");//获取要恢复的数据
}
//恢复数据
@Override
public void onRestoreInstanceState(Bundle saveInstanceState){
super.onRestoreInstanceState( saveInstanceState);
restoreData= saveInstanceState.getString("restoreData");
}
// 保存状态数据
@Override
protected void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putString("restoreData", restoreData);
}
……
}
注意:
1. onSaveInstanceState()方法只适合保存Activity与用户交互的瞬态数据,比如UI控件的状态,成员变量的值等。若要向数据库中插入记录等这种保存持久化数据的操作,应该放在onPause()中做。
2. onSaveInstanceState()不是Activity生命周期的方法,不是什么情况下都会被调用的。系统调用onSaveInstanceState遵循一个重要原则:即当系统“未经你许可”时销毁了你的Activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据。onSaveInstanceState方法在下面几种情况下会系统被调用:
1) 当用户按下HOME键时;
2) 长按HOME键,选择运行其他的程序时;
3) 按下电源按键(关闭屏幕显示)时;
4) 从Activity A中启动一个新的Activity时;
5) 屏幕方向切换时,例如从竖屏切换到横屏时。
网友评论