很多时候我们的activity可能会由于系统资源的紧缺被系统杀死,或者系统配置发生变化等
如果当前activity从横屏直接旋转到竖屏,此时由于系统配置发生了变化,在默认情况下,activity就会被销毁并重新创建。
当系统配置发生变化之后,activity会被销毁,其onPause,onStop,onDestroy均会被回调,同时由于activity是在异常情况下终止的,系统会回调onSaveInstanceState来保存当前activity的状态。onSaveInstanceState是在onStop之前调用的,onRestoreInstanceState是在onStart之后调用的
我们来看下面的栗子:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 这里当activity正常启动的时候,savedInstanceState是null,所以需要做非空判断
if (null != savedInstanceState) {
String str = savedInstanceState.getString("killedStr");
Log.d(TAG,"onCreate runs...str is :"+str);
}
}
/**
* 当activity被销毁,并重新创建时候,回调该方法获取之前保存的数据
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.d(TAG,"onRestoreInstanceState runs...");
String str = savedInstanceState.getString("killedStr");
savedInstanceState.putString("killedStr","testString");
Log.d(TAG,"onRestoreInstanceState runs...str is :"+str);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.d(TAG,"onSaveInstanceState runs...");
outState.putString("killedStr","testString");
}
}
可以看到,我们可以同时在onRestoreInstanceState和onCreate中获取之前保存的数据,区别在于在onRestoreInstanceState方法中,其参数一定不是null,而在onCreate中,如果当前activity正常启动的时候,其参数为null
onSaveInstanceState方法,系统只会在Activity即将被销毁,并且有机会重新显示的情况下才会去调用它
通常情况下我们的activity可以分为下面三种优先级:
需要注意到的是,在sdkversion > 13时候,为了防止旋转屏幕activity重启,除了orientation还需要添加screenSize
activity有四种启动模式:
当一个具有singleTask模式的activity A请求启动之后,系统会先寻找是否存在A想要的任务栈,如果不存在,就会重新创建一个任务栈,然后创建A的实例,并把A放到栈中,如果存在A所需要的任务栈,这时候要看A是否在栈中有实例存在,如果有实例存在,那么系统会把A调到栈顶并调用它的onNewIntent方法,如果实例不存在,就会创建A的实例,并把A放入栈中。
举下面栗子:
默认情况下,所有的activity所需要的任务栈的名字为当前的包名,我们可以单独为每个activity指定TaskAffinity属性,TaskAffinity属性主要和singleTask启动模式或者allowTaskReparenting配对使用。
我们可以分别在代码中或者AndroidManifest.xml里为activity指定启动模式。
// 在AndroidManifest.xml中指定
android:launchMode="singleTask"
// 在代码中指定,优先级较高
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 为MainActivity指定启动模式为singleTask
"com.example.launchmode.MainActivity"
android:launchMode="singleTask"
android:label="@string/app_name" >
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.id_text).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Intent intent = new Intent(MainActivity.this,MainActivity.class);
MainActivity.this.startActivity(intent);
}
});
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d(TAG,"onNewIntent runs....");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG,"onPause runs....");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG,"onResume runs....");
}
此时我们连续点击三次,启动当前MainActivity。
此时由于我们指定当前MainActivity的启动模式为singleTask,所以在默认的栈中,只有一个MainActivity实例,所以此时我们虽然启动了三次,但是按下back键盘的时候,会直接返回到桌面。
通过adb shell dumpsys activity 来查看当前的activity和其任务栈
现在我们为MainActivity去掉singleTask,做相同的操作。
我们都知道,隐式启动一个activity时候,就需要匹配当前activity的IntentFilter设置,如果匹配不成功,将无法启动目的activity,IntentFilter中设置的过滤信息有action,category,data。
为了匹配过滤列表,需要同时匹配action,category,data信息,否则会匹配失败,只有一个intent同时匹配action类别,category类别,data类别才算完全匹配,另外一个activity可能有多个intent-filter,一个intent只要能够匹配任何一组intent-filter即可以成功启动对应的activity
一个过滤规则中可以有多个action,只要intent中的action只要能够和其中的一个action相同即可匹配成功。action的匹配而规则要求intent中的action存在且必须和过滤规则中的一个action相同。
我们的代码中可以没有category,但是XML中要加上”android.intent.category.DEFAULT”这句。
如果你在代码中定义了一个或者多个category,那么你必须跟XML文件中定义的一样。比如你定义了一个category,那么要在XML文件中匹配到一个,,如果你定义了多个category,那么要在XML文件中全部匹配
data的匹配规则和action类似,它也要intent中必须含有data数据,并且data数据能够完全匹配过滤规则中的某一个data,这里的完全匹配是指过滤规则中出现的data部分也出现在了Intent的data中。
如果要为intent指定完整的data,必须要调用setDataAndType方法,因为setData和setType方法中,会彼此清除掉对方设置的值
如果我们使用intent匹配了一个activity,并且会启动当前的activity,那么如果当前的activity不存在即匹配失败的话,就会出现ActivityNotFoundException这样的错误。
我们可以采用PackageManager的resolveActivity或者intent的resolveActivity方法,如果找不到匹配的activity,就会返回null,我们通过返回值可以规避上面的错误。
另外PackageManager还提供了queryIntentActivities方法,这个方法和resolveActivity不同之处在于:它返回的不是最佳匹配的activity,而是所有成功匹配的activity。
public abstract List queryIntentActivities(Intent intent,int flags);
public abstract ResolveInfo resolveActivity(Intent intent,int flags);
上面两个方法第二个参数,使用时候需要注意,我们需要使用MATCH_DEFAULT_ONLY这个标记为,该标记为的含义是仅仅匹配在intent-filter中声明了
<category android:name="android.intent.category.DEFAULT"/>
这样的activity。这样做的好处在于只要上面两个方法返回值不是null,则startActivity一定可以成功。
ok,今天就到这里了,本篇博客来自android开发艺术探索的学习笔记。