Android——探究活动2
1.3活动的生命周期
1.3.1返回栈
android是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合,这个栈也被称作返回栈(Back Stack),默认情况下每当我们启动一个新活动,他会在返回栈中入栈,并处于栈顶位置,每当我们按下Back键或调用finish()方法去销毁一个活动时,处于栈顶的活动会出栈,这时前一个入站的活动会重新处于栈顶的位置,系统总是会显示处于栈顶的活动给用户。
1.3.2活动状态
1.运行状态
当一个活动处于栈顶时,这时活动处于运行状态,系统最不愿回收的就是处于运行状态的活动。
2:暂停状态
当一个活动不处于栈顶,但仍然可见时,就会进入暂停状态,比如对话框式的活动只会占据屏幕中间的部分区域,能看见后面的活动就处于暂停状态。处于暂停状态的活动是完全活着的,系统也不会回收,只有在内存极低的情况下才会去考虑回收,
3.停止状态
当不在处于栈顶,并且完全不可见时,就进入停止状态,系统仍然会为这种活动保存相应的状态和成员变量,但并不可靠,当其他地方需要内存时,处于停止状态的活动有可能被系统回收。
4.销毁状态
当一个活动从返回栈中移除之后就变成销毁状态,系统最倾向于回收这种状态的活动,从而保存手机内存充足。
1.3.3活动的生存期
Activity类中定义了七个回调方法,覆盖了活动生命周期的每一个环节。
1.onCreate():在活动第一次被创建的时候调用,应该在这个方法中完成活动的初始化操作,比如加载布局,绑定事件。
2.onStart():在活动由不可见变为可见时调用。
3.onResume():在活动准备好和用户进行交互时调用,此时活动一定位于返回栈的栈顶,并且处于运行状态。
4.onPause():在系统准备去启动或者恢复另一个活动时调用,通常在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。
5.onStop():在活动完全不可见时调用,和onPause()的主要区别在于,如果启动的活动是一个对话框式的活动,那么onPause()方法会得到执行,而onStop()不会执行。
6.onDestroy():在活动被销毁之前调用,之后活动的状态将变为销毁状态。
7.onRestart():在活动由停止变为运行状态之前调用,也就是活动被重新启动了。
以上七个方法除了onRestart(),其他都是两两相对的,所以可以将活动分为3种生存期。
1.完整生存期:活动在onCreate()和onDestroy()之间经历的,是完整生存期,一般情况,一个活动会在onCreate()方法里完成各种初始化操作,在onDestroy()中完成释放内存的操作。
2.可见生存期:活动在onStart()和onStop()之间经历的,是可见生存期,在可见生存期里,活动对于用户是可见的,即便有可能无法和用户进行交互。我们可以在onStart()方法中对资源进行加载,在onStop()方法中对资源进行释放,从而保证处于停止状态的活动不会占用过多内存。
3.前台生存期:活动在onResume()和onPause()之间经历的就是前台生存期,活动总是处于运行状态,可以和用户进行交互,我i们平时看到和接触最多的也是这个状态下的活动。
1.4活动被回收了怎么办
Activity中提供了一个onSaveInstanceState()回调方法,这个方法可以保证在活动回收之前一定会被调用,我们可以通过这个方法来解决活动被回收时临时数据得不到保存的问题。
onSaveInstanceState()方法会携带一个Bundle类型的参数,Bundle提供了一系列的方法用于保存数据,比如可以使用putString()方法保存字符串,使用putInt()保存整数,以此类推。每个保存方法需要传入两个参数,第一个参数是键,用于后面从Bundle中取值,第二个参数是真正要保存的内容。
在MainActivity中添加如下代码就可以将临时数据保存:
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Log.d(TAG,"onCreate");
setContentView(R.layout.activity_main);
if(savedInstanceState!=null){
String tempData = savedInstanceState.getString("data_key");
Log.d(TAG,tempData);
}
}
补充:Intent可以结合Bundle一起传数据,首先可以把需要传递的数据都保存在Bundle对象中,然后再将Bundle对象存放在Intent里,到了目标活动后先从Intent中取出Bundle,再从Bundle中取出数据。
1.5 活动的启动模式
在实际项目中我们应该根据特定的需求为每个活动指定恰当的启动模式,起订模式一共有四种,standard,singleTop,singleTask和singleInstance。可以在AndroidManifest.xml中通过给< activity >标签指定android:launchMode属性来选择启动模式。
1.5.1 standard
standard是默认的启动模式,上一节我们知道Android通过返回栈来管理活动,在standard模式下,每启动一个新活动,他会在返回栈入栈,并处于栈顶位置,对于使用standard模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例。
1.5.2 singleTop
该模式,在启动活动时如果发现返回栈的栈顶已经是该活动,则认为可以直接使用他,不会再创建新的实例。不过当该活动不在栈顶时,还是会创建新的实例。
1.5.3 singleTask
每次启动活动时,系统会首先在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把这个活动之上的所有活动统统出栈,如果没有发现,则创建一个新的活动实例
1.5.4 singleInstance
指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动(其实如果singleTask模式指定了不同的taskAffinity,也会启动一个新的返回栈)。这种模式下会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用的同一个返回栈,也就解决了共享活动实例的问题。
1.6活动的最佳实践
1.6.1知晓当前是哪一个活动
在ActivityTest基础上新建一个BaseActivity类,此时并不需要在AndroidManifest.xml中注册,所以选择创建一个普通的java类,让其继承自AppCompatActivity,并重写onCreate()方法。
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Log.d("BaseActivity",getClass().getSimpleName());
}
}
让BaseActivity成为其他活动的父类,让其他活动都继承BaseActivity,由于BaseActivity又继承自AppCompatActivity,项目中所有活动的功能都不受影响,完全继承了Activity中的所有特性。
1.6.2 随时随地退出程序
只需要一个专门的集合类对所有的活动进行管理就可以了。
public class ActivityCollector {
public static List<Activity> activities = new ArrayList<>();
public static void addActivity(Activity activity){
activities.add(activity);
}
public static void removeActivity(Activity activity){
activities.remove(activity);
}
public static void finishAll(){
for(Activity activity:activities){
if(!activity.isFinishing()){
activity.finish();
}
}
activities.clear();
}
在活动管理器中,通过一个List来暂存活动,然后提供了addActivity()和removeActivity()方法用于向List中添加,移除活动,最后提供了一个finishAll()方法用于将List中存储的活动全部销毁。
接下来修改BaseActivity中的代码
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Log.d("BaseActivity",getClass().getSimpleName());
ActivityCollector.addActivity(this);
}
@Override
protected void onDestroy(){
super.onDestroy();
ActivityCollector.removeActivity(this);
}
}
在onCreate()中调用了addActivity(),表明将当前正在创建的活动添加到活动管理器里,然后重写了onDestroy()方法,并调用了ActivityCollector的removeActivity()方法,表明将一个马上要销毁的活动从管理器里移除。
从此,不管想在什么地方退出程序,只需调用ActivityCollector。finishAll()方法就可以了。
补充:killProcess()方法只能用于杀掉当前程序的进程,不能杀掉其他程序
1.6.3 启动活动的最佳写法
在两个活动之间传递字符串时,我们很容易写出这样的代码
Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("param1","data1");
intent.putExtra("param2","data2");
startActivity(intent);
在真正项目开发时,经常会有对接的问题出现,比如SecondActivity并不是由你开发,不清楚启动SecondActivity这个活动需要传递哪些数据,此时换一种写法,修改SecondActivity中的代码:
public class SecondActivity extends BaseActivity{
public static void actionStart(Context context,String data1,String data2){
Intent intent = new Intent(context,SecondActivity.class);
intent.putExtra("param1","data1");
intent.putExtra("param2","data2");
context.startActivity(intent);
}
}
我们在SecondActivity中添加了一个actionStart()方法,在这个方法中完成了Intent的构建,另外所有的SecondActivity中需要的数据都是通过actionStart()方法的参数传递过来的,然后把它们储存在Intent中,最后调用startActivity()方法启动SecondActivity,这样可以清晰知道启动SecondActivity需要传递哪些数据。现在只需要一行代码就可以启动SecondActivity
button.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v){
SecondActivity.actionStart(FirstActivity.this,"data1","data2");
}
});