如何从Worker类获取ViewModel?

时间:2018-06-16 22:19:44

标签: android android-architecture-components android-viewmodel android-workmanager

  1. 我的应用程序中有一个名为MainActivity的Activivity
  2. MainActivity与OnCreate方法中的ViewModel相关联

    ...
    val someViewModel = ViewModelProviders.of(this).get(SomeViewModel::class.java)
    ...
    
  3. 我使用Workers做了一些后台工作。

    val someWork = OneTimeWorkRequestBuilder<LoginWorker>().build() WorkManager.getInstance().beginUniqueWork("SOMEWORK", ExistingWorkPolicy.REPLACE, captivePortalWork).enqueue()

  4. 问题就出现了......例如,worker做了10次循环,每次只睡了一秒......所以我们有10秒的背景工作。 我想更新ViewModel以显示后台工作的进度并每次都提升用户界面。

    A)我可以从MainActivity访问View模型并观察worker但是我只能在进度成功或失败时更新ViewModel ...这意味着我可以显示工作完成0或100%时的进度。

    B)如果我可以从Worker类访问ViewModel,我可以随时更新ViewModel。但是我不知道......这种方法在工人阶级中不起作用。

    val someWork = ViewModelProviders.of(this).get(SomeViewModel::class.java)

    如何从ViewModel课程中获得Worker

4 个答案:

答案 0 :(得分:2)

您必须将Worker输出Data的值分配给ViewModel,然后在您的Activity中观察到该数据,就像这样:

工作者代码:

public class MyWorker extends Worker {

    public static final String MY_KEY_DATA_FROM_WORKER = "MY_KEY_DATA_FROM_WORKER";

    public MyWorker(@NonNull Context appContext, @NonNull WorkerParameters workerParams) {
        super(appContext, workerParams);
    }

    @NonNull
    @Override
    public Worker.Result doWork() {
        //here do http or database requests
        String result = "my needed result";

        if (result != null) {
            setOutputData(new Data.Builder()
                    .putString(MY_KEY_DATA_FROM_WORKER, result)
                    .build());

            return Worker.Result.SUCCESS;
        }

        return Result.FAILURE;
    }
}

ViewModel的代码:

 public class MyActivityModel extends ViewModel {

    private final MutableLiveData<String> myLiveData = new MutableLiveData<String>();

    public void loadDataFromWorker(LifecycleOwner lifecycleOwner) {
        OneTimeWorkRequest myWorkerReq = new OneTimeWorkRequest.Builder(MyWorker.class)
                .build();

        WorkManager mWorkManager = WorkManager.getInstance();
        mWorkManager
                .beginWith(myWorkerReq)
                .enqueue();

        mWorkManager.getStatusByIdLiveData(myWorkerReq.getId()).observe(lifecycleOwner, new Observer<WorkStatus>() {
            @Override
            public void onChanged(WorkStatus workStatus) {
                if (workStatus.getState().isFinished()) {

                    // here you asign data to ViewModel
                    Data workOutputData = workStatus.getOutputData();
                    myLiveData.setValue(workOutputData.getString(MyWorker .MY_KEY_DATA_FROM_WORKER));
                }
            }
        });
    }

    public MutableLiveData<String> getData() {
        return myLiveData;
    }
  }

活动代码:

//...

  @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.my_activity);

    //declare model and start loading data in Worker
    MyActivityModel model = ViewModelProviders.of(this).get(MyActivityModel.class);
    model.loadDataFromWorker(this);

    //observe data from model
    model.getData().observe(this, new Observer<String>() {
        @Override
        public void onChanged(String str) {

        //here you get new String value or null if Work failed 

        }
    });
}

//...

答案 1 :(得分:2)

简而言之,您不想将ViewModel暴露给WorkManagerWorker类,而是将百分比暴露给ViewModel

WorkManager 2.3.1+添加了对设置和观察中间进度的支持。

对于使用ListenableWorker或Worker的Java开发人员,setProgressAsync()API返回一个ListenableFuture;鉴于更新过程涉及将进度信息存储在数据库中,因此更新进度是异步的。在Kotlin中,可以使用CoroutineWorker对象的setProgress()扩展功能来更新进度信息。

Source

对于Kotlin,请将CoroutineWorker与以下内容配合使用:

companion object {
    const val Progress = "Progress"
    private const val delayDuration = 1L
}

override suspend fun doWork(): Result {
    val firstUpdate = workDataOf(Progress to 0)
    val lastUpdate = workDataOf(Progress to 100)
    setProgress(firstUpdate)
    delay(delayDuration)
    setProgress(lastUpdate)
    return Result.success()
}

在您的ViewModel中,您可以像这样观察进度:

WorkManager.getInstance(applicationContext)
// requestId is the WorkRequest id
.getWorkInfoByIdLiveData(requestId)
.observe(observer, Observer { workInfo: WorkInfo? ->
        if (workInfo != null) {
            val progress = workInfo.progress
            val value = progress.getInt(Progress, 0)
            // Do something with progress information
        }
})

如果您正在使用WorkContinuation,并且想要总体百分比,我发现最简单的方法就是像这样使用workInfosLiveData

WorkContinuation.combine(workForSyncingFlights).also {
it.enqueue()
it.workInfosLiveData.asFlow().conflate().collect { listOfWorkInfo ->
    if (listOfWorkInfo.isNullOrEmpty()) return@collect
    listOfWorkInfo.forEach { workInfo ->
        if (workInfo.state.isFinished) {
            val finished = listOfWorkInfo.filter { info -> info.state.isFinished }.size
            val totalPercent = finished.toDouble().div(listOfWorkInfo.size.toDouble()) * 100
        }
    }
}

答案 2 :(得分:1)

您不能使用ViewModel。您需要使用自己的会议室数据库,并向您的UI公开LiveData以显示进度。

答案 3 :(得分:0)

非常好的Google文档中的两个附加信息。

a)如果您来这里是因为没有viewmodelscope.launch就无法运行任务,请查看Android“ Threading in WorkManager”文档。

b)此处的问题可能可以通过“ Observing intermediate Worker progress”解决。