1、activity生命周期?
Activity生命周期大家应该都知道,这里就不在啰嗦了,下面看看Google官网提供的一张经典的生命周期流程图:
①当启动Activity时,系统会依次调用onCreate-onStart-onResume,此时Activity处于可见状态
②当Activity被kill或者调用了本身的finish方法时,依次会调用onPause-onStop-onDestroy方法
下面来看看几个特殊情景下Activity的生命周期
2、点击home键、返回键、电源键时activity生命周期
下面结合一个实例,来看一下这几种状态下Activity的生命周期,实例很简单,定义一个BaseActivity类,重写了Activity所有生命周期方法,如下
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Logger.d(this, "onCreate");
}
@Override
protected void onStart() {
super.onStart();
Logger.d(this, "onStart");
}
@Override
protected void onResume() {
super.onResume();
Logger.d(this, "onResume");
}
@Override
protected void onPause() {
super.onPause();
Logger.d(this, "onPause");
}
@Override
protected void onStop() {
super.onStop();
Logger.d(this, "onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Logger.d(this, "onDestroy");
}
@Override
protected void onRestart() {
super.onRestart();
Logger.d(this, "onRestart");
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Logger.d(this, "onNewIntent");
}
}
新建一个MainActivity继承BaseActivity,布局文件很简单,就一个button
public class MainActivity extends BaseActivity implements View.OnClickListener {
private Button mBtShowDialog;
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mBtShowDialog = (Button) findViewById(R.id.bt_show_dialog);
mBtShowDialog.setOnClickListener(this);
}
}
下面通过log看下这几种情况下Activity的生命周期,点击home键
从上图可以出点击home键后,会依次调用onPause-onStop,再次进入时,Activity的onRestart-onStart-onResume会被依次调用
下面来看看点击back键
点击back键后会依次调用onPause-onStop-onDestroy
按电源键
从上图可以出点按电源键和点击home一样,会依次调用onPause-onStop,再次进入时,Activity的onRestart-onStart-onResume会被依次调用
3、Activity中弹出dialog,Activity的生命周期方法会走吗?
在Activity中弹出dialog时,activity的生命周期方法不会被调用;
private void showDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this).setTitle("Dialog Title").setMessage("Are you sure");
builder.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.setPositiveButton("ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(MainActivity.this, "you choose ok", Toast.LENGTH_SHORT).show();
}
});
builder.create().show();
}
当然如果是当前activity中跳转到另一个配置为dialog样式的activity时,对应的生命周期方法是会被掉用的,新建一个activity,配置theme为
<activity android:name=".DialogActivity" android:theme="@style/Theme.AppCompat.Dialog.Alert"/>
这种情况就和两个activity之间跳转时activity生命周期一样,就不在啰嗦了
4、旋转屏幕时Activity的生命周期?
屏幕旋转小插曲:
onConfigurationChanged事件并不是只有屏幕方向改变才可以触发,其他的一些系统设置改变也可以触发,比如打开或者隐藏键盘
在重写onConfigurationChanged需要注意下面几点:
第一:权限声明
<uses-permission Android:name="android.permission.CHANGE_CONFIGURATION"></uses-permission>
第二:声明activity要捕获的事件类型
<activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="orientation|keyboard">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<categoryandroid:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
这里一定要声明Android:configChanges属性,该属性规定了我们可以在程序中捕获到的事件类型,多个事件类型用|分隔。
如果这里没有orientation,那么我们再程序中是无法捕获到屏幕改变的事件的。
第三:重写Activity中的onConfigurationChanged方法
android:configChanges,这个方法主要是负责列出
onConfigurationChanged监听的清单,当清单上用户指定的设置改变时,Activity会自己处理这些变化。
屏幕界面旋转(可能是用户手动旋转的),【注意:如果你的开发API等级等于或高于13,你还需要设置screenSize,因为screenSize会在屏幕旋转时改变】
keyboardHidden,键盘辅助功能改变
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Logger.d(this, " current orientation is landscape");
} else if(newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Logger.d(this, " current orientation is portrait");
}
}
运行后旋转屏幕截图:
通过log看,横竖屏切换,并不会调用activity生命周期方法,只会调用onConfigurationChanged
4、Activity常见启动模式区别
singleInstance:当activity A启动模式配置singleInstance后,如果B通过startActivity启动A,那么A和B不在同一栈中,可以通过dumpsys查看,A和B的taskId是不一样的
singleTask:任务栈中只允许有一个当前Activity实例,如果当前Activity实例 A不在栈顶,会清空A之上所有activity,这样A就在栈顶了
singleTop:singleTop Activity 的实例可以无限多,唯一的区别是如果在栈顶已经有一个相同类型的Activity实例,Intent不会再创建一个Activity,而是通过onNewIntent()被发送到现有的Activity
standard:通过这种方式启动的activity会放在同一栈中,最后启动的activity实例位于栈顶
5、启动activity时需要注意的的几个Flag
FLAG_ACTIVITY_TASK_ON_HOME:如果启动activity时设置了这个flag,会把新的任务置于home activity task顶部(如果home activity task存在的情况下),此时按back会返回到桌面
/**
* If set in an Intent passed to {@link Context#startActivity Context.startActivity()},
* this flag will cause a newly launching task to be placed on top of the current
* home activity task (if there is one). That is, pressing back from the task
* will always return the user to home even if that was not the last activity they
* saw. This can only be used in conjunction with {@link #FLAG_ACTIVITY_NEW_TASK}.
*/
public static final int FLAG_ACTIVITY_TASK_ON_HOME = 0X00004000;
启动当前apk里的activity:
intent = new Intent("com.jason.activity.demo.ActivityWithFlag"); intent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME|Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
这时点击back键,返回到的是之前activity界面
启动别的apk里的activity:
intent = new Intent("android.telecom.action.CHANGE_PHONE_ACCOUNTS");
intent.setPackage("com.android.phone");
intent.setFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME|Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
这时点击back键,返回到launcher界面
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:如果activity设置了这个flag,则不会显示在最近任务栏中