Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android异步任务上下文行为_Android_Multithreading_Android Asynctask_Android Context - Fatal编程技术网

Android异步任务上下文行为

Android异步任务上下文行为,android,multithreading,android-asynctask,android-context,Android,Multithreading,Android Asynctask,Android Context,我一直在Android中处理异步任务,我正在处理一个问题 举一个简单的例子,一个包含一个异步任务的活动。背景上的任务没有做任何惊人的事情,它只是休眠8秒钟 在onPostExecute()方法中的AsyncTask结束时,我只是将按钮可见性状态设置为View.VISIBLE,只是为了验证我的结果 现在,这非常有效,直到用户决定在AsyncTask工作时更改手机方向(在8秒睡眠窗口内) 我了解Android活动的生命周期,我知道活动会被破坏和重新创建 这就是问题所在。AsyncTask引用的是一个

我一直在Android中处理异步任务,我正在处理一个问题

举一个简单的例子,一个包含一个异步任务的活动。背景上的任务没有做任何惊人的事情,它只是休眠8秒钟

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

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

我了解Android活动的生命周期,我知道活动会被破坏和重新创建

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

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

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

更新:我通过保持对活动的
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);
        }
    }
}
public类主扩展活动{
私有按钮mButton=null;
私有按钮mTestButton=null;
@凌驾
创建时的公共void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mButton=(按钮)findViewById(R.id.btnStart);
mButton.setOnClickListener(新的OnClickListener(){
@凌驾
公共void onClick(视图v){
新建taskDoSomething()。执行(0l);
}
});
mtest按钮=(按钮)findViewById(R.id.btnTest);
}
私有类TaskDoSomething扩展了AsyncTask
{
@凌驾
受保护的整数doInBackground(长…参数){
Log.i(“记录器”,“启动…”);
试一试{
睡眠(8000);
}捕捉(中断异常e){
e、 printStackTrace();
}
返回0;
}
@凌驾
受保护的void onPostExecute(整数结果){
Log.i(“记录器”,“完成”);
mTestButton.setVisibility(View.VISIBLE);
}
}
}

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

这是一种让我始终避免在方向更改时破坏/重新创建活动的方式

要执行此操作,请将其添加到清单文件中的
标记中:

android:configChanges="orientation|keyboardHidden" 
并覆盖活动类中更改的OnConfiguration:

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

AsyncTask的设计不允许在拆除并重新启动活动后重用。内部处理程序对象将变得过时,正如您所述。在Romain Guy的Shelfs示例中,他简单地取消了当前正在运行的任何AsyncTask,然后在方向更改后重新启动新的AsyncTask


可以将线程转移到新活动,但这会增加很多管道。目前还没有达成共识的方法,但您可以在此处阅读我的方法:

为了避免这种情况,您可以在此处使用givin的答案:

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


您可以在此处看到一个示例:

如果您只需要一个上下文,而不将其用于ui内容,则只需将ApplicationContext传递给您的AsyncTask即可。例如,您通常需要用于系统资源的上下文

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


您还应该将AsyncTask作为独立于上述活动的公共类,它使测试更加容易。不幸的是,Android编程往往会强化不良做法,官方的例子也没有帮助。

你的方法可行,但你对这种方法有很好的反驳吗?为什么默认情况下操作系统会销毁并重新创建活动?换言之,我做了你提议的事会“失去”什么?顺便说一句,它的工作正如预期的那样。谢谢你的回复。据我所知,你会失去的主要东西将是有不同的横向和纵向模式的活动布局的能力。你可能会“失去”其他东西,但我不知道它们是什么。除非你知道自己在做什么,否则谷歌通常会建议你不要使用这种方法。我知道,例如,一个来电也会导致活动结束。如果是这样的话,你仍然会有问题,对吗?我建议避免这种解决方案。问题#1-配置更改时可以重新启动活动。除了方向改变之外,这可能是字体大小、区域设置等的改变。问题2-异步任务有一个对活动的引用。如果完成()活动,它将保留在内存中,直到AsyncTask完成。如果用户启动活动、按下返回键、再次启动等,则内存中可能存在同一活动的多个实例,并可能导致OOM错误。问题#3-您不能使用不同的资源进行纵向/横向定向。写“保持对活动的弱引用”而不是不在代码中进行有点误导。更糟糕的是,你进一步说“当配置发生变化时进行切换”,这真的没什么意思…切换什么?