33

我一直在使用 Android 中的 AsyncTasks,我正在处理一个问题。

举一个简单的例子,一个带有一个 AsyncTask 的 Activity。后台的任务没有做任何壮观的事情,它只是休眠了 8 秒。

在 onPostExecute() 方法中的 AsyncTask 结束时,我只是将按钮可见性状态设置为 View.VISIBLE,只是为了验证我的结果。

现在,这非常有效,直到用户决定在 AsyncTask 工作时(在 8 秒的睡眠窗口内)改变他的手机方向。

我了解 Android 活动的生命周期,并且知道活动会被销毁并重新创建。

这就是问题所在。 AsyncTask 指的是一个按钮,并且显然包含对首先启动 AsyncTask 的上下文的引用。

我希望,这个旧上下文(因为用户导致方向更改)要么变为空,并且 AsyncTask 抛出一个 NPE 以引用它试图使其可见的按钮。

相反,没有抛出 NPE,AsyncTask 认为按钮引用不为空,将其设置为可见。结果?屏幕上什么都没有发生!

更新:我已经通过保持WeakReference活动并在配置更改发生时切换来解决这个问题。这很麻烦。

这是代码:

public class Main extends Activity {

    private Button mButton = null;
    private Button mTestButton = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mButton = (Button) findViewById(R.id.btnStart);
        mButton.setOnClickListener(new OnClickListener () {
            @Override
            public void onClick(View v) {
                new taskDoSomething().execute(0l);
            }
        });
        mTestButton = (Button) findViewById(R.id.btnTest);   
    }

    private class TaskDoSomething extends AsyncTask<Long, Integer, Integer> 
    {
        @Override
        protected Integer doInBackground(Long... params) {
            Log.i("LOGGER", "Starting...");
            try {
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 0;
        }

        @Override
        protected void onPostExecute(Integer result) {
            Log.i("LOGGER", "...Done");
            mTestButton.setVisibility(View.VISIBLE);
        }
    }
}

尝试执行它并在 AsyncTask 工作时更改您的手机方向。

4

4 回答 4

23

AsyncTask 并非设计为在 Activity 被拆除并重新启动后重用。正如您所说,内部 Handler 对象变得陈旧。在 Romain Guy 的 Shelves 示例中,他简单地取消了任何当前正在运行的 AsyncTask,然后在方向更改后重新启动新的。

可以将您的线程移交给新的活动,但它增加了很多管道。没有普遍同意的方法,但你可以在这里阅读我的方法:http: //foo.jasonhudgins.com/2010/03/simple-progressbar-tutorial.html

于 2010-05-30T17:39:04.910 回答
3

如果您只需要一个上下文并且不会将它用于 ui 的东西,您可以简单地将 ApplicationContext 传递给您的 AsyncTask。例如,您通常需要系统资源的上下文。

不要尝试从 AsyncTask 更新 UI,并尽量避免自己处理配置更改,因为它可能会变得混乱。为了更新 UI,您可以注册一个广播接收器并发送一个广播。

如上所述,您还应该将 AsyncTask 作为与活动分开的公共类,它使测试变得更加容易。不幸的是,Android 编程经常会强化不良做法,而官方示例也无济于事。

于 2012-07-11T09:37:55.670 回答
2

这是导致我始终防止我的 Activity 在方向更改时被销毁/重新创建的类型。

为此,请将其添加到<Activity>清单文件中的标记中:

android:configChanges="orientation|keyboardHidden" 

并在您的 Activity 类中覆盖 onConfigurationChanged:

@Override
public void onConfigurationChanged(final Configuration newConfig)
{
    // Ignore orientation change to keep activity from restarting
    super.onConfigurationChanged(newConfig);
}
于 2010-01-23T21:05:49.620 回答
2

为避免这种情况,您可以在此处使用答案:https ://stackoverflow.com/a/2124731/327011

但是,如果您需要销毁活动(纵向和横向的不同布局),您可以将 AsyncTask 设为公共类(在此处阅读为什么它不应该是私有Android:AsyncTask 建议:私有类还是公共类?)然后创建一个方法 setActivity 在销毁/创建时设置对当前活动的引用。

你可以在这里看到一个例子:Android AsyncTask in external class

于 2011-12-07T15:02:44.843 回答