一、onCreate
onCreate和onDestroy可以说是使用最频繁的了,因为在一个项目中经常会开关页面。而onCreate是生命周期的第一站,它的作用就是给开发者用来对Activity的实例对象中的成员做初始化的。Android为了方便对Activity的管理和使用,对Activity进行了进一步封装,直接new出来的Activity对象是不会委托给Android管理的,就和普通java文件无异。当然,也就失去了Activity本身的界面展示、交互的意义。
在onCreate中的操作一般和view的初始化相关,诸如addView、setContentView和对view中数据的填充(setText等)。当然这些操作也可以在onStart、onResume中进行,但是在onCreate中能保证只被调用一次,别的地方保不齐会被调用N多次。
在onCreate中有一个相当重要的参数——savedInstanceState,该参数的作用就在于可以帮助开发者在页面非正常销毁(如内存不足、横竖屏切换、系统语言改变等)之前保存一些数据(自动保存的、开发者主动保存的),并且会在Activity重新创建时恢复。因此,对应savedInstanceState参数来讲,onCreate的作用至关重要。
相对应的,在Activity主动finish或者设置启动模式下的销毁,属于Activity的正常销毁。
二、savedInstanceState
savedInstanceState会默认保存Edittext中的内容、CheckBox的Check状态、Fragment实例等数据。以fragment为例,如果我们在onCreate中对fragment手动newInstance,这样在Activity异常重启时,就会出现Double分量的fragment实例,显然是不对的。哪怕是对savedInstanceState判空处理,也行!
if (savedInstanceState != null) {
// 这里根据自己需要去从savedInstanceState中去数据
Fragment fragment = getSupportFragmentManager().findFragmentByTag(FragmentBase.class.getName());
if (fragment instanceof FragmentBase) {
FragmentBase base = (FragmentBase) fragment;
}
} else {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, FragmentBase.newInstance(this, "One", FragmentBase.class.getName()))
.add(R.id.container, FragmentFour.newInstance(this, "Two", FragmentFour.class.getName()))
.add(R.id.container, FragmentFive.newInstance(this, "Three", FragmentFive.class.getName()))
.commit();
}
三、onDestroy
走到这一步,代表Activity即将要被销毁,不管它是否是正常关闭。在这个方法内,我们做的最多的就是释放一些资源,为了防止造成Activity引用异常、资源泄漏等问题。
1、异步任务造成资源泄漏
我们都知道handler、Thread这些,会使得异步任务的生命周期与Activity的生命周期不同步,而造成资源泄漏。
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
tv.setText("hello world!");
}
}, 2000);
handler.obtainMessage(1).sendToTarget();
不管怎么发送message,都会或直接、或间接的引用到当前所在的Activity实例对象。如果该Activity已经被finish掉了,可是message还在主线程的消息队列中,结果就是Activity实例对象无非被GC,从而引起内存泄漏。所以在onDestory中需要把message从主线程的消息队列中移除。
@Override
protected void onDestroy() {
super.onDestroy();
if (handler != null) {
handler.removeCallbacksAndMessages(null);
}
}
2.异步任务引发异常
关于这个问题,笔者在项目开发过程中也碰到过。当时的业务场景是mapView上显示一个dialog,就出现了这一异常!
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
new AlertDialog.Builder(MainActivity.this).setMessage("Show Dialog").show();
}
}, 5000);
如果在5s内该Activity就被正常finish掉了,5s后dialog想依靠父窗体MainActivity来显示,如此一来就会引发异常。
final void performDestroy() {
mDestroyed = true;
mWindow.destroy();
mFragments.dispatchDestroy();
onDestroy();
mFragments.doLoaderDestroy();
if (mVoiceInteractor != null) {
mVoiceInteractor.detachActivity();
}
}
要想解决,可以在dialog显示时加一个判断,用来分辨父窗体是否还存在。
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (!MainActivity.this.isDestroyed()) {
new AlertDialog.Builder(MainActivity.this).setMessage("Show Dialog").show();
}
}
}, 5000);
关于isDestroyed的源码:
/**
* Returns true if the final {@link #onDestroy()} call has been made
* on the Activity, so this instance is now dead.
*/
public boolean isDestroyed() {
return mDestroyed;
}
isDestroyed要求最低的api是17,可以立一个flag判断当前Activity的状态。
四、onStart、onRestart和onStop
- 对数据的实时性要求:点击列表进入详情,finish返回列表时为保证列表数据的实时性,需要拉取最新的数据并填充。可以放在onStart中,也可以放onRestart中,但是onRestart在Activity首次加载启动时并不会被调用。
- 动画效果:Activity1进入到Activity2中,由于Activity1已经不可见了动画没必要继续进行了,可以在onStop中停止动画以节省资源
五、onResume和onPause
这两个回调接口的使用频率要稍高一些,比方说是否获得焦点。
- 结束占用CPU的动画、其他正在运行的任务
@Override
protected void onPause(){
mMapView.onPause();
super.onPause();
}
@Override
protected void onResume(){
mMapView.onResume();
super.onResume();
}
- 视频播放,当视图组件获得焦点时,即onResume中播放视频,当视图组件失去焦点时,即onPause中暂停播放视频。
- 保存重要数据,为了防止App被意外强杀,一般会在onPause中将一些重要数据保存到本地。
六、Activity的4种启动模式
1.Standard
默认启动模式,每当我们需要开启一个新的Activity页面时系统都会新建一个Activity实例对象,然后开启上面说的Activity的生命周期流程之旅,onCreate->onStart->onResume。
2.SingleTop
当MainActivity的实例已经存在于栈顶,系统不会重新创建实例,而是进入一个特殊的方法onNewIntent,具体流程为:onNewIntent->onResume。这种场景还是比较多的,比方说商品详情页面一般都会有相关的商品推荐,点击推荐的某个商品后进入的还是一个商品详情页面,这个时候就不需要重新再创建一个新的商品详情Activity页面,直接复用已有的页面,刷新下View中的数据就好了。
3.SingleTask
保证栈中每个Activity都只有一个实例,当通过startActivity启动Activity A时,如果当前Task栈中已经存在一个Activity A的实例,那么不再重新创建一个新的Activity实例,而是直接复用该实例,进入该Activity的onNewIntent方法,同时将位于Activity A实例之上的所有Activity弹出Task栈并销毁。这种场景在IM应用中使用的比较多,比如QQ或者微信的聊天页面,当从聊天页面进入其他页面,然后在重新进入聊天页面时就会直接进入原来的聊天页面,同时销毁中间新创建的Activity页面,并刷新聊天页面的数据。
4.SingleInstance
设置该模式可以保证该Activity所在的Task中有且仅有一个activity实例,当通过startActivity启动Activity A时,如果该Activity的实例已经存在,那么不再重新创建一个新的Activity实例,而是直接复用该实例,进入该Activity的onNewIntent方法。这种场景出现的比较少,该Activity在整个系统只有一个实例,一般用于系统应用,并且可以被其他应用共享使用(有点类似于操作系统概念中的临界资源),比方说来电呼叫页面,在整个系统中就只能有一个,因为同一时刻只能存在一个电话呼叫。
关于启动模式的设置,一般会在清单文件进行配置,也可在代码中动态设置:
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
一般将Intent.FLAG_ACTIVITY_NEW_TASK和Intent.FLAG_ACTIVITY_CLEAR_TOP搭配使用实现类似SingleTask的效果,将Intent.FLAG_ACTIVITY_NEW_TASK和Intent.FLAG_ACTIVITY_SINGLE_TOP搭配使用实现类似SingleTop的效果。