如何在ViewModel中观察FutureTask的执行结果?
问题:ViewModel中FutureTask执行结果无法通过LiveData更新界面
我尝试在ViewModel中观察FutureTask的执行结果,运行程序后,界面会休眠5秒(调试确认longJob()方法已执行,计算得出结果为3),但界面始终显示"No answer",无法获取到计算结果。
相关代码
MainActivity.java
// MainActivity.java package com.weberhwang.testlivedata; import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; import com.weberhwang.testlivedata.databinding.ActivityMainBinding; import android.os.Bundle; import android.util.Log; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; private MyViewModel myViewModel; private TextView result; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); result = binding.result; result.setText("No answer"); SummaryLiveData summaryLiveData = new SummaryLiveData(); summaryLiveData.setB(2); SummaryCallable summaryCallable = new SummaryCallable(); summaryCallable.setSummaryLiveData(summaryLiveData); myViewModel = new ViewModelProvider(this).get(MyViewModel.class); myViewModel.setSummaryCallable(summaryCallable); try { myViewModel.getAnswer().observe(this, new Observer<SummaryLiveData>() { @Override public void onChanged(SummaryLiveData s) { result.setText(String.valueOf(s.getAnswer())); } }); } catch (Exception e) { Log.d("MyLog", e.getMessage()); } } }
MyViewModel.java
// MyViewModel.java package com.weberhwang.testlivedata; import android.app.Application; import androidx.annotation.NonNull; import androidx.lifecycle.AndroidViewModel; import java.util.concurrent.FutureTask; public class MyViewModel extends AndroidViewModel { private SummaryCallable summaryCallable; public MyViewModel(@NonNull Application application) { super(application); } public SummaryLiveData getAnswer() throws Exception { FutureTask<Object> task = new FutureTask<>(summaryCallable); new Thread(task).start(); return (SummaryLiveData) task.get(); } public void setSummaryCallable(SummaryCallable summaryCallable) { this.summaryCallable = summaryCallable; } }
SummaryCallable.java
// SummaryCallable.java package com.weberhwang.testlivedata; import androidx.lifecycle.LiveData; import java.util.concurrent.Callable; public class SummaryCallable implements Callable<Object> { private SummaryLiveData summaryLiveData; @Override public Object call() throws Exception { summaryLiveData.longJob(); return summaryLiveData; } public void setSummaryLiveData(SummaryLiveData summaryLiveData) { this.summaryLiveData = summaryLiveData; } }
SummaryLiveData.java
// SummaryLiveData.java package com.weberhwang.testlivedata; import androidx.lifecycle.LiveData; public class SummaryLiveData extends LiveData { private int a = 1; private int b = 0; private int answer = 0; public void longJob() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } answer = a + b; } public void setB(int b) { this.b = b; } public int getAnswer() { return answer; } }
注意:activity_main.xml代码有误
你提供的activity_main.xml代码实际是SummaryLiveData的重复代码,这里忽略该错误,默认布局中包含id为result的TextView。
问题原因分析
- 主线程阻塞:
MyViewModel.getAnswer()中调用了task.get(),这是阻塞方法,会让主线程等待FutureTask执行完成,导致UI线程卡死,后续LiveData观察逻辑无法正常运行。 - LiveData未触发更新:
SummaryLiveData继承LiveData后,仅修改了answer变量,未调用setValue()或postValue()通知观察者数据变化,LiveData不会主动推送更新。 - LiveData使用逻辑错误:通过阻塞调用直接返回LiveData实例,违背了LiveData异步观察的设计初衷,无法实现数据变化的监听。
解决方案
方案一:用MutableLiveData持有结果,避免主线程阻塞
重新设计ViewModel,使用MutableLiveData管理结果,在FutureTask执行完成后通过postValue()更新数据。
修改后的MyViewModel.java
package com.weberhwang.testlivedata; import android.app.Application; import androidx.annotation.NonNull; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.MutableLiveData; import java.util.concurrent.FutureTask; public class MyViewModel extends AndroidViewModel { private SummaryCallable summaryCallable; private final MutableLiveData<Integer> answerLiveData = new MutableLiveData<>(); public MyViewModel(@NonNull Application application) { super(application); } public MutableLiveData<Integer> getAnswerLiveData() { return answerLiveData; } public void startLongJob() { if (summaryCallable == null) return; FutureTask<Integer> task = new FutureTask<>(() -> { summaryCallable.getSummaryLiveData().longJob(); return summaryCallable.getSummaryLiveData().getAnswer(); }); new Thread(task).start(); // 在子线程处理阻塞调用,避免卡主线程 new Thread(() -> { try { int result = task.get(); answerLiveData.postValue(result); } catch (Exception e) { e.printStackTrace(); } }).start(); } public void setSummaryCallable(SummaryCallable summaryCallable) { this.summaryCallable = summaryCallable; } }
修改MainActivity.java的观察逻辑
// ... 其他代码不变 myViewModel = new ViewModelProvider(this).get(MyViewModel.class); myViewModel.setSummaryCallable(summaryCallable); // 观察结果LiveData myViewModel.getAnswerLiveData().observe(this, result -> { if (result != null) { binding.result.setText(String.valueOf(result)); } }); // 启动后台任务 myViewModel.startLongJob(); // ...
方案二:修复自定义LiveData的通知逻辑
如果要保留自定义LiveData,需在数据变化时调用postValue()触发更新:
修改SummaryLiveData.java
package com.weberhwang.testlivedata; import androidx.lifecycle.LiveData; public class SummaryLiveData extends LiveData<Integer> { private int a = 1; private int b = 0; public void longJob() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } int answer = a + b; // 子线程中用postValue通知数据变化 postValue(answer); } public void setB(int b) { this.b = b; } }
修改MyViewModel.java
package com.weberhwang.testlivedata; import android.app.Application; import androidx.annotation.NonNull; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LiveData; import java.util.concurrent.FutureTask; public class MyViewModel extends AndroidViewModel { private SummaryLiveData summaryLiveData; public MyViewModel(@NonNull Application application) { super(application); summaryLiveData = new SummaryLiveData(); } public LiveData<Integer> getAnswerLiveData() { return summaryLiveData; } public void startLongJob(int b) { summaryLiveData.setB(b); FutureTask<Void> task = new FutureTask<>(() -> { summaryLiveData.longJob(); return null; }); new Thread(task).start(); } }
修改MainActivity.java
// ... myViewModel = new ViewModelProvider(this).get(MyViewModel.class); myViewModel.getAnswerLiveData().observe(this, answer -> { if (answer != null) { binding.result.setText(String.valueOf(answer)); } }); myViewModel.startLongJob(2); // ...
关键注意事项
- 禁止在主线程调用
FutureTask.get(),必须在子线程中处理阻塞操作,避免UI卡死。 - LiveData仅在调用
setValue()(主线程)或postValue()(子线程)时才会通知观察者,单纯修改变量不会触发更新。 - ViewModel应持有LiveData实例供UI层观察,而非直接返回同步计算结果。
内容的提问来源于stack exchange,提问作者Weber Hwang
相关产品推荐
相关产品推荐

