Activity横竖屏切换的那些事(生命周期,数据恢复,防止重建)
hocyber
8年前
<p>Activity的生命周期,这是每个Android开发者必须了解的知识。Activity是四大组件之一,而且是使用最频繁的组件。横竖屏切换是每个Android开发者都会遇到的问题。那么横竖屏切换后Activity到底发生了什么呢?</p> <h3><strong>1、生命周期的变化</strong></h3> <p>建一个Activity,重写所有的生命周期方法,然后在这些方法中添加Log。</p> <pre> <code class="language-java">public class ActivityA extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.test_layout); Log.i("axe.mg","onCreate()"); } @Override protected void onRestart() { super.onRestart(); Log.i("axe.mg","onRestart()"); } @Override protected void onStart() { super.onStart(); Log.i("axe.mg","onStart()"); } @Override protected void onResume() { super.onResume(); Log.i("axe.mg","onResume()"); } @Override protected void onPause() { super.onPause(); Log.i("axe.mg","onPause()"); } @Override protected void onStop() { super.onStop(); Log.i("axe.mg","onStop()"); } @Override protected void onDestroy() { super.onDestroy(); Log.i("axe.mg","onDestroy()"); } }</code></pre> <p>正常启动这个Activity。</p> <pre> <code class="language-java">09-28 23:16:49.809 24348-24348/com.mg.axe.androiddevelop I/axe.mg: onCreate() 09-28 23:16:49.809 24348-24348/com.mg.axe.androiddevelop I/axe.mg: onStart() 09-28 23:16:49.819 24348-24348/com.mg.axe.androiddevelop I/axe.mg: onResume()</code></pre> <p>通过Log可以看到Activity从创建到展示的的生命周期: onCreate() --->onStart() --->onResume()</p> <p>进行横竖屏切换。</p> <pre> <code class="language-java">09-28 23:17:42.519 24348-24348/com.mg.axe.androiddevelop I/axe.mg: onPause() 09-28 23:17:42.519 24348-24348/com.mg.axe.androiddevelop I/axe.mg: onStop() 09-28 23:17:42.519 24348-24348/com.mg.axe.androiddevelop I/axe.mg: onDestroy() 09-28 23:17:42.719 24348-24348/com.mg.axe.androiddevelop I/axe.mg: onCreate() 09-28 23:17:42.729 24348-24348/com.mg.axe.androiddevelop I/axe.mg: onStart() 09-28 23:17:42.729 24348-24348/com.mg.axe.androiddevelop I/axe.mg: onResume()</code></pre> <p>通过Log将生命周期分为两部分来分析:</p> <p>1、Activity销毁: onPause() ---> onStop() ---> onDestroy()</p> <p>2、Activity的重新创建展示 :onCreate() ---> onStart() --->onResume()</p> <p>结论:当Activity横竖屏切换时.Activity就会重新创建。<br> 横竖屏切换时Activity会被销毁 , onPause() onStop() onDestory() 会被调用。<br> 然后Activity又会被重新创建,onCreate() onStart() onResume() 会被调用。</p> <h3><strong>2、横竖屏切换后的数据恢复</strong></h3> <p>已经知道Activity横竖屏切换时Activity会重新创建。那么如何恢复重建前的数据呢?</p> <p>Activity中有两个方法用来保存和恢复Activity重建前的数据:</p> <pre> <code class="language-java">@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); }</code></pre> <p>onSaveInstanceState:横竖屏切换时,Activity销毁前系统会调用onSaveInstanceState通过保存Bundle参数来保存当前的Activity的数据。这个方法会在onStop前调用。</p> <p>onRestoreInstanceState:当Activity被重新创建之后会调用onRestoreInstanceState,并把Activity销毁时 onSaveInstanceState方法所保留的数据作为Bundle参数同时传递给onRestoreInstanceState和onCreate方法。所以我们可以在onRestoreInstanceState和onCreate方法中看到两个一样的参数Bundle savedInstanceState。这个方法会在onStart后调用。</p> <p><strong>强调一点:必须是Activity异常情况下被终止(例如:横竖屏切换)才会调用这两个方法。</strong></p> <p>通过代码验证:在Activity中重写onSaveInstanceState和onRestoreInstanceState来保存和恢复Activity重建前的数据。</p> <p>在onSaveInstanceState保存一个“value_string”的string</p> <p>在onRestoreInstanceState获取“value_string”这个值。</p> <pre> <code class="language-java">public class ActivityA extends Activity { @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString("key","value_string"); Log.i("axe.mg","onSaveInstanceState"); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); String str = savedInstanceState.getString("key"); Log.i("axe.mg","onRestoreInstanceState");Log.i("axe.mg","get value:"+str); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.test_layout); Log.i("axe.mg","onCreate()"); } @Override protected void onRestart() { super.onRestart(); Log.i("axe.mg","onRestart"); } @Override protected void onStart() { super.onStart(); Log.i("axe.mg","onStart()"); } @Override protected void onResume() { super.onResume(); Log.i("axe.mg","onResume()"); } @Override protected void onPause() { super.onPause(); Log.i("axe.mg","onPause()"); } @Override protected void onStop() { super.onStop(); Log.i("axe.mg","onStop()"); } @Override protected void onDestroy() { super.onDestroy(); Log.i("axe.mg","onDestroy()"); }</code></pre> <p>横竖屏后获取到的Log。</p> <pre> <code class="language-java">09-29 00:26:25.439 27568-27568/com.mg.axe.androiddevelop I/axe.mg: onPause() 09-29 00:26:25.439 27568-27568/com.mg.axe.androiddevelop I/axe.mg: onSaveInstanceState 09-29 00:26:25.439 27568-27568/com.mg.axe.androiddevelop I/axe.mg: onStop() 09-29 00:26:25.439 27568-27568/com.mg.axe.androiddevelop I/axe.mg: onDestroy() 09-29 00:26:25.499 27568-27568/com.mg.axe.androiddevelop I/axe.mg: onCreate() 09-29 00:26:25.499 27568-27568/com.mg.axe.androiddevelop I/axe.mg: onStart() 09-29 00:26:25.499 27568-27568/com.mg.axe.androiddevelop I/axe.mg: onRestoreInstanceState 09-29 00:26:25.499 27568-27568/com.mg.axe.androiddevelop I/axe.mg: get value:value_string 09-29 00:26:25.499 27568-27568/com.mg.axe.androiddevelop I/axe.mg: onResume()</code></pre> <p>通过Log来分析:</p> <p>1、Activity销毁,调用onSaveInstanceState,在onSaveInstanceState方法里面</p> <p>通过outState.putString("key","value_string");保存了“value_string”这个值。</p> <p>2、Activity重建,调用onRestoreInstanceState,在onSaveInstanceState方法里面</p> <p>通过savedInstanceState.getString("key");获取出了“value_string”这个值。</p> <h3><strong>3、如何防止横竖屏切换时Activity重建</strong></h3> <p>可能在开发中并不希望横竖屏切换后Activity重建。此时需要配置configChange参数,</p> <p>可以设置: android:configChanges="orientation|screenSize"</p> <pre> <code class="language-java"><activity android:name=".develop.ActivityA" android:label="@string/app_name" android:configChanges="orientation|screenSize" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity></code></pre> <p>配置添加android:configChanges="orientation|screenSize"</p> <p>(跟踪framework层代码,是由于google在android3.2中添加了screensize改变的通知,在转屏的时候,不仅是orientation发生了改变,screensize同样也发生了改变所以要添加“screenSize”)</p> <p>这种情况下横竖屏切换后不再重建Activity。同时会调用如下方法:</p> <pre> <code class="language-java">@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); }</code></pre> <p>通过代码验证:</p> <p>Activity添加android:configChanges="orientation|screenSize"</p> <pre> <code class="language-java"><activity android:name=".develop.ActivityA" android:configChanges="orientation|screenSize" android:label="@string/app_name" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity></code></pre> <pre> <code class="language-java">public class ActivityA extends Activity { @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Log.i("axe.mg","onConfigurationChanged"); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString("key","value_string"); Log.i("axe.mg","onSaveInstanceState"); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); String str = savedInstanceState.getString("key"); Log.i("axe.mg","onRestoreInstanceState");Log.i("axe.mg","get value:"+str); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.test_layout); Log.i("axe.mg","onCreate()"); } @Override protected void onRestart() { super.onRestart(); Log.i("axe.mg","onRestart"); } @Override protected void onStart() { super.onStart(); Log.i("axe.mg","onStart()"); } @Override protected void onResume() { super.onResume(); Log.i("axe.mg","onResume()"); } @Override protected void onPause() { super.onPause(); Log.i("axe.mg","onPause()"); } @Override protected void onStop() { super.onStop(); Log.i("axe.mg","onStop()"); } @Override protected void onDestroy() { super.onDestroy(); Log.i("axe.mg","onDestroy()"); }</code></pre> <p>横竖平切换后:</p> <pre> <code class="language-java">09-29 00:54:00.849 21338-21338/com.mg.axe.androiddevelop I/axe.mg: onConfigurationChanged</code></pre> <p>通过Log分析:</p> <p>此时Activity不再重建, 不会调用生命周期的方法,也不会调用onSaveInstanceState和onRestoreInstanceState。会调用onConfigurationChanged方法。</p> <p>+++++++++附件信息:configChanges属性的值+++++++++++</p> <pre> <code class="language-java">通过设置这个属性可以使Activity捕捉设备状态变化,以下是可以被识别的内容: 设置方法:将下列字段用“|”符号分隔开,例如:“locale|navigation|orientation "mcc" 国际移动用户识别码所属国家代号是改变了----- sim被侦测到了,去更新mcc mcc是移动用户所属国家代号 "mnc" 国际移动用户识别码的移动网号码是改变了------ sim被侦测到了,去更新mnc MNC是移动网号码,最多由两位数字组成,用于识别移动用户所归属的移动通信网 "locale" 地址改变了-----用户选择了一个新的语言会显示出来 "touchscreen" 触摸屏是改变了------通常是不会发生的 "keyboard" 键盘发生了改变----例如用户用了外部的键盘 "keyboardHidden" 键盘的可用性发生了改变 "navigation" 导航发生了变化-----通常也不会发生 "screenLayout" 屏幕的显示发生了变化------不同的显示被激活 "fontScale" 字体比例发生了变化----选择了不同的全局字体 "uiMode" 用户的模式发生了变化 "orientation" 屏幕方向改变了 "screenSize" 屏幕大小改变了 "smallestScreenSize" 屏幕的物理大小改变了,如:连接到一个外部的屏幕上</code></pre> <p> </p> <p> </p> <p>来自:http://www.jianshu.com/p/f62aed417809</p> <p> </p>