下面的简单示例代码具有一个异步任务,该任务重复睡眠2秒钟,然后使用onResultAvailable触发回调函数"some text .."(我在这里用来模拟接收消息的Websocket线程...)。

onResultAvailable类中的MyViewModel方法应该使用text更新变量MutableLiveData<String>(它是postValue)(因为它是从后台线程调用的)。

因此,该代码应该每两秒更新一次text变量,并通过观察者在MyTestActivity中显示文本;因此应该显示以下内容,该内容在首次使用时可以正常工作:

Some text 0
Some text 1
Some text 2
... etc


但是,当我按下后退按钮,然后再次打开MyTestActivity时,text变量不再被postvalue修改,并且不再调用观察者。请注意,使用setvalue修改的其他变量仍然表现出预期的效果。

有什么想法为什么postvalue第一次可以从后台线程正常工作,但是当活动被销毁然后重新创建时,它不再起作用了?在这种情况下,如何更新text变量的值?

MainActivity.java

public class MainActivity extends AppCompatActivity {
    Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = findViewById(R.id.myButton);

        button.setOnClickListener(v -> {
            startActivity(new Intent(MainActivity.this, MyTestActivity.class));
        });
    }
}


MyTestActivity.java

public class MyTestActivity extends AppCompatActivity {
    TextView myTextView, myTextViewTime;
    Button myButton;
    MyViewModel vm;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_test);
        myTextView = findViewById(R.id.myTextView);
        myTextViewTime = findViewById(R.id.myTextViewTime);
        myButton = findViewById(R.id.myButton);

        vm = ViewModelProviders.of(this).get(MyViewModel.class);

        vm.getText().observe(this, text -> {
            myTextView.setText(text);
            Log.d("DEBUG", "observed: " + text);
        });

        vm.getTimestamp().observe(this, strTs -> {
            myTextViewTime.setText(strTs);
            Log.d("DEBUG", "observed: " + strTs);
        });

        myButton.setOnClickListener(v -> {
            vm.setTimestamp("" + System.currentTimeMillis());
        });
    }
}


MyViewModel.java

public class MyViewModel extends ViewModel implements MyAsyncTaskDelegate {
    private MutableLiveData<String> text;
    private MutableLiveData<String> timestamp;
    private MyAsyncTask task;

    public MyViewModel() {
        text = new MutableLiveData<>();
        text.setValue("Hello World!");

        timestamp =  new MutableLiveData<>();
        timestamp.setValue(""+System.currentTimeMillis());

        task = new MyAsyncTask(this);
        task.execute();
    }

    public LiveData<String> getText() {
        return text;
    }

    public LiveData<String> getTimestamp() {
        return timestamp;
    }

    public void setTimestamp(String ts) {
        timestamp.setValue(ts);
    }

    @Override
    public void onResultAvailable(String result) {
        text.postValue(result);
    }
}


MyAsyncTask.java

public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
    private MyAsyncTaskDelegate delegate;

    MyAsyncTask(MyAsyncTaskDelegate delegate) {
        this.delegate = delegate;
    }

    @Override
    protected Void doInBackground(Void... voids) {
        for(int i = 0; i < 100; i++) {
            try {
                Thread.sleep(2000);
                delegate.onResultAvailable("Some text " + i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}


MyAsyncTaskDelegate.java

public interface MyAsyncTaskDelegate {
    void onResultAvailable(String result);
}

最佳答案

有什么想法为什么后值第一次在后台线程中可以正常工作,但是在活动被销毁然后重新创建时不再起作用?


简而言之:发生这种情况是因为MyAsyncTask.doInBackground()将无限循环,对于大多数Android版本,没有并行执行AsyncTask的情况。

MyTestActivity.onCreate()中,您将生成一个MyViewModel的新实例,该实例将启动其自己的MyAsyncTask。因为MyAsyncTask的第一个实例永远不会结束,所以第二个实例永远不会真正开始。 MyAsyncTask的第一个实例仍然保留在MyViewModel的第一个实例(又名MyAsyncTaskDelegate)上。但是由于第一个MyViewModel实例与活动的UI元素没有任何关系,因此更新不会显示在屏幕上。

您可以通过在各种方法中设置断点并比较“实例号”(在每个类名称后附加一个'@'和一个唯一的数字)来使用调试器进行验证。

请注意,仅当您通过按BACK离开MyTestActivity时,才会出现有问题的行为。如果旋转设备,更新将继续。

这是“按预期工作”。通过按BACK(返回),您指示您不再需要MyTestActivity的当前实例:它不会保留在后退堆栈中,并且可以并且将被完全销毁。

AsyncTask是另一种情况:它是MyViewModel的成员,另一方面持有对MyViewModel的引用。可以说存在一个参考死锁:只有在整个周围处理停止时,它才会停止。


  在这种情况下,如何更新文本变量的值?


覆盖onBackPressed()中的MyTestActivity并停止执行MyAsyncTask。有关如何操作,请参见AsyncTask.cancel()上的文档。

08-04 23:19