zoukankan      html  css  js  c++  java
  • Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案

    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 plaincopy
     
    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 plaincopy
     
    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 plaincopy
     
    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 plaincopy
     
    1. <activity  
    2.         android:name=".ConfigChangesTestActivity"  
    3.         android:configChanges="screenSize|orientation" >  
    4.     </activity>  

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

    ConfigChangesTestActivity

    [java] view plaincopy
     
    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 plaincopy
     
    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 plaincopy
     
    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 plaincopy
     
    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/


    有任何问题,欢迎留言 

  • 相关阅读:
    如何:为 Silverlight 客户端生成双工服务
    Microsoft Sync Framework 2.1 软件开发包 (SDK)
    Windows 下的安装phpMoAdmin
    asp.net安全检测工具 Padding Oracle 检测
    HTTP Basic Authentication for RESTFul Service
    Windows系统性能分析
    Windows Server AppFabric Management Pack for Operations Manager 2007
    Mongo Database 性能优化
    服务器未能识别 HTTP 标头 SOAPAction 的值
    TCP WAIT状态及其对繁忙的服务器的影响
  • 原文地址:https://www.cnblogs.com/zhanganju/p/4178372.html
Copyright © 2011-2022 走看看