Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最(3)

6、旋转屏幕的最佳实践

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

首先说一下探索过程:

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

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

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

OtherRetainedFragment

package com.example.zhy_handle_runtime_change;

import Android.app.Fragment;
import android.os.Bundle;

/**
 * 保存对象的Fragment
 *
 * @author zhy
 *
 */
public class OtherRetainedFragment extends Fragment
{

// data object we want to retain
 // 保存一个异步的任务
 private MyAsyncTask 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(MyAsyncTask data)
 {
  this.data = data;
 }

public MyAsyncTask getData()
 {
  return data;
 }


}

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

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;
 /**
  * 是否完成
  */
 private boolean isCompleted;
 /**
  * 进度框
  */
 private LoadingDialog mLoadingDialog;
 private List<String> items;

public MyAsyncTask(FixProblemsActivity activity)
 {
  this.activity = activity;
 }

/**
  * 开始时,显示加载框
  */
 @Override
 protected void onPreExecute()
 {
  mLoadingDialog = new LoadingDialog();
  mLoadingDialog.show(activity.getFragmentManager(), "LOADING");
 }

/**
  * 加载数据
  */
 @Override
 protected Void doInBackground(Void... params)
 {
  items = loadingData();
  return null;
 }

/**
  * 加载完成回调当前的Activity
  */
 @Override
 protected 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("通过Fragment保存大量数据",
    "onSaveInstanceState保存数据",
    "getLastNonConfigurationInstance已经被弃用", "RabbitMQ", "Hadoop",
    "Spark"));
 }

/**
  * 设置Activity,因为Activity会一直变化
  *
  * @param activity
  */
 public void setActivity(FixProblemsActivity activity)
 {
  // 如果上一个Activity销毁,将与上一个Activity绑定的DialogFragment销毁
  if (activity == null)
  {
   mLoadingDialog.dismiss();
  }
  // 设置为当前的Activity
  this.activity = activity;
  // 开启一个与当前Activity绑定的等待框
  if (activity != null && !isCompleted)
  {
   mLoadingDialog = new LoadingDialog();
   mLoadingDialog.show(activity.getFragmentManager(), "LOADING");
  }
  // 如果完成,通知Activity
  if (isCompleted)
  {
   notifyActivityTaskCompleted();
  }
 }

private void notifyActivityTaskCompleted()
 {
  if (null != activity)
  {
   activity.onTaskCompleted();
  }
 }

}

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

主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";
 private ListAdapter mAdapter;
 private List<String> mDatas;
 private OtherRetainedFragment dataFragment;
 private MyAsyncTask mMyTask;

@Override
 public void onCreate(Bundle savedInstanceState)
 {
  super.onCreate(savedInstanceState);
  Log.e(TAG, "onCreate");

// find the retained fragment on activity restarts
  FragmentManager fm = getFragmentManager();
  dataFragment = (OtherRetainedFragment) fm.findFragmentByTag("data");

// create the fragment and data the first time
  if (dataFragment == null)
  {
   // add the fragment
   dataFragment = new OtherRetainedFragment();
   fm.beginTransaction().add(dataFragment, "data").commit();
  }
  mMyTask = dataFragment.getData();
  if (mMyTask != null)
  {
   mMyTask.setActivity(this);
  } else
  {
   mMyTask = new MyAsyncTask(this);
   dataFragment.setData(mMyTask);
   mMyTask.execute();
  }
  // the data is available in dataFragment.getData()
 }


 @Override
 protected void onRestoreInstanceState(Bundle state)
 {
  super.onRestoreInstanceState(state);
  Log.e(TAG, "onRestoreInstanceState");
 }


 @Override
 protected void onSaveInstanceState(Bundle outState)
 {
  mMyTask.setActivity(null);
  super.onSaveInstanceState(outState);
  Log.e(TAG, "onSaveInstanceState");
 }

@Override
 protected void onDestroy()
 {
  Log.e(TAG, "onDestroy");
  super.onDestroy();

}
 /**
  * 回调
  */
 public void onTaskCompleted()
 {
  mDatas = mMyTask.getItems();
  mAdapter = new ArrayAdapter<String>(FixProblemsActivity.this,
    android.R.layout.simple_list_item_1, mDatas);
  setListAdapter(mAdapter);
 }

}

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

在onSaveInstanceState把当前任务加入Fragment

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

效果图:

Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最

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

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

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/acf3f0afe80415755ceb47670d8102fe.html