如何在屏幕旋转期间处理AsyncTask?

时间:2010-04-12 09:17:47

标签: android android-asynctask screen-rotation

我读了很多关于如何保存我的实例状态或如何处理我在屏幕旋转期间被破坏的活动。

似乎有很多可能性,但我还没弄清楚哪一个最适合检索AsyncTask的结果。

我有一些AsyncTasks只是再次启动并调用活动的isFinishing()方法,如果活动正在完成,他们不会更新任何内容。

问题是我有一个任务向Web服务发出请求失败或成功的任务,重新启动任务会导致用户的经济损失。

你会如何解决这个问题?可能的解决方案有哪些优点或缺点?

13 个答案:

答案 0 :(得分:46)

您可以查看code.google.com/p/shelves处理AsyncTask和方向更改的方式。有多种方法可以做到这一点,我在这个应用程序中选择的方法是取消当前正在运行的任务,保存其状态并在创建新Activity时启动具有已保存状态的新状态。它很容易做到,效果很好,作为奖励,它会在用户离开应用程序时停止执行任务。

您还可以使用onRetainNonConfigurationInstance()AsyncTask传递给新的Activity(请注意不要以这种方式泄露之前的Activity。)

答案 1 :(得分:10)

这是我见过的关于Android的最有趣的问题!!!实际上我在过去几个月里一直在寻找解决方案。还是没有解决。

小心,简单地覆盖

android:configChanges="keyboardHidden|orientation"

东西还不够。

考虑用户在AsyncTask运行时接到电话的情况。您的请求已由服务器处理,因此AsyncTask正在等待响应。在这一刻你的应用程序进入后台,因为手机应用程序刚刚进入前台。操作系统可能会因为它在后台而终止您的活动。

答案 2 :(得分:7)

我的第一个建议是确保您确实需要在屏幕旋转时重置您的活动(默认行为)。每次我遇到旋转问题时,我都会将此属性添加到AndroidManifest.xml中的<activity>标记中,并且很好。

android:configChanges="keyboardHidden|orientation"

它看起来很奇怪,但是如果你不提供一个方法,它会将它传递给你的onConfigurationChanged()方法,除了重新测量布局之外什么也没做什么,这似乎是一种非常恰当的方式在大部分时间处理轮换。

答案 3 :(得分:6)

为什么不总是在Android提供的Singleton上保留对当前AsyncTask的引用?

每当任务开始时,在PreExecute或构建器上,您都可以定义:

((Application) getApplication()).setCurrentTask(asyncTask);

每当它完成时,你将它设置为null。

通过这种方式,你总有一个参考资料可以让你做一些像onCreate或onResume这样适合你特定逻辑的东西:

this.asyncTaskReference = ((Application) getApplication()).getCurrentTask();

如果它为null,则表示目前没有运行!

: - )

答案 4 :(得分:4)

最合适的方法是使用片段来保留异步任务的实例,而不是轮换。

以下是一个非常简单示例的链接,可让您轻松将此技术集成到您的应用中。

https://gist.github.com/daichan4649/2480065

答案 5 :(得分:3)

Pro android 4。作者提出了一个很好的方法,你应该使用weak reference

Weak reference note

答案 6 :(得分:3)

就我而言,最好通过onRetainNonConfigurationInstance将asynctask与当前的Activity对象分离,并在方向更改后将其绑定到新的Activity对象。 Here我找到了一个非常好的例子,说明如何使用AsyncTask和ProgressDialog。

答案 7 :(得分:2)

Android:后台处理/配置更改的Async Opeartion

在后台进程中维护异步opeartion的状态: 你可以借助片段。

请参阅以下步骤:

步骤1:创建一个无头片段,让我们说后台任务,并在其中添加一个私有异步任务类。

步骤2(可选步骤):如果您想在活动之上加载游标,请使用以下代码:

步骤3:在您的主要Activity实现第1步中定义的BackgroundTaskCallbacks接口

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
}

}

答案 8 :(得分:1)

要考虑的一件事是AsyncTask的结果是否只应用于启动任务的活动。如果是,那么Romain Guy's answer是最好的。如果它应该可用于您的应用程序的其他活动,那么在onPostExecute中您可以使用LocalBroadcastManager

LocalBroadcastManager.getInstance(getContext()).sendBroadcast(new Intent("finished"));

在活动暂停时发送广播时,您还需要确保活动正确处理情况。

答案 9 :(得分:1)

看看这个post。当一个示例应用程序中发生屏幕旋转时,此Post涉及AsyncTask执行长时间运行操作和内存泄漏。示例应用可在source forge

上找到

答案 10 :(得分:0)

我的解决方案。

在我的情况下,我有一个具有相同上下文的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();
}

答案 11 :(得分:0)

CLUSTERS (collections)
+----+-----------+----+---------+-----------------+-----+------------------+--------------------+--------------------+
|#   |NAME       |  ID|CLASS    |CONFLICT-STRATEGY|COUNT|   OWNER_SERVER   |   OTHER_SERVERS    |AUTO_DEPLOY_NEW_NODE|
+----+-----------+----+---------+-----------------+-----+------------------+--------------------+--------------------+
|5   |event      |  17|event    |                 |   11|orientdb-lab-node2|[orientdb-lab-node1]|        true        |
|6   |event_1    |  18|event    |                 |    3|orientdb-lab-node1|[orientdb-lab-node2]|        true        |
|7   |event_2    |  19|event    |                 |    2|orientdb-lab-node1|[orientdb-lab-node2]|        true        |
|8   |event_3    |  20|event    |                 |    9|orientdb-lab-node2|[orientdb-lab-node1]|        true        |
+----+-----------+----+---------+-----------------+-----+------------------+--------------------+--------------------+
|    |TOTAL      |    |         |                 |   25|                  |                    |                    |
+----+-----------+----+---------+-----------------+-----+------------------+--------------------+--------------------+

答案 12 :(得分:0)

你也可以添加 机器人:configChanges =&#34; keyboardHidden |取向|屏幕尺寸&#34;

到您的清单示例我希望它有所帮助

<%= f.label :'date_start:' %>
<%= f.date_field :date_start %>

<%= f.label :'date_end:' %>
<%= f.date_field :date_end %>