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. 《TypeScript 中文入门教程》 1、基础数据类型

    转载:https://github.com/MyErpSoft/TypeScript-Handbook/blob/master/pages/zh-CHS/Basic%20Types.md 概述 为了让 ...

  2. Struts,spring,hibernate三大框架的面试

    Struts,spring,hibernate三大框架的面试 1.Hibernate工作原理及为什么要用? 原理: 1.读取并解析配置文件 2.读取并解析映射信息,创建SessionFactory 3 ...

  3. Vue.js 递归组件实现树形菜单

    最近看了 Vue.js 的递归组件,实现了一个最基本的树形菜单. 项目结构: main.js 作为入口,很简单: import Vue from 'vue' Vue.config.debug = tr ...

  4. Android—Socket服务端与客户端用字符串的方式互相传递图片

    发送图片: 首先找到具体传递的图片: private Bitmap getimage(String srcPath) { BitmapFactory.Options newOpts = new Bit ...

  5. drawRect与setNeedsDisplay简单介绍

    - (void)drawRect:(CGRect)rect { } p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: ...

  6. Fragment与ViewPager

    众所周知,为了实现滑动界面,经常让Fragment与ViewPager一起结合使用,每一个ViewPager的页面就是一个Fragment,我们可以在fragment中实现丰富的功能.它的基本用法可以 ...

  7. Xcode出现( linker command failed with exit code 1)错误总结

    这种问题,通常出现在添加第三方库文件或者多人开发时. 这种问题一般是找不到文件而导致的链接错误. 我们可以从如下几个方面着手排查. 先可以再试试一下几个方法:  1,看看是不是有新添加的文件跟之前文件 ...

  8. React Native 红屏之Could not connect to development server.

    React Native 是目前最火的开发框架,其他不说了,上Bug. 按照  React Native iOS环境搭建 高级版 在mac上  搭建 React Native  环境,运行 项目 若出 ...

  9. 开发者调试工具Chrome Workspace

    Workspace是个什么样的东西呢?他能够在开发者工具中调试修改js或者css同时自动保存文件,能够避免开发人员在工具中调试好,再到编辑器中修改一次代码的重复操作,能够提高一定的效率 配置Chrom ...

  10. Microsoft IoT Starter Kit 开发初体验

    1. 引子 今年6月底,在上海举办的中国国际物联网大会上,微软中国面向中国物联网社区推出了Microsoft IoT Starter Kit ,并且免费开放1000套的申请.申请地址为:http:// ...