这里所说的异常主要是分为以下这在两种情况下的异常:
情况1、资源相关的系统配置发生改变Activity被杀死并被杀死重新创建Activity
情况2、资源内存不足导致低优先级的Activity被杀死
情况一具体:
那最简单的加载图片资源文件的机制来说,我们将图片放进drawable目录下,开发时为了兼容不同的设备,可能放的不只放在这一个目录中,还有drawable-mdpi,
drawable-hdpi这些目录中,当程序启动的时候会根据设备的情况进行加载合适的图片资源,比如手机的横屏向竖屏进行切换的时候,在切换的过程中Activity就会被销毁
并且进行重建。这个时候会出现一个问题,就是你在竖屏上显示的内容,比如EditText内输入的文字,就会在重构的时候被杀死,这时候你在横屏上就会呈现的EditText
在竖屏上的输入的文字,便统统清空,这个问题在以前的写程序的时候也遇到过,没当时事,前天在慕课上学习一个五子棋的游戏的时候,在最后老师进行提到这个问题,
并且对代码进行了处理。这样的话在你无意切换成宽屏的时候,棋盘不会进行清空。
如果不做这种处理当系统配置进行发生改变的时候其生命周期就会变成这样:盗个图
从竖屏到横屏这一个操作中,关键的生命周期经历这几个过程Log
I/com.example.hejingzhou.activityleft.MainActivity: onPause() I/com.example.hejingzhou.activityleft.MainActivity: onSaveInstanceState()被调用 I/com.example.hejingzhou.activityleft.MainActivity: onStop() I/com.example.hejingzhou.activityleft.MainActivity: onDestroy() I/com.example.hejingzhou.activityleft.MainActivity: onCreate() I/com.example.hejingzhou.activityleft.MainActivity: onRestoreInstanceState()被调用
这里添加一个刚刚知道的一个知识点,在Activity生命周期中,onStart onStop 与 onResume onPause的不同在于前者主要是从Activity 是否可见的角度进行回调,
而后者是从Activity是否位于前台这个角度来回调的。在onPause中不能进行重量级的操作,也就是耗时复杂的操作,因为必须onPause执行完后才对新的Activity
onResume。
我们就举EditText输入的问题进行举例。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i(TAG,"onCreate()"); editText = (EditText) findViewById(R.id.editView); } @Override protected void onSaveInstanceState(Bundle outState) { Log.i(TAG, "onSaveInstanceState()被调用"); strData = editText.getText().toString(); outState.putString("editData", strData); super.onSaveInstanceState(outState); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { Log.i(TAG, "onRestoreInstanceState()被调用"); super.onRestoreInstanceState(savedInstanceState); String string = savedInstanceState.getString("editData"); Log.i(TAG, string); //editText.setText(string); }
这样就可以进行对String类型进行保存,这样的话在切换成横屏的时候EditText输入的数据还是进行了保存。
这样不仅能保存String还有其他的很多方法:
而在慕课学习中我的五子棋项目中老师这个写的,这个更好:
@Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable(INSTANCE, super.onSaveInstanceState()); bundle.putBoolean(INSTANCE_GAME_OVER, mIsGemeoVer); bundle.putParcelableArrayList(INSTANCE_WHITE_ARRAY, mWhiteArray); bundle.putParcelableArrayList(INSTANCE_BLACK_ARRAY, mBlackArray); return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle) state; mIsGemeoVer = bundle.getBoolean(INSTANCE_GAME_OVER); mWhiteArray = bundle.getParcelableArrayList(INSTANCE_WHITE_ARRAY); mBlackArray = bundle.getParcelableArrayList(INSTANCE_BLACK_ARRAY); super.onRestoreInstanceState(bundle.getParcelable(INSTANCE)); return; } super.onRestoreInstanceState(state); }
老师用的是 Parcelable 与 Bundle结合将全部的传递的参数进行打包了。
关于Parcelable 类我查查了资料,他的作用和Serializable作用差不多,都是序列化,Parcelable 内容还挺多的,推荐瞅瞅这个:http://www.cnblogs.com/renqingping/archive/2012/10/25/Parcelable.html 写的挺好的。
情况二具体:
情况二不好直观的表现出来,但是数据存储恢复过程和上边是一样的,只提一下Activity中优先级的情况。
最高:前台的Activity--正在和用户交互的Activity,这样的级别最高,一般肯定不会给枪毙的。
其次:可见但非前台的Activity--比如Activity中弹出一个对话框,导致Activity可见但是位于后台无法和用户直接交互
最低:后台Activity--已经暂停的Activity,比如执行onStop的,优先级别最低,最容易杀死。
我们还有一种方式使数据不会消失,就是使用钉死AndroidManifest方法。
已知我们的某些系统配置出现改变时就会View就会被重建,我们可以不让它重建就可以了,我们可以给Activity制定configChanges属性,我们可以在AndroidMainifest
中进行配置,比如我们想让他在旋转屏幕的时候不发生重建,我们可以添加:android:configChanges="orientation 属性。
configChanges还有其他许多的属性:
mnc:IMSI网络发生改变,检测到SIM卡,或者更新MCC<span style="font-family: Arial, Helvetica, sans-serif;">其中mcc和mnc理论上不可能发生变化</span> locale:语言发生改变,用户选择了一个新的语言,文字应该重新显示 touchscreen:触摸屏发生改变,这通常是不应该发生的 keyboard:键盘类型发生改变,例如,用户使用了外部键盘 keyboardHidden:键盘发生改变,例如,用户使用了硬件键盘 navigation:导航发生改变,(这通常不应该发生) 举例:连接蓝牙键盘,连接后确实导致了navigation的类型发生变化。因为连接蓝牙键盘后,我可以使用方向键来navigate了 screenLayout:屏幕的布局发生改变,这可能导致激活不同的显示 fontScale:全局字体大小缩放发生改变 orientation:设备旋转,横向显示和竖向显示模式切换。 screenSize: 屏幕大小改变了 smallestScreenSize: 屏幕的物理大小改变了,如:连接到一个外部的屏幕上
这些属性可以并行使用 如:属性间添加“|”号 android:configChanges="orientation|screenSize" 即可,这样写的话在发生属性这些事件的时候,Activity就不会重建。
//notes:为了防止在旋转屏幕的时候Activity重启除了orientation属性还要添加screenSize
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.hejingzhou.activityleft"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:configChanges="orientation|screenSize"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
我们在Activity类中覆写一个onConfigurationChanged()方法打印一下log看看:
@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Log.i(TAG,""+newConfig.orientation); }
com.example.hejingzhou.activityleft I/com.example.hejingzhou.activityleft.MainActivity: 2 com.example.hejingzhou.activityleft I/com.example.hejingzhou.activityleft.MainActivity: 1
这就是在上一个方法的基础上添加后的进行旋转屏幕的全部log了 可以看出整个过程他就没有调用第一个方法中onRestoreInstanceState方法和onSaveInstanceState
方法,效果是一样的,填写的数据并没有丢失。