下面的简单示例代码具有一个异步任务,该任务重复睡眠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()上的文档。