理解活动的生命周期可以帮助写出更加连贯流畅的程序,并更好管理资源。最终获得更好的用户体验。
Android使用任务(Task)来管理活动的,一个任务就是一组存放在返回栈(Back Stack)里的活动的集合。每个应用程序都有自己的返回栈,大多数情况下,一个应用程序就是一个任务。(参考活动的启动模式一节。)
Activity定义了7个回调方法,覆盖活动生命周期的每一个环节。
onCreate()
:活动第一次被创建时调用。要在此方法中完成活动的初始化操作,比如加载布局、绑定事件。onStart()
:在活动由不可见变为可见时调用。onResume()
:在活动准备好和用户进行交互时候调用。此时活动一定处于栈顶并处于运行状态。《《《《Resume是重返重新开始的意思》》》》onPause()
:系统准备启动或者恢复另一个活动的时候调用。会将一些消耗CPU的资源释放掉,并保存一些关键数据。此方法执行速度一定要快,不然会影响新的栈顶活动的使用。onStop()
:在活动完全不可见时候调用。onPause()
方法会得到执行,而onStop()
方法并不会执行。onDestroy()
:活动被销毁前调用。之后活动变为销毁状态。onRestart()
:活动由停止状态变为运行状态之前调用,也就是活动被重启了。除了Restart,都是两两相对,可以将活动分为三种生存期:
onCreate()
(完成初始化)和 onDestroy()
(释放内存)方法之间所经历的就是完整生存期。onStart()
和onStop()
方法之间。此时活动对于用户总是可见的,即便有可能无法和用户进行交互。onStart()
方法中对资源进行加载,在onStop()
方法中对资源进行释放,从而保证处于停止状态的活动不会占用过多内存。onResume()
和onPause()
方法之间。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a normal activity"
/>
LinearLayout>
编辑dialog_layout.xml文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a dialog activity"
/>
LinearLayout>
编辑AndroidManifest.xml文件,修改
标签的配置:
<activity android:name=".DialogActivity"
android:theme="@android:style/Theme.Dialog"></activity>
修改activity_main.xml,重新定制主活动布局。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/start_normal_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start NormalActivity"/>
<Button
android:id="@+id/start_dialog_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start DialogActivity"/>
LinearLayout>
修改MainActivity中的代码:
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startNormalActivity = (Button) findViewById(R.id.start_normal_activity);
Button startDialogActivity = (Button) findViewById(R.id.start_dialog_activity);
startNormalActivity.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, NormalActivity.class);
startActivity(intent);
}
});
startDialogActivity.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, DialogActivity.class);
startActivity(intent);
}
});
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart");
}
@Override
protected void onPostResume() {
super.onPostResume();
Log.d(TAG,"onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG,"onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG,"onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG,"onDestroy");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG,"onRestart");
}
}
《《《《《哭了呀为啥出bug了明明都一样》》》》》
当一个活动进入停止状态,有可能会被回收。一旦回收,当Back键返回时,会执行onCreate()
方法而不会执行onRestart()
方法。也就是原有的数据都不会保存。
解决:onSaveInstanceState()
方法会携带一个Bundle类型的参数,Bundle提供方法保存数据。
putString()
保存字符串;putInt()
保存整型数据;以此类推。
每个保存方法传入两个参数:一是键,用于后面从Bundle取值;二是要保存的内容。
数据的恢复:onCreate()
方法含有Bundle理性参数,一般情况下是null。
if (savedInstanceState != null) {
String tempData = savedInstanceState.getString("data_key");
Log.d(TAG, tempData);
}
这样加上if判断可以保证参数包含Bundle时能够将数据读出。
另,Intent可以结合Bundle一起用于传递数据——首先将数据保存在Bundle对象中,再将Bundle对象存放在Intent里;到目标活动后先从Intent去除Bundle,再从Bundle中一一取出数据。
实际项目中应当根据特定的需求为每个活动指定恰当的启动模式。
可以在AndroidManifest.xml中通过给
标签指定Android:launchMode
属性来选择启动模式。
启动模式分为standard、singleTop、singleTask、singleInstance四种。
活动默认的启动模式,没有显示指定时都会自动使用这种启动模式。
standard启动模式下,每当启动一个新的活动,不管活动是否已经存在于返回栈中,都会创建活动的一个新的实例,将其在返回栈中入栈,并使其处于栈顶位置。
启动活动时,如发现返回栈栈顶已经是该活动,则不会再创建新的活动实例。
保证活动在整个应用程序的上下文中只存在一个实例。
当活动启动模式指定为singleTask时,每次启动该活动,系统首先会在返回栈中检查是否存在该活动实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动。
指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动。
应用场景:程序中有一个活动允许其他程序调用的。(每个应用程序都会有自己的返回栈,同一个活动在不同的返回栈中入栈时必然是创建了新的实例。)在singleInstance模式下会有一个单独的返回栈来管理这个活动,不管哪一个应用程序来访问这个活动,都共用同一个返回栈,也就解决了共享活动实例的问题。
getClass().getSimpleName()
获取当前实例的类名。
例子:
在ActivityTest项目的基础上修改,新建一个BaseActivity类。重写类如下:
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity", getClass().getSimpleName());
}
}
而后让Activity继承BaseActivity类而不再继承AppCompatActivity类。此时进入任何活动都会在日志中打印当前活动名称。
当连续进入三个活动时,往往需要按三次Back键才能退出程序。需要一个注销或者退出的功能。
解决:用一个专门的集合类对所有的活动进行管理。
尝试一下:
新建一个ActivityCollector类作为活动管理器:
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();
}
}
}
}
在BaseActivity中加以运用:
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity", getClass().getSimpleName());
ActivityCollector.addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
}
所以在任何地方退出程序只需要调用ActivityCollector.finishAll()
方法就可以了。
也可以通过android.os.Process.killProcess(android.os.Process.myPid())
方法杀掉当前进程。
killProcess()
方法用于杀掉一个进程,接收一个进程id参数。只能杀掉当前程序的进程。myPid()
方法可以获得当前程序的进程id。以上所学到的,使用Intent构建意图然后调用atartActivity()
或者startActivityForResult()
方法将活动启动起来,但在开发项目中此种方式会导致很多的对接工作。最佳写法:将所要传递的内容以及启动活动方法的调用都包装在一个函数中,将要传递的数据以及上下文作为参数传入其中。
例:
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:
SecondActivity.actionStart(FirstActivity.this,"data1", "data2");