Reprint please indicate source: http://blog.csdn.net/lmj623565791/article/details/37936275
1. Overview
It is well known that activity will restart when the user rotates the screen when the screen orientation and configchanges are not explicitly specified. Of course, in response to this situation, Android gives several options:
A, if it is a small amount of data, can be saved and restored through Onsaveinstancestate () and Onrestoreinstancestate ().
Android will call the Onsaveinstancestate () method before destroying your activity, so you can store data about the state of the app in this method. You can then recover from the OnCreate () or Onrestoreinstancestate () method.
b, if it is a large amount of data, use fragment to keep the objects that need to be restored.
C, self-processing configuration changes.
Note: Getlastnonconfigurationinstance () has been deprecated and replaced by the above method.
2. Difficulties
Assuming that the current activity in OnCreate initiates an asynchronous thread to clip in the data, of course, in order to give the user a good experience, there will be a progressdialog, when the data load is complete, ProgressDialog disappears, set the data.
Here, if you rotate the screen after the asynchronous data has finished loading, using the A and B two methods above will not be very difficult, just to save the data and restore the data.
However, if you are rotating in line preempted, the following issues exist:
A) The data is not loaded at this time, the thread is started again when the OnCreate restarts, and the previous thread may be running, and the control that is not already present may be updated, causing an error.
b) Close the ProgressDialog code in the Onpostexecutez of the thread, but if the previous threads have been killed, the previous progressdialog cannot be closed.
c) Google's official does not recommend the use of ProgressDialog, here we will use the official recommended dialogfragment to create my load box, if you do not understand: see Android official Recommendation: dialogfragment Create dialog box . This, in fact, brings us a big problem, dialogfragment plainly is fragment, and the current activity of the life cycle will be bound, we rotate the screen will cause the destruction of activity, Of course, it will also affect the dialogfragment.
Below I will use a few examples, respectively, using the above 3 ways, and how best to solve the above problem.
3, using Onsaveinstancestate () and onrestoreinstancestate () for data preservation and recovery
Code:
Package Com.example.zhy_handle_runtime_change;import Java.util.arraylist;import Java.util.arrays;import Android.app.dialogfragment;import Android.app.listactivity;import Android.os.asynctask;import Android.os.Bundle; Import Android.util.log;import android.widget.arrayadapter;import android.widget.listadapter;/** * does not consider when loading, the rotation of the case, Deliberately avoid this situation, the following example describes the solution * @author Zhy * */public class Savedinstancestateusingactivity extends Listactivity{private static Final String TAG = "mainactivity";p rivate listadapter madapter;private arraylist<string> mdatas;private Dialogfragment mloadingdialog;private loaddataasynctask mloaddataasynctask; @Overridepublic void OnCreate (Bundle Savedinstancestate) {super.oncreate (savedinstancestate); LOG.E (TAG, "onCreate"); InitData (savedinstancestate);} /** * Initialize data */private void InitData (Bundle savedinstancestate) {if (savedinstancestate! = null) Mdatas = Savedinstancestate . Getstringarraylist ("Mdatas"); if (Mdatas = = null) {Mloadingdialog = new Loadingdialog (); MloadingdiaLog.show (Getfragmentmanager (), "Loadingdialog"); mloaddataasynctask = new Loaddataasynctask (); Mloaddataasynctask.execute ();} Else{initadapter ();}} /** * Initialize adapter */private void Initadapter () {madapter = new arrayadapter<string> ( Savedinstancestateusingactivity.this,android. R.layout.simple_list_item_1, Mdatas); Setlistadapter (Madapter);} @Overrideprotected void Onrestoreinstancestate (Bundle state) {super.onrestoreinstancestate (state); LOG.E (TAG, "onrestoreinstancestate");} @Overrideprotected void Onsaveinstancestate (Bundle outstate) {super.onsaveinstancestate (outstate); LOG.E (TAG, "onsaveinstancestate"); Outstate.putserializable ("Mdatas", Mdatas);} /** * Simulation Time-consuming operation * * @return */private arraylist<string> Generatetimeconsumingdatas () {try{thread.sleep ()} catch ( Interruptedexception e) {}return new arraylist<string> (Arrays.aslist ("Save large amounts of data through fragment", " Onsaveinstancestate save Data "," Getlastnonconfigurationinstance has been deprecated "," RabbitMQ "," Hadoop "," Spark ")); Private class Loaddataasynctask extends ASynctask<void, void, void>{@Overrideprotected void doinbackground (void ... params) {Mdatas = Generatetimeconsumingdatas (); return null;} @Overrideprotected void OnPostExecute (void result) {Mloadingdialog.dismiss (); Initadapter ();}} @Overrideprotected void OnDestroy () {LOG.E (TAG, "OnDestroy"); Super.ondestroy ();}}
The interface initiates an asynchronous task in a listview,oncreate to load the data, This uses thread.sleep to simulate a time-consuming operation, and when the user rotates the screen, the data is stored in onsaveinstancestate and the data is restored in oncreate, eliminating unnecessary reloading.
Operation Result:
When the normal loading data is complete, the user constantly rotates the screen, log will continue to play: Onsaveinstancestate->ondestroy->oncreate->onrestoreinstancestate, Verify that we did reboot, but we did not go to the data load again.
If you rotate while loading, an error occurs and nullpointexception occurs when an exception exits (Exit Reason: Dialog.dismiss () because the fragmentmanager that is bound to the current dialog box is null, Also interested in can go to debug, this is not the key).
:
4. Use fragment to save the object for recovering data
Restarting your activity requires recovering large amounts of data, re-establishing a network connection, or performing other intensive operations, which can be a slow user experience because of a change in configuration and a full restart. Also, using the system-provided onsaveintancestate () callback, using bundles to fully restore the state of your activity can be unrealistic (bundles are not designed to carry large amounts of data (such as bitmap), And the data in the bundle must be able to be serialized and deserialized, which consumes a lot of memory and causes the configuration to change slowly. In this case, when your activity restarts due to configuration changes, you can mitigate the burden of restarting by maintaining a fragment. This fragment can contain references to the stateful objects that you want to keep.
When the Android system shuts down your activity because of configuration changes, the fragments that are identified in your activity will not be destroyed. You can add such fragements to your activity to save a stateful object.
Save stateful objects in fragment when configuration changes at run time
A) inherit fragment, declaring a reference to your stateful object
b) Call Setretaininstance (Boolean) when fragment is created
c) Add the fragment instance to the activity
d) Restore the fragment using Fragmentmanager when activity restarts
Code:
The first is fragment:
Package Com.example.zhy_handle_runtime_change;import Android.app.fragment;import Android.graphics.bitmap;import Android.os.bundle;public class Retainedfragment extends fragment{//data object we want to retainprivate Bitmap data;//th Is method was only called once for this fragment@overridepublic void OnCreate (Bundle savedinstancestate) {super.oncreate (SA vedinstancestate);//Retain this fragmentsetretaininstance (true);} public void SetData (Bitmap data) {this.data = data;} Public Bitmap GetData () {return data;}}
It is simpler to declare only the data objects that need to be saved, then provide getter and setter, and note that you must call Setretaininstance (True) in OnCreate.
Then: fragmentretaindataactivity
Package Com.example.zhy_handle_runtime_change;import Android.app.activity;import android.app.DialogFragment; Import Android.app.fragmentmanager;import Android.graphics.bitmap;import Android.graphics.bitmap.config;import Android.os.bundle;import Android.util.log;import Android.widget.imageview;import Com.android.volley.RequestQueue; Import Com.android.volley.response;import Com.android.volley.toolbox.imagerequest;import Com.android.volley.toolbox.volley;public class Fragmentretaindataactivity extends activity{private static final String TAG = "fragmentretaindataactivity";p rivate retainedfragment datafragment;private dialogfragment mLoadingDialog ;p rivate ImageView mimageview;private Bitmap mbitmap; @Overridepublic void OnCreate (Bundle savedinstancestate) { Super.oncreate (savedinstancestate); Setcontentview (R.layout.activity_main); LOG.E (TAG, "onCreate");//Find the retained fragment on activity Restartsfragmentmanager FM = Getfragmentmanager ();d Atafr Agment = (retainedfragment) fm.findfragmentbyTag ("Data");//Create the fragment and data the first timeif (datafragment = = null) {//Add the Fragmentdatafragment = new Retainedfragment (); Fm.begintransaction (). Add (datafragment, "data"). commit (); Mbitmap = Collectmyloadeddata (); InitData ();//The data is available in Datafragment.getdata ()}/** * Initialize data */private void I Nitdata () {Mimageview = (ImageView) Findviewbyid (R.id.id_imageview); if (Mbitmap = = null) {Mloadingdialog = new Loadingdialog (); Mloadingdialog.show (Getfragmentmanager (), "Loading_dialog"); Requestqueue newrequestqueue = Volley.newrequestqueue (fragmentretaindataactivity.this); ImageRequest ImageRequest = New Imagerequest ("Http://img.my.csdn.net/uploads/201407/18/1405652589_5125.jpg", New Response.listener<bitmap > () {@Overridepublic void Onresponse (Bitmap response) {Mbitmap = Response;mimageview.setimagebitmap (MBITMAP);// Load the data from the Webdatafragment.setdata (Mbitmap); Mloadingdialog.dismiss ();}}, 0, 0, config.rgb_565, NULL); Newrequestqueue.add (imagerequest);} Else{mImageview.setimagebitmap (Mbitmap);}} @Overridepublic void OnDestroy () {LOG.E (TAG, "OnDestroy"); Super.ondestroy ();//Store the data in the Fragmentdatafragment.setdata (MBITMAP);} Private Bitmap Collectmyloadeddata () {return datafragment.getdata ();}}
Here in OnCreate always used volley to load a beautiful picture, and then in OnDestroy to store bitmap, add one in OnCreate or restore a fragment reference, and then read and set bitmap. This method is suitable for storing and recovering large data.
Note: There is no consideration for rotating the screen when loading, the problem is consistent with the above.
:
5, the configuration of Configchanges, their own changes in the screen rotation processing
To set the property in Menifest:
<activity android:name= ". Configchangestestactivity " android:configchanges=" screensize|orientation "> </activity>
The lower version of the API only needs to join the orientation, and the higher version needs to join the screensize.
Configchangestestactivity
Package Com.example.zhy_handle_runtime_change;import Java.util.arraylist;import Java.util.arrays;import Android.app.dialogfragment;import Android.app.listactivity;import Android.content.res.configuration;import Android.os.asynctask;import Android.os.bundle;import Android.util.log;import Android.widget.arrayadapter;import Android.widget.listadapter;import android.widget.toast;/** * @author Zhy * */public class Configchangestestactivity Extends listactivity{private static final String TAG = "mainactivity";p rivate listadapter madapter;private arraylist< string> mdatas;private dialogfragment mloadingdialog;private loaddataasynctask mLoadDataAsyncTask;@ overridepublic void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate); LOG.E (TAG, "onCreate"); InitData (savedinstancestate);} /** * Initialize data */private void InitData (Bundle savedinstancestate) {mloadingdialog = new Loadingdialog (); mloadingdialog.show (Getfragmentmanager (), "Loadingdialog"); mloaddataasynctask = new LoaddataasYnctask (); Mloaddataasynctask.execute ();} /** * Initialize adapter */private void Initadapter () {madapter = new arrayadapter<string> (Configchangestestactivity.this, Android. R.layout.simple_list_item_1, Mdatas); Setlistadapter (Madapter);} /** * Simulation Time-consuming operation * * @return */private arraylist<string> Generatetimeconsumingdatas () {try{thread.sleep ()} catch ( Interruptedexception e) {}return new arraylist<string> (Arrays.aslist ("Save large amounts of data through fragment", " Onsaveinstancestate save Data "," Getlastnonconfigurationinstance has been deprecated "," RabbitMQ "," Hadoop "," Spark ")); /** * The activity will not be restarted when the configuration has changed. However, this method will be recalled, the user self-rotating the screen after processing */@Overridepublic void onconfigurationchanged (Configuration newconfig) { Super.onconfigurationchanged (Newconfig); if (newconfig.orientation = = Configuration.orientation_landscape) { Toast.maketext (This, "landscape", Toast.length_short). Show (); else if (newconfig.orientation = = configuration.orientation_portrait) {Toast.maketext (this, "PORTRAIT", toast.length_ Short). Show ();}} Private Class Loaddataasynctask EXTends asynctask<void, void, void>{@Overrideprotected void doinbackground (void ... params) {Mdatas = Generatetimeconsumingdatas (); return null;} @Overrideprotected void OnPostExecute (void result) {Mloadingdialog.dismiss (); Initadapter ();}} @Overrideprotected void OnDestroy () {LOG.E (TAG, "OnDestroy"); Super.ondestroy ();}}
The code for the first method was modified, the saved and restored code was removed, the onconfigurationchanged was rewritten, and the activity was not restarted whenever the user rotated the screen. And the code in the onconfigurationchanged can be called. As you can see, rotation does not restart the activity anyway.
:
6. Best Practices for rotating screens
Here's the difficult point to start with today, when you're dealing with the beginning of the article, when the asynchronous task is executing, rotate, if the above problem is resolved.
First, let's talk about the process of exploration:
At first, I thought it was just a matter of starting the thread again and not causing an exception, I just had to close the last asynchronous task inside the OnDestroy. In fact, if I close, the last dialog will always be there, and if I don't close it, but the activity is bound to be destroyed, the dismiss of the dialog box will be abnormal. It hurts so much, and even if the dialog is closed, the task is closed; user rotation will cause the task to be recreated, loading the data from the beginning.
Below we want a solution that rotates the screen when the data is loaded, does not break the load task, and, for the user, the Wait box is displayed normally before the load is complete:
Of course we also use fragment for data preservation, after all this is officially recommended:
Otherretainedfragment
Package Com.example.zhy_handle_runtime_change;import Android.app.fragment;import android.os.bundle;/** * Save Object Fragment * * @author Zhy * */public class Otherretainedfragment extends fragment{//data object we want to re tain//save an asynchronous task private Myasynctask data;//This method was only called once for this fragment@overridepublic void onCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate);//Retain this fragmentsetretaininstance (true);} public void SetData (Myasynctask data) {this.data = data;} Public Myasynctask GetData () {return data;}}
And the difference is not very small, the only difference is that it is to save the object programming an asynchronous task, I believe see this, already know that often a core of the above problems, save an asynchronous task, in the restart, continue this task.
Package Com.example.zhy_handle_runtime_change;import Java.util.arraylist;import Java.util.arrays;import Java.util.list;import Android.os.asynctask;public class Myasynctask extends Asynctask<void, Void, Void>{private Fixproblemsactivity activity;/** * Whether to complete */private boolean iscompleted;/** * Progress box */private Loadingdialog MLOADINGDIALOG;PR Ivate list<string> items;public myasynctask (fixproblemsactivity activity) {this.activity = activity;} /** * Start, Show load box */@Overrideprotected void OnPreExecute () {mloadingdialog = new Loadingdialog (); Mloadingdialog.show ( Activity.getfragmentmanager (), "LOADING");} /** * Load Data */@Overrideprotected void doinbackground (void ... params) {items = Loadingdata (); return null;} /** * Load Complete callback Current Activity */@Overrideprotected void OnPostExecute (void unused) {iscompleted = true; Notifyactivitytaskcompleted (); if (mloadingdialog! = null) Mloadingdialog.dismiss ();} Public list<string> GetItems () {return items;} Private List<string> Loadingdata () {Try{thread.sleep (5000);} catch (Interruptedexception e) {}return new arraylist<string> (Arrays.aslist ("Save large amounts of data through fragment", " Onsaveinstancestate save Data "," Getlastnonconfigurationinstance has been deprecated "," RabbitMQ "," Hadoop "," Spark ")); /** * Set activity, because activity will always change * * @param activity */public void setactivity (fixproblemsactivity activity) {//If previous ACTI Vity destroys, destroys if (activity = = null) {Mloadingdialog.dismiss () with the dialogfragment bound to the previous activity;} Set to current activitythis.activity = activity;//to open a wait box that is bound to the current activity if (activity! = NULL &&!iscompleted) { Mloadingdialog = new Loadingdialog (); Mloadingdialog.show (Activity.getfragmentmanager (), "LOADING");} If complete, notify Activityif (iscompleted) {notifyactivitytaskcompleted ();}} private void notifyactivitytaskcompleted () {if (null! = activity) {activity.ontaskcompleted ();}}}
Asynchronous task, manage a dialog box that displays the Progress box, the download End progress box disappears, and provides callbacks for the activity before the download begins. Of course, the activity continues to restart during operation, we also provide the Setactivity method, Ondestory, will setactivity (NULL) to prevent memory leaks, and we will also close its bound load box When OnCreate incoming new activity, we will open a load box again, of course, because the screen rotation does not affect the loaded data, all the background data continues to load. is not perfect ~ ~
Main activity:
Package Com.example.zhy_handle_runtime_change;import Java.util.list;import Android.app.fragmentmanager;import Android.app.listactivity;import Android.os.bundle;import Android.util.log;import Android.widget.ArrayAdapter; Import Android.widget.listadapter;public class Fixproblemsactivity extends listactivity{private static final String TAG = "Mainactivity";p rivate listadapter madapter;private list<string> mdatas;private OtherRetainedFragment Datafragment;private myasynctask mmytask; @Overridepublic void OnCreate (Bundle savedinstancestate) {super.oncreate ( Savedinstancestate); LOG.E (TAG, "onCreate");//Find the retained fragment on activity Restartsfragmentmanager FM = Getfragmentmanager ();d Atafr Agment = (otherretainedfragment) fm.findfragmentbytag ("Data");//Create the fragment and data the first Timeif (Datafragme NT = = NULL) {//Add the Fragmentdatafragment = new Otherretainedfragment (); Fm.begintransaction (). Add (datafragment, "data "). commit ();} Mmytask = Datafragment.getdata (); if (MmytasK! = null) {mmytask.setactivity (this),} else{mmytask = new Myasynctask (this);d atafragment.setdata (mmytask); Mmytask.execute ();} The data is available. Datafragment.getdata ()} @Overrideprotected void Onrestoreinstancestate (Bundle state) { Super.onrestoreinstancestate (state); LOG.E (TAG, "onrestoreinstancestate");} @Overrideprotected void Onsaveinstancestate (Bundle outstate) {mmytask.setactivity (null); Super.onsaveinstancestate ( Outstate); LOG.E (TAG, "onsaveinstancestate");} @Overrideprotected void OnDestroy () {LOG.E (TAG, "OnDestroy"); Super.ondestroy ();} /** * callback */public void ontaskcompleted () {Mdatas = Mmytask.getitems (); madapter = new Arrayadapter<string> ( Fixproblemsactivity.this,android. R.layout.simple_list_item_1, Mdatas); Setlistadapter (Madapter);}}
In OnCreate, if the task is not opened (first entry), the task is opened, and if it is already open, call setactivity (this);
Add the current task to fragment in Onsaveinstancestate
I set the wait for 5 seconds, enough to rotate three or four back and forth ~ ~ ~ ~ can be seen in the continuous restart, but the slightest impact on the load data task run and Load box display ~ ~ ~
:
You can see me on the loading of the three heart disease crazy spin screen ~ ~ but does not affect the display effect and task loading ~ ~
Finally, explain, in fact, not only the screen rotation need to save data, when the user in the use of your app, suddenly received a call, long time not back to your app interface will also cause the destruction and reconstruction of activities, so a good app, it is necessary to have the ability to recover data ~ ~.
Some reference documents when reviewing the materials:
Http://developer.android.com/guide/topics/resources/runtime-changes.html
http://blog.doityourselfandroid.com/2010/11/14/handling-progress-dialogs-and-screen-orientation-changes/
Have any questions, welcome message
SOURCE Click to download
The best solution for Android screen rotation to handle asynctask and ProgressDialog