如何观察 Android WorkManager Workers

huangapple go评论91阅读模式
英文:

How to Observer Android WorkManager Workers

问题

我的当前Android应用程序使用以下代码来处理后台工作。

archWorkerRuntimeVersion = '2.3.0-beta02'

api "androidx.work:work-runtime:$archWorkerRuntimeVersion"
api "androidx.work:work-runtime-ktx:$archWorkerRuntimeVersion"

在工作程序运行时,我会显示一个工具栏进度旋转器,以向用户表示应用程序正在繁忙。

我的应用程序由多个活动组成,我使用Android JetPack ViewModels来管理工作程序。

我启动工作如下:

val myWorkRequest: OneTimeWorkRequest =
    OneTimeWorkRequest.Builder(MyWorker::class.java)
        .addTag(WORK_IN_PROGRESS_TAG + "${UUID.randomUUID()}").build()

WorkManager.getInstance(applicationContext)
    .beginUniqueWork(
        UNIQUE_WORK_NAME,
        ExistingWorkPolicy.KEEP,
        myWorkRequest
    )
    .enqueue()

我定义了一个观察者如下:

private val observer = Observer<WorkInfo> { workInfo ->
    
    when(workInfo.state) {
        WorkInfo.State.RUNNING -> Log.e("OBSERVING", "${UUID.randomUUID()} :: 显示进度旋转器")
        WorkInfo.State.ENQUEUED -> Log.e("OBSERVING", "${UUID.randomUUID()} :: 显示进度旋转器")
        WorkInfo.State.SUCCEEDED -> Log.e("OBSERVING", "${UUID.randomUUID()} :: 停止显示进度旋转器")
        WorkInfo.State.FAILED -> Log.e("OBSERVING", "${UUID.randomUUID()} :: 停止显示进度旋转器")
        WorkInfo.State.BLOCKED -> Log.e("OBSERVING", "${UUID.randomUUID()} :: 显示进度旋转器")
        WorkInfo.State.CANCELLED -> Log.e("OBSERVING", "${UUID.randomUUID()} :: 停止显示进度旋转器")
    }
}

我观察工作如下:

WorkManager.getInstance(applicationContext)
            .getWorkInfoByIdLiveData(myWorkRequest.id)
            .observe(lifeCycleOwner, observer)

并在我的ViewModel中记住当前的workId:

class MyViewModel : ViewModel() {
    
    private var currentWorkInfoId: UUID? = null

    fun startWorkTags(lifeCycleOwner: LifecycleOwner, applicationContext: Context) {
    
        val myWorkRequest: OneTimeWorkRequest =
            OneTimeWorkRequest.Builder(MyWorker::class.java)
                .addTag(WORK_IN_PROGRESS_TAG + "${UUID.randomUUID()}").build()

        WorkManager.getInstance(applicationContext)
            .beginUniqueWork(
                UNIQUE_WORK_NAME,
                ExistingWorkPolicy.KEEP,
                myWorkRequest
            )
            .enqueue()

        currentWorkInfoId = myWorkRequest.id
   }
}

我有以下问题:

1). 因为我使用了ExistingWorkPolicy.KEEP,我需要知道是否已经有后台工作处于活动状态,以便我不会记住错误的工作id。目前,我无条件地记住了我尝试enqueue()的最后一个工作id。有没有办法检测ExistingWorkPolicy.KEEP是否导致没有启动新工作?

2). 因为我在ViewModel中存储了当前的工作id,当用户退出我的应用程序时,我会丢失这个id。当用户重新进入我的应用程序时,如何发现是否有活动的工作程序?

对于问题2)。我尝试使用以下方法:

WorkManager.getInstance(applicationContext)
            .getWorkInfosForUniqueWorkLiveData(UNIQUE_WORK_NAME)
            .observe(lifeCycleOwner, observerUnique)

但这种方法“看到”了所有先前的工作程序执行,我如何识别最新的工作实例?

英文:

My current Android Application employs

archWorkerRuntimeVersion = &#39;2.3.0-beta02&#39;

api &quot;androidx.work:work-runtime:$archWorkerRuntimeVersion&quot;
api &quot;androidx.work:work-runtime-ktx:$archWorkerRuntimeVersion&quot;

to process background work.

While the worker is running I display a toolbar progress spinner to signal to my users that the App is "Busy".

My App consists of multiple activities and I employ Android JetPack ViewModels to manage the Worker.

I start the work as follows:-

val myWorkRequest: OneTimeWorkRequest =
    OneTimeWorkRequest.Builder(MyWorker::class.java)
        .addTag(WORK_IN_PROGRESS_TAG + &quot;${UUID.randomUUID()}&quot;).build()

    WorkManager.getInstance(applicationContext)
        .beginUniqueWork(
            UNIQUE_WORK_NAME,
            ExistingWorkPolicy.KEEP,
            myWorkRequest
        )
        .enqueue()

I have an observer defined as follows:-

private val observer = Observer&lt;WorkInfo&gt; { workInfo -&gt;

    when(workInfo.state) {
        WorkInfo.State.RUNNING -&gt; Log.e(&quot;OBSERVING&quot;, &quot;${UUID.randomUUID()} :: show Progress Spinner&quot;)
        WorkInfo.State.ENQUEUED -&gt; Log.e(&quot;OBSERVING&quot;, &quot;${UUID.randomUUID()} :: show Progress Spinner&quot;)
        WorkInfo.State.SUCCEEDED -&gt; Log.e(&quot;OBSERVING&quot;, &quot;${UUID.randomUUID()} :: stop showing Progress Spinner&quot;)
        WorkInfo.State.FAILED -&gt; Log.e(&quot;OBSERVING&quot;, &quot;${UUID.randomUUID()} :: stop showing Progress Spinner&quot;)
        WorkInfo.State.BLOCKED -&gt; Log.e(&quot;OBSERVING&quot;, &quot;${UUID.randomUUID()} :: show Progress Spinner&quot;)
        WorkInfo.State.CANCELLED -&gt; Log.e(&quot;OBSERVING&quot;, &quot;${UUID.randomUUID()} :: stop showing Progress Spinner&quot;)
    }

}

I observe the work as follows:-

 WorkManager.getInstance(applicationContext)
            .getWorkInfoByIdLiveData(myWorkRequest.id)
            .observe(lifeCycleOwner, observer)

and remember the current workId in my view model

class MyViewModel : ViewModel() {

    private var currentWorkInfoId: UUID? = null


  fun startWorkTags(lifeCycleOwner: LifecycleOwner, applicationContext: Context) {

        val myWorkRequest: OneTimeWorkRequest =
    OneTimeWorkRequest.Builder(MyWorker::class.java)
        .addTag(WORK_IN_PROGRESS_TAG + &quot;${UUID.randomUUID()}&quot;).build()

    WorkManager.getInstance(applicationContext)
        .beginUniqueWork(
            UNIQUE_WORK_NAME,
            ExistingWorkPolicy.KEEP,
            myWorkRequest
        )
        .enqueue()

        currentWorkInfoId = myWorkRequest.id
   }
}

I have the following issues

1). As I am using ExistingWorkPolicy.KEEP, I need to know when there is already background work active so that I do not remember the wrong work id. Currently I unconditionally remember the last work id I attempted to enqueue(). Is there any way I can detect when ExistingWorkPolicy.KEEP results in no new work being started?

2). As I am storing the current work id in my viewModel when the user exits my app I lose this id. How can I discover if I have an active worker when my users re enter my App?

For issue 2). I have tried using

 WorkManager.getInstance(applicationContext)
            .getWorkInfosForUniqueWorkLiveData(UNIQUE_WORK_NAME)
            .observe(lifeCycleOwner, observerUnique)

However this approach "sees" all previous worker executions, how am I to identify the latest worker instance?

答案1

得分: 2

鉴于您只想在 Worker 运行时显示进度旋转器,您可以实现一个更简单的解决方案,其中您的观察者只需检查是否有一个处于非终态的 Worker。

observe(this, listOfWorkInfo -> {
    // 如果没有匹配的工作信息,不执行任何操作
    if (listOfWorkInfo == null || listOfWorkInfo.isEmpty()) {
        return;
    }

    boolean showProgress = false;
    for (WorkInfo workInfo : listOfWorkInfo) {
        if (!(workInfo.getState().isFinished())) {
            showProgress = true;
        }
    }

    if (showProgress) {
        showSpinner();
    } else {
        hideSpinner();
    }
});

在这里,您可以使用 getWorkInfosForUniqueWorkLiveData() 获取 WorkInfos 列表。

另一种替代方案是使用您已经在 WorkManager v2.3.0 中使用的新的 Progress API

思路是,您在 Worker 启动时(在构造函数中)立即发布进度:

import android.content.Context;
import androidx.annotation.NonNull;
import androidx.work.Data;
import androidx.work.Worker;
import androidx.work.WorkerParameters;

public class ProgressWorker extends Worker {

    private static final String PROGRESS = "PROGRESS";
    private static final long DELAY = 1000L;

    public ProgressWorker(
        @NonNull Context context,
        @NonNull WorkerParameters parameters) {
        super(context, parameters);
        // 将初始进度设置为 0
        setProgressAsync(new Data.Builder().putInt(PROGRESS, 1).build());
    }

    @NonNull
    @Override
    public Result doWork() {
        try {
            // 执行工作
            Thread.sleep(DELAY);
        } catch (InterruptedException exception) {
            // ... 处理异常
        }
        return Result.success();
    }
}

您可以在您已经拥有的观察者中使用 workInfo.getProgress() 检索进度。这种情况下的好处在于,进度信息仅在 Worker 正在运行时可用。您仍然可以请求来自 UniqueWorkName 的所有 WorkInfos,但只有在 Worker 运行时才会有进度。

WorkManager 将在 Worker 处于最终状态时负责删除进度信息。

英文:

Given that you just want to show a progress spinner when the Worker is running, you may implement a simpler solution, where your observer just check if there's one Worker in a non-final state.

observe(this, listOfWorkInfo -&gt; {
    // If there are no matching work info, do nothing
    if (listOfWorkInfo == null || listOfWorkInfo.isEmpty()) {
        return;
    }

    boolean showProgress = false;
    for (WorkInfo workInfo : listOfWorkInfo) {
        if (!(workInfo.getState().isFinished())) {
            showProgress = true;
        }
    }

    if (showProgress) {
        showSpinner();
    } else {
        hideSpinner();
    }

}

Where you get the list of WorkInfos with getWorkInfosForUniqueWorkLiveData().

An alternative solution is also to use the new Progress API introduced in WorkManager v2.3.0 that you're already using.

The idea is that you publish a progress as soon as your worker starts (in the constructor):

import android.content.Context;
import androidx.annotation.NonNull;
import androidx.work.Data;
import androidx.work.Worker;
import androidx.work.WorkerParameters;

public class ProgressWorker extends Worker {

    private static final String PROGRESS = &quot;PROGRESS&quot;;
    private static final long DELAY = 1000L;

    public ProgressWorker(
        @NonNull Context context,
        @NonNull WorkerParameters parameters) {
        super(context, parameters);
        // Set initial progress to 0
        setProgressAsync(new Data.Builder().putInt(PROGRESS, 1).build());
    }

    @NonNull
    @Override
    public Result doWork() {
        try {
            // Doing work.
            Thread.sleep(DELAY);
        } catch (InterruptedException exception) {
            // ... handle exception
        }
        return Result.success();
    }
}

You can retrieve the progress in the observer you already have using workInfo.getProgress(). The benefit in this case is coming from the fact that the progress information is only available as long as the worker is running.
You can still request all the WorkInfos from the UniqueWorkName, but you're going to have a progress only if the worker is running.

WorkManager will take care of removing the progress information when the worker is in a final state.

huangapple
  • 本文由 发表于 2020年1月3日 19:21:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/59577708.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定