You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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。

问题原因分析

  1. 主线程阻塞:MyViewModel.getAnswer()中调用了task.get(),这是阻塞方法,会让主线程等待FutureTask执行完成,导致UI线程卡死,后续LiveData观察逻辑无法正常运行。
  2. LiveData未触发更新:SummaryLiveData继承LiveData后,仅修改了answer变量,未调用setValue()或postValue()通知观察者数据变化,LiveData不会主动推送更新。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 23:54:23