如何在屏幕旋转期间处理 AsyncTask?
-
26-09-2019 - |
题
我读了很多关于如何保存实例状态或如何处理屏幕旋转期间被破坏的活动的内容。
似乎有很多可能性,但我还没有弄清楚哪一种最适合检索 AsyncTask 的结果。
我有一些 AsyncTasks 只是再次启动并调用 isFinishing()
活动的方法,如果活动正在完成,他们不会更新任何内容。
问题是我有一个任务向 Web 服务发出请求,该请求可能失败或成功,并且重新启动该任务将导致用户的经济损失。
你会如何解决这个问题?可能的解决方案有哪些优点或缺点?
解决方案
我的第一个建议是,以确保你真正需要你的活动是在一个屏幕旋转(默认行为)复位。每次我已经受够了旋转的问题我已经添加了这个属性在AndroidManifest.xml我<activity>
标签,并一直就好了。
android:configChanges="keyboardHidden|orientation"
看起来怪异,但它做什么它移交给您onConfigurationChanged()
方法,如果你不提供一个少了点什么比其他重新测量的布局,这似乎是处理旋转完全足够方式大部分时间。
其他提示
您还可以使用onRetainNonConfigurationInstance()
您AsyncTask
传递给新Activity
(小心不漏以前Activity
这种方式虽然)。
这是最有趣的问题,我就到Android见过!其实我一直已经在寻找在过去几个月的解决方案。仍然没有解决。
小心,简单地重写
android:configChanges="keyboardHidden|orientation"
东西是不够的。
当用户接收到,而你的AsyncTask运行打电话考虑这样的情况。您的请求已经被服务器处理,所以的AsyncTask正在等待答复。在这一刻你的应用程序进入后台,因为手机应用刚刚进来的前景。因为它是在后台OS可能会杀了你的活动。
你为什么不始终保持对辛格尔顿目前的AsyncTask由Android提供的参考?
每当一个任务开始,在PreExecute或在生成器,定义:
((Application) getApplication()).setCurrentTask(asyncTask);
每当它完成将其设置为空。
这样,你总是有一个参考,它允许作为拨到您的特定逻辑,你做这样的事情,的onCreate或的onResume:
this.asyncTaskReference = ((Application) getApplication()).getCurrentTask();
如果它的空,你知道,至少目前还没有运行!
: - )
这个最合适的方法是使用一个片段保留了异步任务的实例,用旋转。
下面是非常简单的例子,因此很容易遵循这一技术集成到应用程序的链接。
在Pro android 4
。笔者建议有一个很好的方法,你应该使用weak reference
。
以我的观点,最好通过存储 asynctask onRetainNonConfigurationInstance
将其与当前 Activity 对象解耦,并在方向更改后将其绑定到新的 Activity 对象。 这里 我发现了一个非常好的示例,说明如何使用 AsyncTask 和 ProgressDialog。
机器人:后台处理/异步Opeartion与配置改变
要在后台进程保持异步opeartion的状态: 可以采取片段的帮助。
请参阅以下步骤:
步骤1:创建一个无报头片段让说后台任务,并在其中添加的私人异步任务类
步骤2(可选步骤):如果你想要把一个装载光标在下面的代码的活动中使用的顶部:
步骤3:在你的主要活动实施BackgroundTaskCallbacks接口在步骤1中所定义
class BackgroundTask extends Fragment {
public BackgroundTask() {
}
// Add a static interface
static interface BackgroundTaskCallbacks {
void onPreExecute();
void onCancelled();
void onPostExecute();
void doInBackground();
}
private BackgroundTaskCallbacks callbacks;
private PerformAsyncOpeation asyncOperation;
private boolean isRunning;
private final String TAG = BackgroundTask.class.getSimpleName();
/**
* Start the async operation.
*/
public void start() {
Log.d(TAG, "********* BACKGROUND TASK START OPERATION ENTER *********");
if (!isRunning) {
asyncOperation = new PerformAsyncOpeation();
asyncOperation.execute();
isRunning = true;
}
Log.d(TAG, "********* BACKGROUND TASK START OPERATION EXIT *********");
}
/**
* Cancel the background task.
*/
public void cancel() {
Log.d(TAG, "********* BACKGROUND TASK CANCEL OPERATION ENTER *********");
if (isRunning) {
asyncOperation.cancel(false);
asyncOperation = null;
isRunning = false;
}
Log.d(TAG, "********* BACKGROUND TASK CANCEL OPERATION EXIT *********");
}
/**
* Returns the current state of the background task.
*/
public boolean isRunning() {
return isRunning;
}
/**
* Android passes us a reference to the newly created Activity by calling
* this method after each configuration change.
*/
public void onAttach(Activity activity) {
Log.d(TAG, "********* BACKGROUND TASK ON ATTACH ENTER *********");
super.onAttach(activity);
if (!(activity instanceof BackgroundTaskCallbacks)) {
throw new IllegalStateException(
"Activity must implement the LoginCallbacks interface.");
}
// Hold a reference to the parent Activity so we can report back the
// task's
// current progress and results.
callbacks = (BackgroundTaskCallbacks) activity;
Log.d(TAG, "********* BACKGROUND TASK ON ATTACH EXIT *********");
}
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "********* BACKGROUND TASK ON CREATE ENTER *********");
super.onCreate(savedInstanceState);
// Retain this fragment across configuration changes.
setRetainInstance(true);
Log.d(TAG, "********* BACKGROUND TASK ON CREATE EXIT *********");
}
public void onDetach() {
super.onDetach();
callbacks = null;
}
private class PerformAsyncOpeation extends AsyncTask<Void, Void, Void> {
protected void onPreExecute() {
Log.d(TAG,
"********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON PRE EXECUTE ENTER *********");
if (callbacks != null) {
callbacks.onPreExecute();
}
isRunning = true;
Log.d(TAG,
"********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON PRE EXECUTE EXIT *********");
}
protected Void doInBackground(Void... params) {
Log.d(TAG,
"********* BACKGROUND TASK :-> ASYNC OPERATION :- > DO IN BACKGROUND ENTER *********");
if (callbacks != null) {
callbacks.doInBackground();
}
Log.d(TAG,
"********* BACKGROUND TASK :-> ASYNC OPERATION :- > DO IN BACKGROUND EXIT *********");
return null;
}
protected void onCancelled() {
Log.d(TAG,
"********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON CANCEL ENTER *********");
if (callbacks != null) {
callbacks.onCancelled();
}
isRunning = false;
Log.d(TAG,
"********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON CANCEL EXIT *********");
}
protected void onPostExecute(Void ignore) {
Log.d(TAG,
"********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON POST EXECUTE ENTER *********");
if (callbacks != null) {
callbacks.onPostExecute();
}
isRunning = false;
Log.d(TAG,
"********* BACKGROUND TASK :-> ASYNC OPERATION :- > ON POST EXECUTE EXIT *********");
}
}
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
}
public void onStart() {
super.onStart();
}
public void onResume() {
super.onResume();
}
public void onPause() {
super.onPause();
}
public void onStop() {
super.onStop();
}
的
public class ProgressIndicator extends Dialog {
public ProgressIndicator(Context context, int theme) {
super(context, theme);
}
private ProgressBar progressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.progress_indicator);
this.setCancelable(false);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
progressBar.getIndeterminateDrawable().setColorFilter(R.color.DarkBlue, android.graphics.PorterDuff.Mode.SCREEN);
}
@Override
public void show() {
super.show();
}
@Override
public void dismiss() {
super.dismiss();
}
@Override
public void cancel() {
super.cancel();
}
的
public class MyActivity extends FragmentActivity implements BackgroundTaskCallbacks,{
private static final String KEY_CURRENT_PROGRESS = "current_progress";
ProgressIndicator progressIndicator = null;
private final static String TAG = MyActivity.class.getSimpleName();
private BackgroundTask task = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(//"set your layout here");
initialize your views and widget here .............
FragmentManager fm = getSupportFragmentManager();
task = (BackgroundTask) fm.findFragmentByTag("login");
// If the Fragment is non-null, then it is currently being
// retained across a configuration change.
if (task == null) {
task = new BackgroundTask();
fm.beginTransaction().add(task, "login").commit();
}
// Restore saved state
if (savedInstanceState != null) {
Log.i(TAG, "KEY_CURRENT_PROGRESS_VALUE ON CREATE :: "
+ task.isRunning());
if (task.isRunning()) {
progressIndicator = new ProgressIndicator(this,
R.style.TransparentDialog);
if (progressIndicator != null) {
progressIndicator.show();
}
}
}
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// save the current state of your operation here by saying this
super.onSaveInstanceState(outState);
Log.i(TAG, "KEY_CURRENT_PROGRESS_VALUE ON SAVE INSTANCE :: "
+ task.isRunning());
outState.putBoolean(KEY_CURRENT_PROGRESS, task.isRunning());
if (progressIndicator != null) {
progressIndicator.dismiss();
progressIndicator.cancel();
}
progressIndicator = null;
}
private void performOperation() {
if (!task.isRunning() && progressIndicator == null) {
progressIndicator = new ProgressIndicator(this,
R.style.TransparentDialog);
progressIndicator.show();
}
if (task.isRunning()) {
task.cancel();
} else {
task.start();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (progressIndicator != null) {
progressIndicator.dismiss();
progressIndicator.cancel();
}
progressIndicator = null;
}
@Override
public void onPreExecute() {
Log.i(TAG, "CALLING ON PRE EXECUTE");
}
@Override
public void onCancelled() {
Log.i(TAG, "CALLING ON CANCELLED");
if (progressIndicator != null) {
progressIndicator.dismiss();
progressIndicator.cancel();
}
public void onPostExecute() {
Log.i(TAG, "CALLING ON POST EXECUTE");
if (progressIndicator != null) {
progressIndicator.dismiss();
progressIndicator.cancel();
progressIndicator = null;
}
}
@Override
public void doInBackground() {
// put your code here for background operation
}
}
有一点要考虑的是的AsyncTask的结果是否应该是仅适用于启动该任务的活动。如果是,则罗曼盖伊的回答是最好的。如果它应该是提供给您的应用程序的其他活动,那么在onPostExecute
你可以使用LocalBroadcastManager
。
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(new Intent("finished"));
您还需要确保活动正确处理情况时,同时活动暂停时发出的广播。
我的解决办法。
在我的情况下,我已经得到了具有相同上下文AsyncTasks的链。活动只有到第一个访问。要取消任何正在运行的任务我做了如下:
public final class TaskLoader {
private static AsyncTask task;
private TaskLoader() {
throw new UnsupportedOperationException();
}
public static void setTask(AsyncTask task) {
TaskLoader.task = task;
}
public static void cancel() {
TaskLoader.task.cancel(true);
}
}
任务doInBackground()
:
protected Void doInBackground(Params... params) {
TaskLoader.setTask(this);
....
}
活动onStop()
或onPause()
:
protected void onStop() {
super.onStop();
TaskLoader.cancel();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
final AddTask task = mAddTask;
if (task != null && task.getStatus() != UserTask.Status.FINISHED) {
final String bookId = task.getBookId();
task.cancel(true);
if (bookId != null) {
outState.putBoolean(STATE_ADD_IN_PROGRESS, true);
outState.putString(STATE_ADD_BOOK, bookId);
}
mAddTask = null;
}
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState.getBoolean(STATE_ADD_IN_PROGRESS)) {
final String id = savedInstanceState.getString(STATE_ADD_BOOK);
if (!BooksManager.bookExists(getContentResolver(), id)) {
mAddTask = (AddTask) new AddTask().execute(id);
}
}
}
您还可以添加 机器人:configChanges = “keyboardHidden |取向|屏幕尺寸”
你的清单例子,我希望它帮助
<application
android:name=".AppController"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/AppTheme">