• Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案


    出处:http://blog.csdn.net/lmj623565791/article/details/37936275

    1、概述

    众所周知,Activity在不明确指定屏幕方向和configChanges时,当用户旋转屏幕会重新启动。当然了,应对这种情况,Android给出了几种方案:

    a、如果是少量数据,可以通过onSaveInstanceState()和onRestoreInstanceState()进行保存与恢复。

    Android会在销毁你的Activity之前调用onSaveInstanceState()方法,于是,你可以在此方法中存储关于应用状态的数据。然后你可以在onCreate()或onRestoreInstanceState()方法中恢复。

    b、如果是大量数据,使用Fragment保持需要恢复的对象。

    c、自已处理配置变化。

    注:getLastNonConfigurationInstance()已经被弃用,被上述方法二替代。

    2、难点

    假设当前Activity在onCreate中启动一个异步线程去夹在数据,当然为了给用户一个很好的体验,会有一个ProgressDialog,当数据加载完成,ProgressDialog消失,设置数据。

    这里,如果在异步数据完成加载之后,旋转屏幕,使用上述a、b两种方法都不会很难,无非是保存数据和恢复数据。

    但是,如果正在线程加载的时候,进行旋转,会存在以下问题:

    a)此时数据没有完成加载,onCreate重新启动时,会再次启动线程;而上个线程可能还在运行,并且可能会更新已经不存在的控件,造成错误。

    b)关闭ProgressDialog的代码在线程的onPostExecutez中,但是上个线程如果已经杀死,无法关闭之前ProgressDialog。

    c)谷歌的官方不建议使用ProgressDialog,这里我们会使用官方推荐的DialogFragment来创建我的加载框,如果你不了解:请看 Android 官方推荐 : DialogFragment 创建对话框。这样,其实给我们带来一个很大的问题,DialogFragment说白了是Fragment,和当前的Activity的生命周期会发生绑定,我们旋转屏幕会造成Activity的销毁,当然也会对DialogFragment造成影响。

    下面我将使用几个例子,分别使用上面的3种方式,和如何最好的解决上述的问题。

    3、使用onSaveInstanceState()和onRestoreInstanceState()进行数据保存与恢复

    代码:

    [java] view plain copy
     
    1. package com.example.zhy_handle_runtime_change;  
    2.   
    3. import java.util.ArrayList;  
    4. import java.util.Arrays;  
    5.   
    6. import android.app.DialogFragment;  
    7. import android.app.ListActivity;  
    8. import android.os.AsyncTask;  
    9. import android.os.Bundle;  
    10. import android.util.Log;  
    11. import android.widget.ArrayAdapter;  
    12. import android.widget.ListAdapter;  
    13. /** 
    14.  * 不考虑加载时,进行旋转的情况,有意的避开这种情况,后面例子会介绍解决方案 
    15.  * @author zhy 
    16.  * 
    17.  */  
    18. public class SavedInstanceStateUsingActivity extends ListActivity  
    19. {  
    20.     private static final String TAG = "MainActivity";  
    21.     private ListAdapter mAdapter;  
    22.     private ArrayList<String> mDatas;  
    23.     private DialogFragment mLoadingDialog;  
    24.     private LoadDataAsyncTask mLoadDataAsyncTask;  
    25.   
    26.     @Override  
    27.     public void onCreate(Bundle savedInstanceState)  
    28.     {  
    29.         super.onCreate(savedInstanceState);  
    30.         Log.e(TAG, "onCreate");  
    31.         initData(savedInstanceState);  
    32.     }  
    33.   
    34.     /** 
    35.      * 初始化数据 
    36.      */  
    37.     private void initData(Bundle savedInstanceState)  
    38.     {  
    39.         if (savedInstanceState != null)  
    40.             mDatas = savedInstanceState.getStringArrayList("mDatas");  
    41.   
    42.         if (mDatas == null)  
    43.         {  
    44.             mLoadingDialog = new LoadingDialog();  
    45.             mLoadingDialog.show(getFragmentManager(), "LoadingDialog");  
    46.             mLoadDataAsyncTask = new LoadDataAsyncTask();  
    47.             mLoadDataAsyncTask.execute();  
    48.               
    49.         } else  
    50.         {  
    51.             initAdapter();  
    52.         }  
    53.   
    54.     }  
    55.   
    56.     /** 
    57.      * 初始化适配器 
    58.      */  
    59.     private void initAdapter()  
    60.     {  
    61.         mAdapter = new ArrayAdapter<String>(  
    62.                 SavedInstanceStateUsingActivity.this,  
    63.                 android.R.layout.simple_list_item_1, mDatas);  
    64.         setListAdapter(mAdapter);  
    65.     }  
    66.   
    67.     @Override  
    68.     protected void onRestoreInstanceState(Bundle state)  
    69.     {  
    70.         super.onRestoreInstanceState(state);  
    71.         Log.e(TAG, "onRestoreInstanceState");  
    72.     }  
    73.   
    74.     @Override  
    75.     protected void onSaveInstanceState(Bundle outState)  
    76.     {  
    77.         super.onSaveInstanceState(outState);  
    78.         Log.e(TAG, "onSaveInstanceState");  
    79.         outState.putSerializable("mDatas", mDatas);  
    80.   
    81.     }  
    82.   
    83.     /** 
    84.      * 模拟耗时操作 
    85.      *  
    86.      * @return 
    87.      */  
    88.     private ArrayList<String> generateTimeConsumingDatas()  
    89.     {  
    90.         try  
    91.         {  
    92.             Thread.sleep(2000);  
    93.         } catch (InterruptedException e)  
    94.         {  
    95.         }  
    96.         return new ArrayList<String>(Arrays.asList("通过Fragment保存大量数据",  
    97.                 "onSaveInstanceState保存数据",  
    98.                 "getLastNonConfigurationInstance已经被弃用", "RabbitMQ", "Hadoop",  
    99.                 "Spark"));  
    100.     }  
    101.   
    102.     private class LoadDataAsyncTask extends AsyncTask<Void, Void, Void>  
    103.     {  
    104.         @Override  
    105.         protected Void doInBackground(Void... params)  
    106.         {  
    107.             mDatas = generateTimeConsumingDatas();  
    108.             return null;  
    109.         }  
    110.   
    111.         @Override  
    112.         protected void onPostExecute(Void result)  
    113.         {  
    114.             mLoadingDialog.dismiss();  
    115.             initAdapter();  
    116.         }  
    117.     }  
    118.   
    119.     @Override  
    120.     protected void onDestroy()  
    121.     {  
    122.         Log.e(TAG, "onDestroy");  
    123.         super.onDestroy();  
    124.     }  
    125.   
    126. }  



    界面为一个ListView,onCreate中启动一个异步任务去加载数据,这里使用Thread.sleep模拟了一个耗时操作;当用户旋转屏幕发生重新启动时,会onSaveInstanceState中进行数据的存储,在onCreate中对数据进行恢复,免去了不必要的再加载一遍。

    运行结果:

    当正常加载数据完成之后,用户不断进行旋转屏幕,log会不断打出:onSaveInstanceState->onDestroy->onCreate->onRestoreInstanceState,验证我们的确是重新启动了,但是我们没有再次去进行数据加载。

    如果在加载的时候,进行旋转,则会发生错误,异常退出(退出原因:dialog.dismiss()时发生NullPointException,因为与当前对话框绑定的FragmentManager为null,又有兴趣的可以去Debug,这个不是关键)。

    效果图:

    4、使用Fragment来保存对象,用于恢复数据

    如果重新启动你的Activity需要恢复大量的数据,重新建立网络连接,或者执行其他的密集型操作,这样因为配置发生变化而完全重新启动可能会是一个慢的用户体验。并且,使用系统提供的onSaveIntanceState()的回调中,使用Bundle来完全恢复你Activity的状态是可能是不现实的(Bundle不是设计用来携带大量数据的(例如bitmap),并且Bundle中的数据必须能够被序列化和反序列化),这样会消耗大量的内存和导致配置变化缓慢。在这样的情况下,当你的Activity因为配置发生改变而重启,你可以通过保持一个Fragment来缓解重新启动带来的负担。这个Fragment可以包含你想要保持的有状态的对象的引用。

    当Android系统因为配置变化关闭你的Activity的时候,你的Activity中被标识保持的fragments不会被销毁。你可以在你的Activity中添加这样的fragements来保存有状态的对象。

    在运行时配置发生变化时,在Fragment中保存有状态的对象
    a) 继承Fragment,声明引用指向你的有状态的对象
    b) 当Fragment创建时调用setRetainInstance(boolean)
    c) 把Fragment实例添加到Activity中
    d) 当Activity重新启动后,使用FragmentManager对Fragment进行恢复
    代码:

    首先是Fragment:

    [java] view plain copy
     
    1. package com.example.zhy_handle_runtime_change;  
    2.   
    3. import android.app.Fragment;  
    4. import android.graphics.Bitmap;  
    5. import android.os.Bundle;  
    6.   
    7. public class RetainedFragment extends Fragment  
    8. {  
    9.     // data object we want to retain  
    10.     private Bitmap data;  
    11.     // this method is only called once for this fragment  
    12.     @Override  
    13.     public void onCreate(Bundle savedInstanceState)  
    14.     {  
    15.         super.onCreate(savedInstanceState);  
    16.         // retain this fragment  
    17.         setRetainInstance(true);  
    18.     }  
    19.   
    20.     public void setData(Bitmap data)  
    21.     {  
    22.         this.data = data;  
    23.     }  
    24.   
    25.     public Bitmap getData()  
    26.     {  
    27.         return data;  
    28.     }  
    29. }  


    比较简单,只需要声明需要保存的数据对象,然后提供getter和setter,注意,一定要在onCreate调用setRetainInstance(true);

    然后是:FragmentRetainDataActivity

    [java] view plain copy
     
    1. package com.example.zhy_handle_runtime_change;  
    2.   
    3. import android.app.Activity;  
    4. import android.app.DialogFragment;  
    5. import android.app.FragmentManager;  
    6. import android.graphics.Bitmap;  
    7. import android.graphics.Bitmap.Config;  
    8. import android.os.Bundle;  
    9. import android.util.Log;  
    10. import android.widget.ImageView;  
    11.   
    12. import com.android.volley.RequestQueue;  
    13. import com.android.volley.Response;  
    14. import com.android.volley.toolbox.ImageRequest;  
    15. import com.android.volley.toolbox.Volley;  
    16.   
    17. public class FragmentRetainDataActivity extends Activity  
    18. {  
    19.   
    20.     private static final String TAG = "FragmentRetainDataActivity";  
    21.     private RetainedFragment dataFragment;  
    22.     private DialogFragment mLoadingDialog;  
    23.     private ImageView mImageView;  
    24.     private Bitmap mBitmap;  
    25.   
    26.     @Override  
    27.     public void onCreate(Bundle savedInstanceState)  
    28.     {  
    29.         super.onCreate(savedInstanceState);  
    30.         setContentView(R.layout.activity_main);  
    31.         Log.e(TAG, "onCreate");  
    32.   
    33.         // find the retained fragment on activity restarts  
    34.         FragmentManager fm = getFragmentManager();  
    35.         dataFragment = (RetainedFragment) fm.findFragmentByTag("data");  
    36.         // create the fragment and data the first time  
    37.         if (dataFragment == null)  
    38.         {  
    39.             // add the fragment  
    40.             dataFragment = new RetainedFragment();  
    41.             fm.beginTransaction().add(dataFragment, "data").commit();  
    42.         }  
    43.         mBitmap = collectMyLoadedData();  
    44.         initData();  
    45.   
    46.         // the data is available in dataFragment.getData()  
    47.     }  
    48.   
    49.     /** 
    50.      * 初始化数据 
    51.      */  
    52.     private void initData()  
    53.     {  
    54.         mImageView = (ImageView) findViewById(R.id.id_imageView);  
    55.         if (mBitmap == null)  
    56.         {  
    57.             mLoadingDialog = new LoadingDialog();  
    58.             mLoadingDialog.show(getFragmentManager(), "LOADING_DIALOG");  
    59.             RequestQueue newRequestQueue = Volley  
    60.                     .newRequestQueue(FragmentRetainDataActivity.this);  
    61.             ImageRequest imageRequest = new ImageRequest(  
    62.                     "http://img.my.csdn.net/uploads/201407/18/1405652589_5125.jpg",  
    63.                     new Response.Listener<Bitmap>()  
    64.                     {  
    65.                         @Override  
    66.                         public void onResponse(Bitmap response)  
    67.                         {  
    68.                             mBitmap = response;  
    69.                             mImageView.setImageBitmap(mBitmap);  
    70.                             // load the data from the web  
    71.                             dataFragment.setData(mBitmap);  
    72.                             mLoadingDialog.dismiss();  
    73.                         }  
    74.                     }, 0, 0, Config.RGB_565, null);  
    75.             newRequestQueue.add(imageRequest);  
    76.         } else  
    77.         {  
    78.             mImageView.setImageBitmap(mBitmap);  
    79.         }  
    80.   
    81.     }  
    82.   
    83.     @Override  
    84.     public void onDestroy()  
    85.     {  
    86.         Log.e(TAG, "onDestroy");  
    87.         super.onDestroy();  
    88.         // store the data in the fragment  
    89.         dataFragment.setData(mBitmap);  
    90.     }  
    91.   
    92.     private Bitmap collectMyLoadedData()  
    93.     {  
    94.         return dataFragment.getData();  
    95.     }  
    96.   
    97. }  



    这里在onCreate总使用了Volley去加载 了一张美女照片,然后在onDestroy中对Bitmap进行存储,在onCreate添加一个或者恢复一个Fragment的引用,然后对Bitmap进行读取和设置。这种方式适用于比较大的数据的存储与恢复。

    注:这里也没有考虑加载时旋转屏幕,问题与上面的一致。

    效果图:

    5、配置configChanges,自己对屏幕旋转的变化进行处理

    在menifest中进行属性设置:

    [html] view plain copy
     
    1. <activity  
    2.         android:name=".ConfigChangesTestActivity"  
    3.         android:configChanges="screenSize|orientation" >  
    4.     </activity>  

    低版本的API只需要加入orientation,而高版本的则需要加入screenSize。

    ConfigChangesTestActivity

    [java] view plain copy
     
    1. package com.example.zhy_handle_runtime_change;  
    2.   
    3. import java.util.ArrayList;  
    4. import java.util.Arrays;  
    5.   
    6. import android.app.DialogFragment;  
    7. import android.app.ListActivity;  
    8. import android.content.res.Configuration;  
    9. import android.os.AsyncTask;  
    10. import android.os.Bundle;  
    11. import android.util.Log;  
    12. import android.widget.ArrayAdapter;  
    13. import android.widget.ListAdapter;  
    14. import android.widget.Toast;  
    15.   
    16. /** 
    17.  * @author zhy 
    18.  *  
    19.  */  
    20. public class ConfigChangesTestActivity extends ListActivity  
    21. {  
    22.     private static final String TAG = "MainActivity";  
    23.     private ListAdapter mAdapter;  
    24.     private ArrayList<String> mDatas;  
    25.     private DialogFragment mLoadingDialog;  
    26.     private LoadDataAsyncTask mLoadDataAsyncTask;  
    27.   
    28.     @Override  
    29.     public void onCreate(Bundle savedInstanceState)  
    30.     {  
    31.         super.onCreate(savedInstanceState);  
    32.         Log.e(TAG, "onCreate");  
    33.         initData(savedInstanceState);  
    34.     }  
    35.   
    36.     /** 
    37.      * 初始化数据 
    38.      */  
    39.     private void initData(Bundle savedInstanceState)  
    40.     {  
    41.   
    42.         mLoadingDialog = new LoadingDialog();  
    43.         mLoadingDialog.show(getFragmentManager(), "LoadingDialog");  
    44.         mLoadDataAsyncTask = new LoadDataAsyncTask();  
    45.         mLoadDataAsyncTask.execute();  
    46.   
    47.     }  
    48.   
    49.     /** 
    50.      * 初始化适配器 
    51.      */  
    52.     private void initAdapter()  
    53.     {  
    54.         mAdapter = new ArrayAdapter<String>(ConfigChangesTestActivity.this,  
    55.                 android.R.layout.simple_list_item_1, mDatas);  
    56.         setListAdapter(mAdapter);  
    57.     }  
    58.   
    59.     /** 
    60.      * 模拟耗时操作 
    61.      *  
    62.      * @return 
    63.      */  
    64.     private ArrayList<String> generateTimeConsumingDatas()  
    65.     {  
    66.         try  
    67.         {  
    68.             Thread.sleep(2000);  
    69.         } catch (InterruptedException e)  
    70.         {  
    71.         }  
    72.         return new ArrayList<String>(Arrays.asList("通过Fragment保存大量数据",  
    73.                 "onSaveInstanceState保存数据",  
    74.                 "getLastNonConfigurationInstance已经被弃用", "RabbitMQ", "Hadoop",  
    75.                 "Spark"));  
    76.     }  
    77.   
    78.     /** 
    79.      * 当配置发生变化时,不会重新启动Activity。但是会回调此方法,用户自行进行对屏幕旋转后进行处理 
    80.      */  
    81.     @Override  
    82.     public void onConfigurationChanged(Configuration newConfig)  
    83.     {  
    84.         super.onConfigurationChanged(newConfig);  
    85.   
    86.         if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)  
    87.         {  
    88.             Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();  
    89.         } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT)  
    90.         {  
    91.             Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();  
    92.         }  
    93.   
    94.     }  
    95.   
    96.     private class LoadDataAsyncTask extends AsyncTask<Void, Void, Void>  
    97.     {  
    98.         @Override  
    99.         protected Void doInBackground(Void... params)  
    100.         {  
    101.             mDatas = generateTimeConsumingDatas();  
    102.             return null;  
    103.         }  
    104.   
    105.         @Override  
    106.         protected void onPostExecute(Void result)  
    107.         {  
    108.             mLoadingDialog.dismiss();  
    109.             initAdapter();  
    110.         }  
    111.     }  
    112.   
    113.     @Override  
    114.     protected void onDestroy()  
    115.     {  
    116.         Log.e(TAG, "onDestroy");  
    117.         super.onDestroy();  
    118.     }  
    119.   
    120. }  

    对第一种方式的代码进行了修改,去掉了保存与恢复的代码,重写了onConfigurationChanged;此时,无论用户何时旋转屏幕都不会重新启动Activity,并且onConfigurationChanged中的代码可以得到调用。从效果图可以看到,无论如何旋转不会重启Activity.

    效果图:

    6、旋转屏幕的最佳实践

    下面要开始今天的难点了,就是处理文章开始时所说的,当异步任务在执行时,进行旋转,如果解决上面的问题。

    首先说一下探索过程:

    起初,我认为此时旋转无非是再启动一次线程,并不会造成异常,我只要即使的在onDestroy里面关闭上一个异步任务就可以了。事实上,如果我关闭了,上一次的对话框会一直存在;如果我不关闭,但是activity是一定会被销毁的,对话框的dismiss也会出异常。真心很蛋疼,并且即使对话框关闭了,任务关闭了;用户旋转还是会造成重新创建任务,从头开始加载数据。

    下面我们希望有一种解决方案:在加载数据时旋转屏幕,不会对加载任务进行中断,且对用户而言,等待框在加载完成之前都正常显示:

    当然我们还使用Fragment进行数据保存,毕竟这是官方推荐的:

    OtherRetainedFragment

    [java] view plain copy
     
    1. package com.example.zhy_handle_runtime_change;  
    2.   
    3. import android.app.Fragment;  
    4. import android.os.Bundle;  
    5.   
    6. /** 
    7.  * 保存对象的Fragment 
    8.  *  
    9.  * @author zhy 
    10.  *  
    11.  */  
    12. public class OtherRetainedFragment extends Fragment  
    13. {  
    14.   
    15.     // data object we want to retain  
    16.     // 保存一个异步的任务  
    17.     private MyAsyncTask data;  
    18.   
    19.     // this method is only called once for this fragment  
    20.     @Override  
    21.     public void onCreate(Bundle savedInstanceState)  
    22.     {  
    23.         super.onCreate(savedInstanceState);  
    24.         // retain this fragment  
    25.         setRetainInstance(true);  
    26.     }  
    27.   
    28.     public void setData(MyAsyncTask data)  
    29.     {  
    30.         this.data = data;  
    31.     }  
    32.   
    33.     public MyAsyncTask getData()  
    34.     {  
    35.         return data;  
    36.     }  
    37.   
    38.       
    39. }  


    和上面的差别不大,唯一不同的就是它要保存的对象编程一个异步的任务了,相信看到这,已经知道经常上述问题的一个核心了,保存一个异步任务,在重启时,继续这个任务。

    [java] view plain copy
     
    1. package com.example.zhy_handle_runtime_change;  
    2.   
    3. import java.util.ArrayList;  
    4. import java.util.Arrays;  
    5. import java.util.List;  
    6.   
    7. import android.os.AsyncTask;  
    8.   
    9. public class MyAsyncTask extends AsyncTask<Void, Void, Void>  
    10. {  
    11.     private FixProblemsActivity activity;  
    12.     /** 
    13.      * 是否完成 
    14.      */  
    15.     private boolean isCompleted;  
    16.     /** 
    17.      * 进度框 
    18.      */  
    19.     private LoadingDialog mLoadingDialog;  
    20.     private List<String> items;  
    21.   
    22.     public MyAsyncTask(FixProblemsActivity activity)  
    23.     {  
    24.         this.activity = activity;  
    25.     }  
    26.   
    27.     /** 
    28.      * 开始时,显示加载框 
    29.      */  
    30.     @Override  
    31.     protected void onPreExecute()  
    32.     {  
    33.         mLoadingDialog = new LoadingDialog();  
    34.         mLoadingDialog.show(activity.getFragmentManager(), "LOADING");  
    35.     }  
    36.   
    37.     /** 
    38.      * 加载数据 
    39.      */  
    40.     @Override  
    41.     protected Void doInBackground(Void... params)  
    42.     {  
    43.         items = loadingData();  
    44.         return null;  
    45.     }  
    46.   
    47.     /** 
    48.      * 加载完成回调当前的Activity 
    49.      */  
    50.     @Override  
    51.     protected void onPostExecute(Void unused)  
    52.     {  
    53.         isCompleted = true;  
    54.         notifyActivityTaskCompleted();  
    55.         if (mLoadingDialog != null)  
    56.             mLoadingDialog.dismiss();  
    57.     }  
    58.   
    59.     public List<String> getItems()  
    60.     {  
    61.         return items;  
    62.     }  
    63.   
    64.     private List<String> loadingData()  
    65.     {  
    66.         try  
    67.         {  
    68.             Thread.sleep(5000);  
    69.         } catch (InterruptedException e)  
    70.         {  
    71.         }  
    72.         return new ArrayList<String>(Arrays.asList("通过Fragment保存大量数据",  
    73.                 "onSaveInstanceState保存数据",  
    74.                 "getLastNonConfigurationInstance已经被弃用", "RabbitMQ", "Hadoop",  
    75.                 "Spark"));  
    76.     }  
    77.   
    78.     /** 
    79.      * 设置Activity,因为Activity会一直变化 
    80.      *  
    81.      * @param activity 
    82.      */  
    83.     public void setActivity(FixProblemsActivity activity)  
    84.     {  
    85.         // 如果上一个Activity销毁,将与上一个Activity绑定的DialogFragment销毁  
    86.         if (activity == null)  
    87.         {  
    88.             mLoadingDialog.dismiss();  
    89.         }  
    90.         // 设置为当前的Activity  
    91.         this.activity = activity;  
    92.         // 开启一个与当前Activity绑定的等待框  
    93.         if (activity != null && !isCompleted)  
    94.         {  
    95.             mLoadingDialog = new LoadingDialog();  
    96.             mLoadingDialog.show(activity.getFragmentManager(), "LOADING");  
    97.         }  
    98.         // 如果完成,通知Activity  
    99.         if (isCompleted)  
    100.         {  
    101.             notifyActivityTaskCompleted();  
    102.         }  
    103.     }  
    104.   
    105.     private void notifyActivityTaskCompleted()  
    106.     {  
    107.         if (null != activity)  
    108.         {  
    109.             activity.onTaskCompleted();  
    110.         }  
    111.     }  
    112.   
    113. }  


    异步任务中,管理一个对话框,当开始下载前,进度框显示,下载结束进度框消失,并为Activity提供回调。当然了,运行过程中Activity不断的重启,我们也提供了setActivity方法,onDestory时,会setActivity(null)防止内存泄漏,同时我们也会关闭与其绑定的加载框;当onCreate传入新的Activity时,我们会在再次打开一个加载框,当然了因为屏幕的旋转并不影响加载的数据,所有后台的数据一直继续在加载。是不是很完美~~

    主Activity:

    [java] view plain copy
     
    1. package com.example.zhy_handle_runtime_change;  
    2.   
    3. import java.util.List;  
    4.   
    5. import android.app.FragmentManager;  
    6. import android.app.ListActivity;  
    7. import android.os.Bundle;  
    8. import android.util.Log;  
    9. import android.widget.ArrayAdapter;  
    10. import android.widget.ListAdapter;  
    11.   
    12. public class FixProblemsActivity extends ListActivity  
    13. {  
    14.     private static final String TAG = "MainActivity";  
    15.     private ListAdapter mAdapter;  
    16.     private List<String> mDatas;  
    17.     private OtherRetainedFragment dataFragment;  
    18.     private MyAsyncTask mMyTask;  
    19.   
    20.     @Override  
    21.     public void onCreate(Bundle savedInstanceState)  
    22.     {  
    23.         super.onCreate(savedInstanceState);  
    24.         Log.e(TAG, "onCreate");  
    25.   
    26.         // find the retained fragment on activity restarts  
    27.         FragmentManager fm = getFragmentManager();  
    28.         dataFragment = (OtherRetainedFragment) fm.findFragmentByTag("data");  
    29.   
    30.         // create the fragment and data the first time  
    31.         if (dataFragment == null)  
    32.         {  
    33.             // add the fragment  
    34.             dataFragment = new OtherRetainedFragment();  
    35.             fm.beginTransaction().add(dataFragment, "data").commit();  
    36.         }  
    37.         mMyTask = dataFragment.getData();  
    38.         if (mMyTask != null)  
    39.         {  
    40.             mMyTask.setActivity(this);  
    41.         } else  
    42.         {  
    43.             mMyTask = new MyAsyncTask(this);  
    44.             dataFragment.setData(mMyTask);  
    45.             mMyTask.execute();  
    46.         }  
    47.         // the data is available in dataFragment.getData()  
    48.     }  
    49.   
    50.   
    51.     @Override  
    52.     protected void onRestoreInstanceState(Bundle state)  
    53.     {  
    54.         super.onRestoreInstanceState(state);  
    55.         Log.e(TAG, "onRestoreInstanceState");  
    56.     }  
    57.   
    58.   
    59.     @Override  
    60.     protected void onSaveInstanceState(Bundle outState)  
    61.     {  
    62.         mMyTask.setActivity(null);  
    63.         super.onSaveInstanceState(outState);  
    64.         Log.e(TAG, "onSaveInstanceState");  
    65.     }  
    66.   
    67.     @Override  
    68.     protected void onDestroy()  
    69.     {  
    70.         Log.e(TAG, "onDestroy");  
    71.         super.onDestroy();  
    72.   
    73.     }  
    74.     /** 
    75.      * 回调 
    76.      */  
    77.     public void onTaskCompleted()  
    78.     {  
    79.         mDatas = mMyTask.getItems();  
    80.         mAdapter = new ArrayAdapter<String>(FixProblemsActivity.this,  
    81.                 android.R.layout.simple_list_item_1, mDatas);  
    82.         setListAdapter(mAdapter);  
    83.     }  
    84.   
    85. }  


    在onCreate中,如果没有开启任务(第一次进入),开启任务;如果已经开启了,调用setActivity(this);

    在onSaveInstanceState把当前任务加入Fragment

    我设置了等待5秒,足够旋转三四个来回了~~~~可以看到虽然在不断的重启,但是丝毫不影响加载数据任务的运行和加载框的显示~~~~

    效果图:

    可以看到我在加载的时候就三心病狂的旋转屏幕~~但是丝毫不影响显示效果与任务的加载~~

    最后,说明一下,其实不仅是屏幕旋转需要保存数据,当用户在使用你的app时,忽然接到一个来电,长时间没有回到你的app界面也会造成Activity的销毁与重建,所以一个行为良好的App,是有必要拥有恢复数据的能力的~~。

    查阅资料时的一些参考文档:

    http://developer.android.com/guide/topics/resources/runtime-changes.html

    http://blog.doityourselfandroid.com/2010/11/14/handling-progress-dialogs-and-screen-orientation-changes/


    有任何问题,欢迎留言 

    源码点击下载

  • 相关阅读:
    linux服务器MySQL数据从磁盘拷贝以及恢复
    java编程思想第四版第十四章 类型信息习题
    java编程思想第四版第十四章 类型信息总结
    java编程思想第四版第十三章字符串 总结
    java编程思想第四版第十三章字符串 习题
    java编程思想第四版第十一章习题
    java编程思想第四版第十一章总结
    jQuery—自定义HTTP请求
    JavaScript—Json操作
    JavaScript—字符串(String)用法
  • 原文地址:https://www.cnblogs.com/endv/p/6607849.html
Copyright © 2020-2023  润新知