Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
来源:互联网 发布:立体设计图软件 编辑:程序博客网 时间:2024/06/06 02:35
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()进行数据保存与恢复
代码:
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,这个不是关键)。
效果图:
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- Android旋转屏幕处理 AsyncTask 和 ProgressDialog 的最佳方案
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- hdu 1004 Let the Balloon Rise(trie||映射统计)
- 【Python 学习手册笔记】动态类型简介
- 内存映射数据交换
- 黑马day17 json格式的数据
- Xutils 总结
- Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
- phalcon 设定数据库参数
- UICollectionView-使用Flow Layout
- iOS 点击状态栏回滚scrollView顶部
- navicat修改表的属性
- Fiddler工具的使用
- bzoj2875: [Noi2012]随机数生成器 裸矩阵乘法
- exit(0),exit(1),exit(-1)的区别?
- leetcode-二分搜索:求一个数的平方根