Best practice: AsyncTask during orientation change

AsyncTask is a great thing to run complex tasks in another thread.

But when there is an orientation change or another configuration change while the AsyncTask is still running, the current Activity is destroyed and restarted. And as the instance of AsyncTask is connected to that activity, it fails and causes a "force close" message window.

So, I am looking for some kind of "best-practice" to avoid these errors and prevent AsyncTask from failing.

What I've seen so far is:

  • Disable orientation changes.(For sure not the way you should handle this.)
  • Letting the task survive and updating it with the new activity instance via onRetainNonConfigurationInstance
  • Just canceling the task when the Activity is destroyed and restarting it when the Activity is created again.
  • Binding the task to the application class instead of the activity instance.
  • Some method used in the "shelves" project (via onRestoreInstanceState)

Some code examples:

Android AsyncTasks during a screen rotation, Part I and Part II

ShelvesActivity.java

Can you help me to find the best approach which solves the problem best and is easy to implement as well? The code itself is also important as I don't know how to solve this correctly.

72580 次浏览

最近,我发现了一个很好的解决方案 给你。它基于通过 RetainConfiguration 保存任务对象。在我看来,这个解决方案非常优雅,至于我,我已经开始使用它了。您只需要从基准任务嵌套异步任务,仅此而已。

我通常通过让我的 AsyncTasks 在。OnPostExecute ()回调,这样它们就不会直接修改启动它们的活动。这些活动使用动态 BroadcastReceivers 监听这些广播并相应地采取行动。

这样,AsyncTasks 就不必关心处理其结果的特定 Activity 实例。当他们完成的时候,他们只是“呼喊”,如果一个活动是在那个时间左右(是活跃的和集中的/是在它的恢复状态) ,这是对任务的结果感兴趣,然后它将被处理。

这涉及到更多的开销,因为运行时需要处理广播,但我通常不介意。我认为使用 LocalBroadcastManager 而不是默认的系统范围可以加快速度。

使用 android:configChanges来解决这个问题。这是非常糟糕的做法。

没有使用 Activity#onRetainNonConfigurationInstance()。这是模块化程度较低,不太适合基于 Fragment的应用程序。

可以使用 阅读我的文章描述如何使用保留的 Fragment处理配置更改。它很好地解决了在旋转变化中保留 AsyncTask的问题。您基本上需要将 AsyncTask驻留在 Fragment中,在 Fragment上调用 setRetainInstance(true),并通过保留的 FragmentAsyncTask的进度/结果报告给它的 Activity

下面是 AsyncTask 的另一个示例,它使用 Fragment处理运行时配置更改(例如当用户旋转屏幕时)和 setRetainInstance(true)。还演示了一个确定的(定期更新的)进度条。

该示例部分基于官方文档 在配置更改期间保留对象

在这个例子中,需要背景线程的工作仅仅是将图像从互联网加载到 UI 中。

亚历克斯 · 洛克伍德似乎是对的,当涉及到使用“保留片段”来处理 AsyncTask 运行时配置更改时,这是最佳实践。onRetainNonConfigurationInstance()在 Lint 和 Android Studio 中被弃用。官方文件警告我们不要使用 android:configChanges,从 自己处理配置更改,..。

自己处理配置更改会使使用替代资源变得更加困难,因为系统不会自动为您应用这些资源。当由于配置更改而必须避免重新启动时,这种技术应被视为最后的手段,并且不推荐用于大多数应用程序。

然后还有一个问题,即是否应该对后台线程使用 AsyncTask。

AsyncTask 的官方引用警告..。

理想情况下,应该将 AsyncTask 用于短期操作(最多几秒钟)如果需要保持线程长时间运行,强烈建议您使用 java.util.current 包提供的各种 API,比如 Execator、 ThreadPoolExecator 和 FutureTask。

或者,可以使用服务、加载程序(使用 CursorLoader 或 AsyncTaskLoader)或内容提供程序来执行异步操作。

我把文章的其余部分分成:

  • 程序; 及
  • 上述过程的所有代码。

程序

  1. 从一个基本的 AsyncTask 开始,它作为一个活动的内部类(它不需要是一个内部类,但它可能是方便的)。在此阶段,AsyncTask 不处理运行时配置更改。

    public class ThreadsActivity extends ActionBarActivity {
    
    
    private ImageView mPictureImageView;
    
    
    private class LoadImageFromNetworkAsyncTask
    extends AsyncTask<String, Void, Bitmap> {
    
    
    @Override
    protected Bitmap doInBackground(String... urls) {
    return loadImageFromNetwork(urls[0]);
    }
    
    
    @Override
    protected void onPostExecute(Bitmap bitmap) {
    mPictureImageView.setImageBitmap(bitmap);
    }
    }
    
    
    /**
    * Requires in AndroidManifext.xml
    *  <uses-permission android:name="android.permission.INTERNET" />
    */
    private Bitmap loadImageFromNetwork(String url) {
    Bitmap bitmap = null;
    try {
    bitmap = BitmapFactory.decodeStream((InputStream)
    new URL(url).getContent());
    } catch (Exception e) {
    e.printStackTrace();
    }
    return bitmap;
    }
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_threads);
    
    
    mPictureImageView =
    (ImageView) findViewById(R.id.imageView_picture);
    }
    
    
    public void getPicture(View view) {
    new LoadImageFromNetworkAsyncTask()
    .execute("http://i.imgur.com/SikTbWe.jpg");
    }
    
    
    }
    
  2. Add a nested class RetainedFragment that extends the Fragement class and doesn't have it's own UI. Add setRetainInstance(true) to the onCreate event of this Fragment. Provide procedures to set and get your data.

    public class ThreadsActivity extends Activity {
    
    
    private ImageView mPictureImageView;
    private RetainedFragment mRetainedFragment = null;
    ...
    
    
    public static class RetainedFragment extends Fragment {
    
    
    private Bitmap mBitmap;
    
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    
    
    // The key to making data survive
    // runtime configuration changes.
    setRetainInstance(true);
    }
    
    
    public Bitmap getData() {
    return this.mBitmap;
    }
    
    
    public void setData(Bitmap bitmapToRetain) {
    this.mBitmap = bitmapToRetain;
    }
    }
    
    
    private class LoadImageFromNetworkAsyncTask
    extends AsyncTask<String, Integer,Bitmap> {
    ....
    
  3. In the outermost Activity class's onCreate() handle the RetainedFragment: Reference it if it already exists (in case the Activity is restarting); create and add it if it doesn't exist; Then, if it already existed, get data from the RetainedFragment and set your UI with that data.

    public class ThreadsActivity extends Activity {
    
    
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_threads);
    
    
    final String retainedFragmentTag = "RetainedFragmentTag";
    
    
    mPictureImageView =
    (ImageView) findViewById(R.id.imageView_picture);
    mLoadingProgressBar =
    (ProgressBar) findViewById(R.id.progressBar_loading);
    
    
    // Find the RetainedFragment on Activity restarts
    FragmentManager fm = getFragmentManager();
    // The RetainedFragment has no UI so we must
    // reference it with a tag.
    mRetainedFragment =
    (RetainedFragment) fm.findFragmentByTag(retainedFragmentTag);
    
    
    // if Retained Fragment doesn't exist create and add it.
    if (mRetainedFragment == null) {
    
    
    // Add the fragment
    mRetainedFragment = new RetainedFragment();
    fm.beginTransaction()
    .add(mRetainedFragment, retainedFragmentTag).commit();
    
    
    // The Retained Fragment exists
    } else {
    
    
    mPictureImageView
    .setImageBitmap(mRetainedFragment.getData());
    }
    }
    
  4. Initiate the AsyncTask from the UI

    public void getPicture(View view) {
    new LoadImageFromNetworkAsyncTask().execute(
    "http://i.imgur.com/SikTbWe.jpg");
    }
    
  5. Add and code a determinate progress bar:

    • Add a progress bar to the UI layout;
    • Get a reference to it in the Activity oncreate();
    • Make it visible and invisble at the start and end of the process;
    • Define the progress to report to UI in onProgressUpdate.
    • Change the AsyncTask 2nd Generic parameter from Void to a type that can handle progress updates (e.g. Integer).
    • publishProgress at regular points in doInBackground().

All the code for the above procedure

Activity Layout.

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.mysecondapp.ThreadsActivity">


<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">


<ImageView
android:id="@+id/imageView_picture"
android:layout_width="300dp"
android:layout_height="300dp"
android:background="@android:color/black" />


<Button
android:id="@+id/button_get_picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@id/imageView_picture"
android:onClick="getPicture"
android:text="Get Picture" />


<Button
android:id="@+id/button_clear_picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/button_get_picture"
android:layout_toEndOf="@id/button_get_picture"
android:layout_toRightOf="@id/button_get_picture"
android:onClick="clearPicture"
android:text="Clear Picture" />


<ProgressBar
android:id="@+id/progressBar_loading"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/button_get_picture"
android:progress="0"
android:indeterminateOnly="false"
android:visibility="invisible" />


</RelativeLayout>
</ScrollView>

活动: 子类化的 AsyncTask 内部类; 子类化的 RetainedFragment 内部类,处理运行时配置更改(例如,当用户旋转屏幕时) ; 以及一个确定的定期更新进度条。...

public class ThreadsActivity extends Activity {


private ImageView mPictureImageView;
private RetainedFragment mRetainedFragment = null;
private ProgressBar mLoadingProgressBar;


public static class RetainedFragment extends Fragment {


private Bitmap mBitmap;


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);


// The key to making data survive runtime configuration changes.
setRetainInstance(true);
}


public Bitmap getData() {
return this.mBitmap;
}


public void setData(Bitmap bitmapToRetain) {
this.mBitmap = bitmapToRetain;
}
}


private class LoadImageFromNetworkAsyncTask extends AsyncTask<String,
Integer, Bitmap> {


@Override
protected Bitmap doInBackground(String... urls) {
// Simulate a burdensome load.
int sleepSeconds = 4;
for (int i = 1; i <= sleepSeconds; i++) {
SystemClock.sleep(1000); // milliseconds
publishProgress(i * 20); // Adjust for a scale to 100
}


return com.example.standardapplibrary.android.Network
.loadImageFromNetwork(
urls[0]);
}


@Override
protected void onProgressUpdate(Integer... progress) {
mLoadingProgressBar.setProgress(progress[0]);
}


@Override
protected void onPostExecute(Bitmap bitmap) {
publishProgress(100);
mRetainedFragment.setData(bitmap);
mPictureImageView.setImageBitmap(bitmap);
mLoadingProgressBar.setVisibility(View.INVISIBLE);
publishProgress(0);
}
}


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_threads);


final String retainedFragmentTag = "RetainedFragmentTag";


mPictureImageView = (ImageView) findViewById(R.id.imageView_picture);
mLoadingProgressBar = (ProgressBar) findViewById(R.id.progressBar_loading);


// Find the RetainedFragment on Activity restarts
FragmentManager fm = getFragmentManager();
// The RetainedFragment has no UI so we must reference it with a tag.
mRetainedFragment = (RetainedFragment) fm.findFragmentByTag(
retainedFragmentTag);


// if Retained Fragment doesn't exist create and add it.
if (mRetainedFragment == null) {


// Add the fragment
mRetainedFragment = new RetainedFragment();
fm.beginTransaction().add(mRetainedFragment,
retainedFragmentTag).commit();


// The Retained Fragment exists
} else {


mPictureImageView.setImageBitmap(mRetainedFragment.getData());
}
}


public void getPicture(View view) {
mLoadingProgressBar.setVisibility(View.VISIBLE);
new LoadImageFromNetworkAsyncTask().execute(
"http://i.imgur.com/SikTbWe.jpg");
}


public void clearPicture(View view) {
mRetainedFragment.setData(null);
mPictureImageView.setImageBitmap(null);
}
}

在这个例子中,库函数(上面用显式的包前缀 com.example.standard applibrary. android. Network 引用)执行真正的工作..。

public static Bitmap loadImageFromNetwork(String url) {
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream((InputStream) new URL(url)
.getContent());
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}

将后台任务需要的任何权限添加到 AndroidManifest.xml..。

<manifest>
...
<uses-permission android:name="android.permission.INTERNET" />

将您的活动添加到 AndroidManifest.xml..。

<manifest>
...
<application>
<activity
android:name=".ThreadsActivity"
android:label="@string/title_activity_threads"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.mysecondapp.MainActivity" />
</activity>

基于@Alex Lockwood 的回答和@William &@Quick drawmcgraw 的回答,我写了一个通用的解决方案。

这样就可以处理旋转,如果活动在异步任务执行期间进入后台,那么活动将在恢复后接收回调(onPreExecute,onProgressUpdate,onPostExecute & onCancled) ,因此不会抛出 IllegalStateException (参见 当活动/片段暂停时如何处理 Handler 消息)。

如果能有同样的参数类型,比如 AsyncTask (例如: AsyncTaskFragment < Params,Progress,Result >) ,那就再好不过了,但是我没有办法快速完成,而且现在没有时间。如果有人想改进,请随意!

密码:

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Message;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;


public class AsyncTaskFragment extends Fragment {


/* ------------------------------------------------------------------------------------------ */
// region Classes & Interfaces


public static abstract class Task extends AsyncTask<Object, Object, Object> {


private AsyncTaskFragment _fragment;


private void setFragment(AsyncTaskFragment fragment) {


_fragment = fragment;
}


@Override
protected final void onPreExecute() {


// Save the state :
_fragment.setRunning(true);


// Send a message :
sendMessage(ON_PRE_EXECUTE_MESSAGE, null);
}


@Override
protected final void onPostExecute(Object result) {


// Save the state :
_fragment.setRunning(false);


// Send a message :
sendMessage(ON_POST_EXECUTE_MESSAGE, result);
}


@Override
protected final void onProgressUpdate(Object... values) {


// Send a message :
sendMessage(ON_PROGRESS_UPDATE_MESSAGE, values);
}


@Override
protected final void onCancelled() {


// Save the state :
_fragment.setRunning(false);


// Send a message :
sendMessage(ON_CANCELLED_MESSAGE, null);
}


private void sendMessage(int what, Object obj) {


Message message = new Message();
message.what = what;
message.obj = obj;


Bundle data = new Bundle(1);
data.putString(EXTRA_FRAGMENT_TAG, _fragment.getTag());
message.setData(data);


_fragment.handler.sendMessage(message);
}
}


public interface AsyncTaskFragmentListener {


void onPreExecute(String fragmentTag);
void onProgressUpdate(String fragmentTag, Object... progress);
void onCancelled(String fragmentTag);
void onPostExecute(String fragmentTag, Object result);
}


private static class AsyncTaskFragmentPauseHandler extends PauseHandler {


@Override
final protected void processMessage(Activity activity, Message message) {


switch (message.what) {


case ON_PRE_EXECUTE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onPreExecute(message.getData().getString(EXTRA_FRAGMENT_TAG)); break; }
case ON_POST_EXECUTE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onPostExecute(message.getData().getString(EXTRA_FRAGMENT_TAG), message.obj); break; }
case ON_PROGRESS_UPDATE_MESSAGE : { ((AsyncTaskFragmentListener)activity).onProgressUpdate(message.getData().getString(EXTRA_FRAGMENT_TAG), ((Object[])message.obj)); break; }
case ON_CANCELLED_MESSAGE : { ((AsyncTaskFragmentListener)activity).onCancelled(message.getData().getString(EXTRA_FRAGMENT_TAG)); break; }
}
}
}


// endregion
/* ------------------------------------------------------------------------------------------ */






/* ------------------------------------------------------------------------------------------ */
// region Attributes


private Task _task;
private AsyncTaskFragmentListener _listener;
private boolean _running = false;


private static final String EXTRA_FRAGMENT_TAG = "EXTRA_FRAGMENT_TAG";
private static final int ON_PRE_EXECUTE_MESSAGE = 0;
private static final int ON_POST_EXECUTE_MESSAGE = 1;
private static final int ON_PROGRESS_UPDATE_MESSAGE = 2;
private static final int ON_CANCELLED_MESSAGE = 3;


private AsyncTaskFragmentPauseHandler handler = new AsyncTaskFragmentPauseHandler();


// endregion
/* ------------------------------------------------------------------------------------------ */






/* ------------------------------------------------------------------------------------------ */
// region Getters


public AsyncTaskFragmentListener getListener() { return _listener; }
public boolean isRunning() { return _running; }


// endregion
/* ------------------------------------------------------------------------------------------ */






/* ------------------------------------------------------------------------------------------ */
// region Setters


public void setTask(Task task) {


_task = task;
_task.setFragment(this);
}


public void setListener(AsyncTaskFragmentListener listener) { _listener = listener; }
private void setRunning(boolean running) { _running = running; }


// endregion
/* ------------------------------------------------------------------------------------------ */






/* ------------------------------------------------------------------------------------------ */
// region Fragment lifecycle


@Override
public void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setRetainInstance(true);
}


@Override
public void onResume() {


super.onResume();
handler.resume(getActivity());
}


@Override
public void onPause() {


super.onPause();
handler.pause();
}


@Override
public void onAttach(Activity activity) {


super.onAttach(activity);
_listener = (AsyncTaskFragmentListener) activity;
}


@Override
public void onDetach() {


super.onDetach();
_listener = null;
}


// endregion
/* ------------------------------------------------------------------------------------------ */






/* ------------------------------------------------------------------------------------------ */
// region Utils


public void execute(Object... params) {


_task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
}


public void cancel(boolean mayInterruptIfRunning) {


_task.cancel(mayInterruptIfRunning);
}


public static AsyncTaskFragment getRetainedOrNewFragment(AppCompatActivity activity, String fragmentTag) {


FragmentManager fm = activity.getSupportFragmentManager();
AsyncTaskFragment fragment = (AsyncTaskFragment) fm.findFragmentByTag(fragmentTag);


if (fragment == null) {


fragment = new AsyncTaskFragment();
fragment.setListener( (AsyncTaskFragmentListener) activity);
fm.beginTransaction().add(fragment, fragmentTag).commit();
}


return fragment;
}


// endregion
/* ------------------------------------------------------------------------------------------ */
}

你需要 PauseHandler:

import android.app.Activity;
import android.os.Handler;
import android.os.Message;


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;


/**
* Message Handler class that supports buffering up of messages when the activity is paused i.e. in the background.
*
* https://stackoverflow.com/questions/8040280/how-to-handle-handler-messages-when-activity-fragment-is-paused
*/
public abstract class PauseHandler extends Handler {


/**
* Message Queue Buffer
*/
private final List<Message> messageQueueBuffer = Collections.synchronizedList(new ArrayList<Message>());


/**
* Flag indicating the pause state
*/
private Activity activity;


/**
* Resume the handler.
*/
public final synchronized void resume(Activity activity) {
this.activity = activity;


while (messageQueueBuffer.size() > 0) {
final Message msg = messageQueueBuffer.get(0);
messageQueueBuffer.remove(0);
sendMessage(msg);
}
}


/**
* Pause the handler.
*/
public final synchronized void pause() {
activity = null;
}


/**
* Store the message if we have been paused, otherwise handle it now.
*
* @param msg   Message to handle.
*/
@Override
public final synchronized void handleMessage(Message msg) {
if (activity == null) {
final Message msgCopy = new Message();
msgCopy.copyFrom(msg);
messageQueueBuffer.add(msgCopy);
} else {
processMessage(activity, msg);
}
}


/**
* Notification message to be processed. This will either be directly from
* handleMessage or played back from a saved message when the activity was
* paused.
*
* @param activity  Activity owning this Handler that isn't currently paused.
* @param message   Message to be handled
*/
protected abstract void processMessage(Activity activity, Message message);
}

使用方法:

public class TestActivity extends AppCompatActivity implements AsyncTaskFragmentListener {


private final static String ASYNC_TASK_FRAGMENT_A = "ASYNC_TASK_FRAGMENT_A";
private final static String ASYNC_TASK_FRAGMENT_B = "ASYNC_TASK_FRAGMENT_B";


@Override
public void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);


Button testButton = (Button) findViewById(R.id.test_button);
final AsyncTaskFragment fragment = AsyncTaskFragment.getRetainedOrNewFragment(TestActivity.this, ASYNC_TASK_FRAGMENT_A);


testButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {


if(!fragment.isRunning()) {


fragment.setTask(new Task() {


@Override
protected Object doInBackground(Object... objects) {


// Do your async stuff


return null;
}
});


fragment.execute();
}
}
});
}


@Override
public void onPreExecute(String fragmentTag) {}


@Override
public void onProgressUpdate(String fragmentTag, Float percent) {}


@Override
public void onCancelled(String fragmentTag) {}


@Override
public void onPostExecute(String fragmentTag, Object result) {


switch (fragmentTag) {


case ASYNC_TASK_FRAGMENT_A: {


// Handle ASYNC_TASK_FRAGMENT_A
break;
}
case ASYNC_TASK_FRAGMENT_B: {


// Handle ASYNC_TASK_FRAGMENT_B
break;
}
}
}
}

快速变通(不推荐)

要避免“活动”自我销毁和创建,就要在清单文件中声明您的活动: 配置变化 = “取向 | 键盘隐藏 | 屏幕大小

  <activity
android:name=".ui.activity.MyActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="@string/app_name">

就像 医生里提到的

屏幕方向已经改变ーー用户已经旋转了设备。

注意: 如果应用程序的目标是 API 级别13或更高(如声明的那样) 属性) ,那么您应该 也声明“ screen Size”配置,因为它也会改变 当设备在纵向和横向之间切换时。

对于那些想要避免碎片的人,您可以使用 OnRetainCustomNonConfigurationInstance ()和一些连接保留在方向更改上运行的 AsyncTask。

(注意,这个方法是不推荐的 OnRetainNonConfigurationInstance ()的替代方法)。

不过,这种解决方案似乎并不常被提及。 我写了一个简单的运行示例来说明。

干杯!

public class MainActivity extends AppCompatActivity {


private TextView result;
private Button run;
private AsyncTaskHolder asyncTaskHolder;


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
result = (TextView) findViewById(R.id.textView_result);
run = (Button) findViewById(R.id.button_run);
asyncTaskHolder = getAsyncTaskHolder();
run.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
asyncTaskHolder.execute();
}
});
}


private AsyncTaskHolder getAsyncTaskHolder() {
if (this.asyncTaskHolder != null) {
return asyncTaskHolder;
}
//Not deprecated. Get the same instance back.
Object instance = getLastCustomNonConfigurationInstance();


if (instance == null) {
instance = new AsyncTaskHolder();
}
if (!(instance instanceof ActivityDependant)) {
Log.e("", instance.getClass().getName() + " must implement ActivityDependant");
}
return (AsyncTaskHolder) instance;
}


@Override
//Not deprecated. Save the object containing the running task.
public Object onRetainCustomNonConfigurationInstance() {
return asyncTaskHolder;
}


@Override
protected void onStart() {
super.onStart();
if (asyncTaskHolder != null) {
asyncTaskHolder.attach(this);
}
}


@Override
protected void onStop() {
super.onStop();
if (asyncTaskHolder != null) {
asyncTaskHolder.detach();
}
}


void updateUI(String value) {
this.result.setText(value);
}


interface ActivityDependant {


void attach(Activity activity);


void detach();
}


class AsyncTaskHolder implements ActivityDependant {


private Activity parentActivity;
private boolean isRunning;
private boolean isUpdateOnAttach;


@Override
public synchronized void attach(Activity activity) {
this.parentActivity = activity;
if (isUpdateOnAttach) {
((MainActivity) parentActivity).updateUI("done");
isUpdateOnAttach = false;
}
}


@Override
public synchronized void detach() {
this.parentActivity = null;
}


public synchronized void execute() {
if (isRunning) {
Toast.makeText(parentActivity, "Already running", Toast.LENGTH_SHORT).show();
return;
}
isRunning = true;
new AsyncTask<Void, Integer, Void>() {


@Override
protected Void doInBackground(Void... params) {
for (int i = 0; i < 100; i += 10) {
try {
Thread.sleep(500);
publishProgress(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}


@Override
protected void onProgressUpdate(Integer... values) {
if (parentActivity != null) {
((MainActivity) parentActivity).updateUI(String.valueOf(values[0]));
}
}


@Override
protected synchronized void onPostExecute(Void aVoid) {
if (parentActivity != null) {
((MainActivity) parentActivity).updateUI("done");
} else {
isUpdateOnAttach = true;
}
isRunning = false;
}
}.execute();
}
}

我已经实现了 图书馆,它可以在执行任务时解决活动暂停和重新创建的问题。

您应该实现 AsmykPleaseWaitTaskAsmykBasicPleaseWaitActivity。你的活动和背景任务将工作良好,即使你将旋转屏幕和应用程序之间切换

你可以使用装载机。检查 医生在这里