2014-07-19 09:25 39227人阅读 评论(46) 收藏 举报
分类:
【android 进阶之路】(70) 【Android 精彩案例】(36)

版权声明:本文为博主原创文章,未经博主允许不得转载。

 

目录(?)[+]

 

转载请表明出处: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()进行数据保存与恢复

代码:

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

界面为一个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:

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

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

然后是:FragmentRetainDataActivity

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

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

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

效果图:

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

在menifest中进行属性设置:

  1. <activity
  2. android:name=".ConfigChangesTestActivity"
  3. android:configChanges="screenSize|orientation" >
  4. </activity>

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

ConfigChangesTestActivity

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

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

效果图:

6、旋转屏幕的最佳实践

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

首先说一下探索过程:

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

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

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

OtherRetainedFragment

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

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

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

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

主Activity:

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

在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/

Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案的更多相关文章

  1. Android DiskLruCache完全解析,硬盘缓存的最佳方案

    Android DiskLruCache完全解析,硬盘缓存的最佳方案 概述   记得在很早之前,我有写过一篇文章Android高效加载大图.多图解决方案,有效避免程序OOM,这篇文章是翻译自Andro ...

  2. 【Android】[转] Android屏幕旋转使用OrientationEventListener的监听

    说明 遇到一个奇葩的问题,我在使用onConfigChanged拦截屏幕的横竖屏旋转时,发现直接进行180度的横屏/竖屏转换居然没有反应!查找原因发现仅对landscape或者portrait状态有用 ...

  3. android 屏幕旋转

    转自:http://blog.csdn.net/oyzhizhong/article/details/8131799 屏是LANDSCAPE的,要让它默认显示为PORTRAIT. 1.kernel里要 ...

  4. Android屏幕旋转

    一个手机最基本的旋转方向有上面4种,而在Android开发中,涉及到屏幕旋转的地方很多,而且各个函数给出的角度值都不一样,比如 Activity的getRotate,Camera的setDisplay ...

  5. Android屏幕旋转总结

    转自:http://www.myexception.cn/operating-system/1452058.html 1. ProjectConifg.mk中定义宏MTK_LCM_PHYSICAL_R ...

  6. android 屏幕旋转 不重新加载oncreate

    当手机设定了使用横屏或者竖屏的时候,还想要使用重力感应,可以设置activity属性 android:screenOrientation="sensor" 但是每次翻转屏幕,都会重 ...

  7. Android DiskLruCache完全解析,硬盘缓存的最佳方案(转)

    概述 记得在很早之前,我有写过一篇文章<Android高效加载大图.多图解决方案,有效避免程序OOM>,这篇文章是翻译自Android Doc的,其中防止多图OOM的核心解决思路就是使用L ...

  8. (转)Android DiskLruCache完全解析,硬盘缓存的最佳方案

    摘自:http://blog.csdn.net/guolin_blog/article/details/28863651 转载请注明出处: http://blog.csdn.net/guolin_bl ...

  9. Android DiskLruCache完全解析,硬盘缓存的最佳方案 --转载

    概述 记得在很早之前,我有写过一篇文章 Android高效加载大图.多图解决方案,有效避免程序OOM,这篇文章是翻译自Android Doc的,其中防止多图OOM的核心解决思路就是使用LruCache ...

随机推荐

  1. yii的入口文件index.php中为什么会有这两句

    yii的应用模板中,index.php中 前面会有这两句 <?php // comment out the following two lines when deployed to produc ...

  2. Java IO之字节流

    Java中的输入是指从数据源等读到Java程序中,这里的数据源可以是文件,内存或网络连接,输出则是指从Java程序中写到目的地. 输入输出流可以分为以下几种类型(暂时不考虑File类) 类名 中文名 ...

  3. Verilog学习笔记基本语法篇(十一)········ 常用系统函数

    1)系统任务:$monitor   格式: $monitor(p1,p2,p3...pn); $monitor; $monitoron; $monitoroff; 任务$monitor提供了监控输出列 ...

  4. 转载:C#中的泛型

    泛型(generic)是C#语言2.0和通用语言运行时(CLR)的一个新特性.泛型为.NET框架引入了类型参数(type parameters)的概念.类型参数使得设计类和方法时,不必确定一个或多个具 ...

  5. 自定义JsonResult解决 序列化类型 System.Data.Entity.DynamicProxies 的对象时检测到循环引用

    接上篇的问题,给出我自己的解决方案. 同时推荐要学习MVC的可以参考下<ASP.NET MVC4 框架揭秘>. 首先,要自定义JSonResult,就要明白MVC中 JsonResult的 ...

  6. BFC总结

    图1:问题图    图2:代码    图3:解决图 问题图与解决图的区别:黄色箭头那行代码的无和有 BFC 定义 BFC(Block formatting context)直译为"块级格式化 ...

  7. python操作SQL

    pymysql pymsql是Python中操作MySQL的模块,其使用方法和MySQLdb几乎相同 一.下载安装 pip3 install pymysql 二.操作使用 1.执行SQL #!/usr ...

  8. 函数模块:CTVB_COMPARE_TABLES--两个表中删除/变更/粘贴分解

    这个函数模块比较两个内表,将被删除.增加和修改的内表行分别分组输出. 输入参数:TABLE_OLD:旧表TABLE_NEW:新表KEY_LENGTH:键长度,指定内表中的前若干个字节(在 Unicod ...

  9. 路径分析之NetworkX实例

    #!/usr/bin/env python # -*- coding: utf-8 -*- import networkx as nx import numpy as np import json i ...

  10. android SQLite 批量插入数据慢的解决方案 (针对于不同的android api 版本)

    原地址 :http://www.cnblogs.com/wangmars/p/3914090.html SQLite,是一款轻型的数据库,被广泛的运用到很多嵌入式的产品中,因为占用的资源非常少,二其中 ...