Welcome 微信登录
编程资源 图片资源库 蚂蚁家优选 PDF转换器

首页 / 操作系统 / Linux / Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案

众所周知,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 创建对话框(http://www.linuxidc.com/Linux/2015-08/121313.htm)。这样,其实给我们带来一个很大的问题,DialogFragment说白了是Fragment,和当前的Activity的生命周期会发生绑定,我们旋转屏幕会造成Activity的销毁,当然也会对DialogFragment造成影响。下面我将使用几个例子,分别使用上面的3种方式,和如何最好的解决上述的问题。本文相关源码下载地址:------------------------------------------分割线------------------------------------------免费下载地址在 http://linux.linuxidc.com/用户名与密码都是www.linuxidc.com具体下载目录在 /2015年资料/8月/8日/Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案/下载方法见 http://www.linuxidc.com/Linux/2013-07/87684.htm------------------------------------------分割线------------------------------------------3、使用onSaveInstanceState()和onRestoreInstanceState()进行数据保存与恢复代码: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;
/**
 * 不考虑加载时,进行旋转的情况,有意的避开这种情况,后面例子会介绍解决方案
 * @author zhy
 *
 */
public class SavedInstanceStateUsingActivity extends ListActivity
{
 private static final String TAG = "MainActivity";
 private ListAdapter mAdapter;
 private ArrayList<String> mDatas;
 private DialogFragment mLoadingDialog;
 private LoadDataAsyncTask mLoadDataAsyncTask; @Override
 public void onCreate(Bundle savedInstanceState)
 {
  super.onCreate(savedInstanceState);
  Log.e(TAG, "onCreate");
  initData(savedInstanceState);
 } /**
  * 初始化数据
  */
 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();
  } } /**
  * 初始化适配器
  */
 private void initAdapter()
 {
  mAdapter = new ArrayAdapter<String>(
    SavedInstanceStateUsingActivity.this,
    android.R.layout.simple_list_item_1, mDatas);
  setListAdapter(mAdapter);
 } @Override
 protected void onRestoreInstanceState(Bundle state)
 {
  super.onRestoreInstanceState(state);
  Log.e(TAG, "onRestoreInstanceState");
 } @Override
 protected void onSaveInstanceState(Bundle outState)
 {
  super.onSaveInstanceState(outState);
  Log.e(TAG, "onSaveInstanceState");
  outState.putSerializable("mDatas", mDatas); } /**
  * 模拟耗时操作
  *
  * @return
  */
 private ArrayList<String> generateTimeConsumingDatas()
 {
  try
  {
   Thread.sleep(2000);
  } catch (InterruptedException e)
  {
  }
  return new ArrayList<String>(Arrays.asList("通过Fragment保存大量数据",
    "onSaveInstanceState保存数据",
    "getLastNonConfigurationInstance已经被弃用", "RabbitMQ", "Hadoop",
    "Spark"));
 } private class LoadDataAsyncTask extends AsyncTask<Void, Void, Void>
 {
  @Override
  protected Void doInBackground(Void... params)
  {
   mDatas = generateTimeConsumingDatas();
   return null;
  }  @Override
  protected void onPostExecute(Void result)
  {
   mLoadingDialog.dismiss();
   initAdapter();
  }
 } @Override
 protected void onDestroy()
 {
  Log.e(TAG, "onDestroy");
  super.onDestroy();
 }}界面为一个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: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 retain
 private Bitmap data;
 // this method is only called once for this fragment
 @Override
 public void onCreate(Bundle savedInstanceState)
 {
  super.onCreate(savedInstanceState);
  // retain this fragment
  setRetainInstance(true);
 } public void setData(Bitmap data)
 {
  this.data = data;
 } public Bitmap getData()
 {
  return data;
 }
}更多详情见请继续阅读下一页的精彩内容: http://www.linuxidc.com/Linux/2015-08/121312p2.htm