Android AsyncTask 上下文行为

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2124701/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 04:38:30  来源:igfitidea点击:

Android AsyncTask context behavior

androidmultithreadingandroid-asynctaskandroid-context

提问by dnkoutso

I've been working with AsyncTasks in Android and I am dealing with an issue.

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

Take a simple example, an Activity with one AsyncTask. The task on the background does not do anything spectacular, it just sleeps for 8 seconds.

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

At the end of the AsyncTask in the onPostExecute() method I am just setting a button visibility status to View.VISIBLE, only to verify my results.

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

Now, this works great until the user decides to change his phones orientation while the AsyncTask is working (within the 8 second sleep window).

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

I understand the Android activity life cycle and I know the activity gets destroyed and recreated.

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

This is where the problem comes in. The AsyncTask is referring to a button and apparently holds a reference to the context that started the AsyncTask in the first place.

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

I would expect, that this old context (since the user caused an orientation change) to either become null and the AsyncTask to throw an NPE for the reference to the button it is trying to make visible.

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

Instead, no NPE is thrown, the AsyncTask thinks that the button reference is not null, sets it to visible. The result? Nothing is happening on the screen!

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

Update:I have tackled this by keeping a WeakReferenceto the activity and switching when a configuration change happens. This is cumbersome.

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

Here's the code:

这是代码:

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);
        }
    }
}

Try executing it and while the AsyncTask is working change your phones orientation.

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

采纳答案by jasonhudgins

AsyncTask is not designed to be reused once an Activity has been torn down and restarted. The internal Handler object becomes stale, just like you stated. In the Shelves example by Romain Guy, he simple cancels any currently running AsyncTask's and then restarts new ones post-orientation change.

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

It is possible to hand off your Thread to the new Activity, but it adds a lot of plumbing. There is no generally agreed on way to do this, but you can read about my method here : http://foo.jasonhudgins.com/2010/03/simple-progressbar-tutorial.html

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

回答by Ralph Mueller

If you only need a context and won't use it for ui stuff you can simply pass the ApplicationContext to your AsyncTask.You often need the context for system resources, for example.

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

Don't try to update the UI from an AsyncTask and try to avoid handling configuration changes yourself as it can get messy. In order to update the UI you could register a Broadcast receiver and send a Broadcast.

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

You should also have the AsyncTask as a separate public class from the activity as mentioned above, it makes testing a lot easier. Unfortunately Android programming often reinforces bad practices and the official examples are not helping.

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

回答by neteinstein

To avoid this you can use the answer givin here: https://stackoverflow.com/a/2124731/327011

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

But if you need to destroy the activity (different layouts for portrait and landscape) you can make the AsyncTask a public class (Read here why it shouldn't be private Android: AsyncTask recommendations: private class or public class?) and then create a method setActivity to set the reference to the current activity whenever it is destroyed/created.

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

You can see an example here: Android AsyncTask in external class

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

回答by Mark B

This is the type of thing that leads me to always prevent my Activity from being destroyed/recreated on orientation change.

这是一种让我总是防止我的 Activity 在方向改变时被破坏/重新创建的事情。

To do so add this to your <Activity>tag in your manifest file:

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

android:configChanges="orientation|keyboardHidden" 

And override onConfigurationChanged in your Activity class:

并在您的 Activity 类中覆盖 onConfigurationChanged:

@Override
public void onConfigurationChanged(final Configuration newConfig)
{
    // Ignore orientation change to keep activity from restarting
    super.onConfigurationChanged(newConfig);
}