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>