Reference : http://blog.csdn.net/aliaooooo/article/details/23606179
1. Handling Runtime Changes
Some device configurations can change during runtime (such as screen orientation, keyboard availability, and language). When such a
change occurs, Android restarts the running Activity(onDestroy()
is called, followed by onCreate()
). The restart behavior is designed to
help your application adapt to new configurations by automatically reloading your application with alternative resources that match the
new device configuration.
To properly handle a restart, it is important that your activity restores its previous state through the normal Activity lifecycle, in which
Android calls onSaveInstanceState() before it destroys your activity so that you can save data about the application state. You can then
restore the state during onCreate()
or onRestoreInstanceState()
.
However, you might encounter a situation in which restarting your application and restoring significant amounts of data can be costly and
create a poor user experience. In such a situation, you have two other options:
<1>Retaining an Object During a Configuration Change
<2>Handle the configuration change yourself
2. Retaining an Object During a Configuration Change
If restarting your activity requires that you recover large sets of data, re-establish a network connection, or perform other intensive
operations, then a full restart due to a configuration change might be a slow user experience.
Also, it might not be possible for you to completely restore your activity state with the Bundle
that the system saves for you with the
onSaveInstanceState()
callback—it is not designed to carry large objects (such as bitmaps) and the data within it must be serialized then
deserialized, which can consume a lot of memory and make the configuration change slow.
In such a situation, you can alleviate the burden of reinitializing your activity by retaining a Fragment
when your activity is restarted due to a
configuration change. This fragment can contain references to stateful objects that you want to retain.
When the Android system shuts down your activity due to a configuration change, the fragments of your activity that you have marked to
retain are not destroyed. You can add such fragments to your activity to preserve stateful objects.
To retain stateful objects in a fragment during a runtime configuration change:
<1> Extend the Fragment class and declare refrences to your stateful objects
<2> Call setRetainInstance(boolean) when the fragment is created
<3> Add the fragment to your activity
<4> Use FragmentManager to retrieve the fragment when the activity is restarted
//ConfigurationChangeHandlerTest.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="mirror.android.configurationchangehandlertest.ConfigurationChangeHandlerTest" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="点击按钮启动线程模拟网络耗时操作,获取新闻详情显示在按钮下面" /> <Button android:id="@+id/btn_createthread" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="获取娱乐新闻"/> <TextView android:id="@+id/tv_shownews" android:layout_width="match_parent" android:layout_height="match_parent" android:textColor="@android:color/holo_green_dark" /> </LinearLayout>
//ConfigurationChangeHandlerTest.java package mirror.android.configurationchangehandlertest; import android.app.Activity; import android.app.ProgressDialog; import android.content.res.Configuration; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; public class ConfigurationChangeHandlerTest extends Activity implements OnClickListener { private final String TAG = "ConfiguartionTest"; private String newsInfo; private Button button; private TextView showNewsTextView; private ProgressDialog progressDialog; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_configuration_change_handler_test); button = (Button)findViewById(R.id.btn_createthread); button.setOnClickListener(this); showNewsTextView = (TextView)findViewById(R.id.tv_shownews); if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){ Log.i( TAG ,"----onCreate - landscape---"); } else if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT){ Log.i(TAG, "----onCreate - portrait ---"); } } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_createthread: excuteLongTimeOperation(); break; } } private void excuteLongTimeOperation() { progressDialog = ProgressDialog.show(ConfigurationChangeHandlerTest.this , "Load Info" , "Loading" , true , true); Thread workerThread = new Thread(new MyNewThread()); workerThread.start(); } class MyNewThread extends Thread{ @Override public void run() { try { sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } newsInfo = "10月20日,周杰伦御用作词人方文山在微博发声表示,周杰伦的第十三张专辑的所有歌曲已经录制完毕," + "此次专辑会收录超过十首歌曲,预计12月发行。网友纷纷表示期待“小十三等待实在太不易了”。" + "还有部分网友就之前蔡依林[微博]的新专辑《呸》调侃:杰伦新专辑名字是不是<<爱呸不呸>> ?"; Message message = handler.obtainMessage(); Bundle bundle = new Bundle(); bundle.putString("message", newsInfo); message.setData(bundle); handler.sendMessage(message); } } private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { progressDialog.dismiss(); showNewsTextView.setText(newsInfo); } }; @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Log.i(TAG, "----onConfigurationChanged---"); } @Override protected void onStart() { super.onStart(); Log.i(TAG, "----onStart---"); } @Override protected void onRestart() { super.onRestart(); Log.i(TAG, "----onRestart---"); } @Override protected void onResume() { super.onResume(); Log.i(TAG, "----onResume---"); } @Override protected void onPause() { super.onPause(); Log.i(TAG, "----onPause---"); } @Override protected void onStop() { super.onStop(); Log.i(TAG, "----onStop---"); } @Override protected void onDestroy() { super.onDestroy(); Log.i(TAG, "----onDestroy---"); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.i(TAG, "----onSaveInstanceState---"); } }
Run this appliction, then after sleep 5 seconds, show the news about "周杰伦". Then, change the Phone to landspace
LogCat to observe
This way works, but when we change the screen orientation not until the sleep time ends, we will see
To solve this problem, change code like this
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="mirror.android.configurationchangehandlertest.ConfigurationChangeHandlerTest" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="点击按钮启动线程模拟网络耗时操作,获取新闻详情显示在按钮下面" /> <Button android:id="@+id/btn_createthread" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="获取娱乐新闻"/> <ProgressBar android:id="@+id/progressbarId" android:layout_width="wrap_content" android:layout_height="wrap_content" style="@android:style/Widget.ProgressBar.Small" /> <TextView android:id="@+id/tv_shownews" android:layout_width="match_parent" android:layout_height="match_parent" android:textColor="@android:color/holo_green_dark" /> </LinearLayout>
package mirror.android.configurationchangehandlertest; import android.app.Activity; import android.app.Fragment; import android.os.Bundle; import android.os.Handler; import android.os.Message; public class RetainedFragment extends Fragment { private OnFragmentInteractionListener mListener; //This interface must be implemented by activities that contain this //fragment to allow an interaction in this fragment to be communicated //to the activity and potentially other fragments contained in that //activity. public interface OnFragmentInteractionListener{ public void onFragmentInteraction(String string); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // retain this fragment setRetainInstance(true); } void excuteLongTimeOperation() { Thread workerThread = new Thread(new MyNewThread()); workerThread.start(); } class MyNewThread extends Thread{ @Override public void run() { try { sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } String newsInfo = "10月20日,周杰伦御用作词人方文山在微博发声表示,周杰伦的第十三张专辑的所有歌曲已经录制完毕," + "此次专辑会收录超过十首歌曲,预计12月发行。网友纷纷表示期待“小十三等待实在太不易了”。" + "还有部分网友就之前蔡依林[微博]的新专辑《呸》调侃:杰伦新专辑名字是不是<<爱呸不呸>> ?"; Message message = handler.obtainMessage(); Bundle bundle = new Bundle(); bundle.putString("message", newsInfo); message.setData(bundle); handler.sendMessage(message); } } private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { if(mListener != null){ mListener.onFragmentInteraction(msg.getData().getString("message")); } } }; @Override public void onAttach(Activity activity) { // Called when a fragment is first attached to its activity. onCreate(Bundle) will be called after this. super.onAttach(activity); mListener = (OnFragmentInteractionListener)activity; } }
package mirror.android.configurationchangehandlertest; import mirror.android.configurationchangehandlertest.RetainedFragment.OnFragmentInteractionListener; import android.app.Activity; import android.app.FragmentManager; import android.app.ProgressDialog; import android.content.res.Configuration; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; public class ConfigurationChangeHandlerTest extends Activity implements OnClickListener,OnFragmentInteractionListener { private final String TAG = "ConfiguartionTest"; private TextView showNewsInfoTxt; private ProgressBar progressBar; private String newsInfo; private RetainedFragment dataFragment; private static final String KEY_CURRENT_NEWSDATA = "current_nesdata"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_configuration_change_handler_test); Log.i(TAG, "----onCreate---"); Button anrBtn = (Button) findViewById(R.id.btn_createthread); anrBtn.setOnClickListener(this); showNewsInfoTxt = (TextView) findViewById(R.id.tv_shownews); progressBar = (ProgressBar) findViewById(R.id.progressbarId); if(null != savedInstanceState){ refreshNewsInfo((String) savedInstanceState.get(KEY_CURRENT_NEWSDATA)); } //在activity重启时获取到保留的fragment对象 FragmentManager fm = getFragmentManager(); dataFragment = (RetainedFragment) fm.findFragmentByTag("data"); if(null == dataFragment){ //添加fragment dataFragment = new RetainedFragment(); fm.beginTransaction().add(dataFragment, "data").commit(); //从网上下载数据 } } @Override public void onClick(View view) { switch (view.getId()){ case R.id.btn_createthread: progressBar.setVisibility(View.VISIBLE); dataFragment.excuteLongTimeOperation(); break; } } @Override public void onFragmentInteraction(String newsInfo) { this.newsInfo = newsInfo; refreshNewsInfo(newsInfo); } /** * 更新界面内容 */ private void refreshNewsInfo(String newsInfo) { progressBar.setVisibility(View.GONE); showNewsInfoTxt.setText(newsInfo); } @Override protected void onSaveInstanceState(Bundle outState) { //This method is called before an activity may be killed //so that when it comes back some time in the future it can restore its state. super.onSaveInstanceState(outState); outState.putString(KEY_CURRENT_NEWSDATA,showNewsInfoTxt.getText().toString());//注意不要直接传newsInfo,否则在异步操作执行完成后旋转屏幕,内容还是会消失。因为该值只有在屏幕旋转的时候才赋值, Log.i(TAG, "----onSaveInstanceState---"); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Log.i(TAG, "----onConfigurationChanged---"); } @Override protected void onDestroy() { super.onDestroy(); Log.i(TAG, "====onDestroy===="); } @Override protected void onStart() { super.onStart(); Log.i(TAG, "----onStart---"); } @Override protected void onResume() { super.onResume(); Log.i(TAG, "----onResume---"); } @Override protected void onRestart() { super.onRestart(); Log.i(TAG, "----onRestart---"); } @Override protected void onPause() { super.onPause(); Log.i(TAG, "----onPause---"); } @Override protected void onStop() { super.onStop(); Log.i(TAG, "----onStop---"); } }
3. Handling the Configuration Change Yourself
If your application doesn't need to update resources during a specific configuration change and you have a performance limitation
that requires you to avoid the activity restart, then you can declare that your activity handles the configuration change itself,
which prevents the system from restarting your activity.
Handling the configuration change yourself can make it much more difficult to use alternative resources, because the system does not
automatically apply them for you. This technique should be considered a last resort when you must avoid restarts due to a
configuration change and is not recommended for most applications.
For example, the following manifest code declares an activity that handles both the screen orientation change and keyboard
availability change:
<activity android:name=".MyActivity" android:configChanges="orientation|keyboardHidden" android:label="@string/app_name">