温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Android WorkManager的示例分析

发布时间:2021-06-26 11:37:37 来源:亿速云 阅读:341 作者:小新 栏目:移动开发

这篇文章主要介绍Android WorkManager的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

一、原文翻译

WorkManager API 可以很容易的指定可延迟的异步任务。允许你创建任务,并把它交给WorkManager来立即运行或在适当的时间运行。WorkManager根据设备API的级别和应用程序状态等因素来选择适当的方式运行任务。如果WorkManager在应用程序运行时执行你的任务,它会在应用程序进程的新线程中执行。如果应用程序没有运行,WorkManager会根据设备API级别和包含的依赖项选择适当的方式安排后台任务,可能会使用JobScheduler、Firebase JobDispatcher或AlarmManager。你不需要编写设备逻辑来确定设备有哪些功能和选择适当的API;相反,你只要把它交给WorkManager让它选择最佳的方式。

Note:WorkManager适用于需要保证即使应用程序退出系统也能运行任务,比如上传应用数据到服务器。不适用于当应用程序退出后台进程能安全终止工作,这种情况推荐使用ThreadPools。

Android WorkManager的示例分析

功能:

基础功能

  • 使用WorkManager创建运行在你选择的环境下的单个任务或指定间隔的重复任务

  • WorkManager API使用几个不同的类,有时,你需要继承一些类。

  • Worker 指定需要执行的任务。有一个抽象类Worker,你需要继承并在此处工作。在后台线程同步工作的类。WorkManager在运行时实例化Worker类,并在预先指定的线程调用doWork方法(见Configuration.getExecutor())。此方法同步处理你的工作,意味着一旦方法返回,Worker被视为已经完成并被销毁。如果你需要异步执行或调用异步API,应使用ListenableWorker。如果因为某种原因工作没抢占,相同的Worker实例不会被重用。即每个Worker实例只会调用一次doWork()方法,如果需要重新运行工作单元,需要创建新的Worker。Worker最大10分钟完成执行并ListenableWorker.Result。如果过期,则会被发出信号停止。(Worker的doWork()方法是同步的,方法执行完则结束,不会重复执行,且默认超时时间是10分钟,超过则被停止。)

  • WorkRequest 代表一个独立的任务。一个WorkRequest对象至少指定哪个Worker类应该执行该任务。但是,你还可以给WorkRequest添加详细信息,比如任务运行时的环境。每个WorkRequest有一个自动生成的唯一ID,你可以使用ID来取消排队的任务或获取任务的状态。WorkRequest是一个抽象类,你需要使用它一个子类,OneTimeWorkRequest或PeriodicWorkRequest。

    • WorkRequest.Builder 创建WorkRequest对象的帮助类,你需要使用子类OneTimeWorkRequest.Builder或PeriodicWorkRequest.Builder。

    • Constraints(约束) 指定任务执行时的限制(如只有网络连接时)。使用Constraints.Builder创建Constraints对象,并在创建WorkRequest对象前传递给WorkRequest.Builder。

  • WorkManager 排队和管理WorkRequest。将WorkRequest对象传递给WorkManager来将任务添加到队列。WorkManager 使用分散加载系统资源的方式安排任务,同时遵守你指定的约束。

    • WorkManager使用一种底层作业调度服务基于下面的标注

    • 使用JobScheduler API23+

    • 使用AlarmManager + BroadcastReceiver API14-22

  • WorkInfo 包含有关特定任务的信息。WorkManager为每个WorkRequest对象提供一个LiveData。LiveData持有WorkInfo对象,通过观察LiveData,你可以确定任务的当前状态,并在任务完成后获取任何返回的值。

Android WorkManager的示例分析

二、源码简单分析

android.arch.work:work-runtime-1.0.0-beta03

WorkerManager的具体实现类是WorkManagerImpl。

WorkManager不同的方法,会创建不同的***Runnable类来执行。

下面是整体的包结构

Android WorkManager的示例分析

以EnqueueRunnable为例

@Override   public void run() {     try {       if (mWorkContinuation.hasCycles()) {         throw new IllegalStateException(             String.format("WorkContinuation has cycles (%s)", mWorkContinuation));       }       boolean needsScheduling = addToDatabase();       if (needsScheduling) {                final Context context =             mWorkContinuation.getWorkManagerImpl().getApplicationContext();         PackageManagerHelper.setComponentEnabled(context, RescheduleReceiver.class, true);         scheduleWorkInBackground();       }       mOperation.setState(Operation.SUCCESS);     } catch (Throwable exception) {       mOperation.setState(new Operation.State.FAILURE(exception));     }   }   /**    * Schedules work on the background scheduler.    */   @VisibleForTesting   public void scheduleWorkInBackground() {     WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();     Schedulers.schedule(         workManager.getConfiguration(),         workManager.getWorkDatabase(),         workManager.getSchedulers());   }

主要执行在Schedulers类中

/**    * Schedules {@link WorkSpec}s while honoring the {@link Scheduler#MAX_SCHEDULER_LIMIT}.    *    * @param workDatabase The {@link WorkDatabase}.    * @param schedulers  The {@link List} of {@link Scheduler}s to delegate to.    */   public static void schedule(       @NonNull Configuration configuration,       @NonNull WorkDatabase workDatabase,       List<Scheduler> schedulers) {     if (schedulers == null || schedulers.size() == 0) {       return;     }     ...     if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {       WorkSpec[] eligibleWorkSpecsArray = eligibleWorkSpecs.toArray(new WorkSpec[0]);       // Delegate to the underlying scheduler.       for (Scheduler scheduler : schedulers) {         scheduler.schedule(eligibleWorkSpecsArray);       }     }   }

下面看下Scheduler的子类

Android WorkManager的示例分析

最后会创建WorkerWrapper包装类,来执行我们定义的Worker类。

@WorkerThread   @Override   public void run() {     mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);     mWorkDescription = createWorkDescription(mTags);     runWorker();   }   private void runWorker() {     if (tryCheckForInterruptionAndResolve()) {       return;     }     mWorkDatabase.beginTransaction();     try {       mWorkSpec = mWorkSpecDao.getWorkSpec(mWorkSpecId);       if (mWorkSpec == null) {         Logger.get().error(             TAG,             String.format("Didn't find WorkSpec for id %s", mWorkSpecId));         resolve(false);         return;       }       // running, finished, or is blocked.       if (mWorkSpec.state != ENQUEUED) {         resolveIncorrectStatus();         mWorkDatabase.setTransactionSuccessful();         return;       }       // Case 1:       // Ensure that Workers that are backed off are only executed when they are supposed to.       // GreedyScheduler can schedule WorkSpecs that have already been backed off because       // it is holding on to snapshots of WorkSpecs. So WorkerWrapper needs to determine       // if the ListenableWorker is actually eligible to execute at this point in time.       // Case 2:       // On API 23, we double scheduler Workers because JobScheduler prefers batching.       // So is the Work is periodic, we only need to execute it once per interval.       // Also potential bugs in the platform may cause a Job to run more than once.       if (mWorkSpec.isPeriodic() || mWorkSpec.isBackedOff()) {         long now = System.currentTimeMillis();         if (now < mWorkSpec.calculateNextRunTime()) {           resolve(false);           return;         }       }       mWorkDatabase.setTransactionSuccessful();     } finally {       mWorkDatabase.endTransaction();     }     // Merge inputs. This can be potentially expensive code, so this should not be done inside     // a database transaction.     Data input;     if (mWorkSpec.isPeriodic()) {       input = mWorkSpec.input;     } else {       InputMerger inputMerger = InputMerger.fromClassName(mWorkSpec.inputMergerClassName);       if (inputMerger == null) {         Logger.get().error(TAG, String.format("Could not create Input Merger %s",             mWorkSpec.inputMergerClassName));         setFailedAndResolve();         return;       }       List<Data> inputs = new ArrayList<>();       inputs.add(mWorkSpec.input);       inputs.addAll(mWorkSpecDao.getInputsFromPrerequisites(mWorkSpecId));       input = inputMerger.merge(inputs);     }     WorkerParameters params = new WorkerParameters(         UUID.fromString(mWorkSpecId),         input,         mTags,         mRuntimeExtras,         mWorkSpec.runAttemptCount,         mConfiguration.getExecutor(),         mWorkTaskExecutor,         mConfiguration.getWorkerFactory());     // Not always creating a worker here, as the WorkerWrapper.Builder can set a worker override     // in test mode.     if (mWorker == null) {       mWorker = mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback(           mAppContext,           mWorkSpec.workerClassName,           params);     }     if (mWorker == null) {       Logger.get().error(TAG,           String.format("Could not create Worker %s", mWorkSpec.workerClassName));       setFailedAndResolve();       return;     }     if (mWorker.isUsed()) {       Logger.get().error(TAG,           String.format("Received an already-used Worker %s; WorkerFactory should return "               + "new instances",               mWorkSpec.workerClassName));       setFailedAndResolve();       return;     }     mWorker.setUsed();     // Try to set the work to the running state. Note that this may fail because another thread     // may have modified the DB since we checked last at the top of this function.     if (trySetRunning()) {       if (tryCheckForInterruptionAndResolve()) {         return;       }       final SettableFuture<ListenableWorker.Result> future = SettableFuture.create();       // Call mWorker.startWork() on the main thread.       mWorkTaskExecutor.getMainThreadExecutor()           .execute(new Runnable() {             @Override             public void run() {               try {                 mInnerFuture = mWorker.startWork();                 future.setFuture(mInnerFuture);               } catch (Throwable e) {                 future.setException(e);               }             }           });       // Avoid synthetic accessors.       final String workDescription = mWorkDescription;       future.addListener(new Runnable() {         @Override         @SuppressLint("SyntheticAccessor")         public void run() {           try {             // If the ListenableWorker returns a null result treat it as a failure.             ListenableWorker.Result result = future.get();             if (result == null) {               Logger.get().error(TAG, String.format(                   "%s returned a null result. Treating it as a failure.",                   mWorkSpec.workerClassName));             } else {               mResult = result;             }           } catch (CancellationException exception) {             // Cancellations need to be treated with care here because innerFuture             // cancellations will bubble up, and we need to gracefully handle that.             Logger.get().info(TAG, String.format("%s was cancelled", workDescription),                 exception);           } catch (InterruptedException | ExecutionException exception) {             Logger.get().error(TAG,                 String.format("%s failed because it threw an exception/error",                     workDescription), exception);           } finally {             onWorkFinished();           }         }       }, mWorkTaskExecutor.getBackgroundExecutor());     } else {       resolveIncorrectStatus();     }   }

这里使用了androidx.work.impl.utils.futures.SettableFuture,并调用了addListener方法,该回调方法会在调用set时执行。

future.addListener(new Runnable() {         @Override         @SuppressLint("SyntheticAccessor")         public void run() {           try {             // If the ListenableWorker returns a null result treat it as a failure.             ListenableWorker.Result result = future.get();             if (result == null) {               Logger.get().error(TAG, String.format(                   "%s returned a null result. Treating it as a failure.",                   mWorkSpec.workerClassName));             } else {               mResult = result;             }           } catch (CancellationException exception) {             // Cancellations need to be treated with care here because innerFuture             // cancellations will bubble up, and we need to gracefully handle that.             Logger.get().info(TAG, String.format("%s was cancelled", workDescription),                 exception);           } catch (InterruptedException | ExecutionException exception) {             Logger.get().error(TAG,                 String.format("%s failed because it threw an exception/error",                     workDescription), exception);           } finally {             onWorkFinished();           }         }       }, mWorkTaskExecutor.getBackgroundExecutor());

下面看下核心的Worker类

@Override   public final @NonNull ListenableFuture<Result> startWork() {     mFuture = SettableFuture.create();     getBackgroundExecutor().execute(new Runnable() {       @Override       public void run() {         Result result = doWork();         mFuture.set(result);       }     });     return mFuture;   }

可见,在调用doWork()后,任务执行完调用了set方法,此时会回调addListener方法。

addListener回调中主要用来判断当前任务的状态,所以如果任务被停止,此处展示捕获的异常信息。

比如调用一个任务的cancel方法,会展示下面的信息。

1. 2019-02-02 15:35:41.682 30526-30542/com.outman.study.workmanagerdemo I/WM-WorkerWrapper: Work [ id=3d775394-e0d7-44e3-a670-c3527a3245ee, tags={ com.outman.study.workmanagerdemo.SimpleWorker } ] was cancelled 2.   java.util.concurrent.CancellationException: Task was cancelled. 3.     at androidx.work.impl.utils.futures.AbstractFuture.cancellationExceptionWithCause(AbstractFuture.java:1184) 4.     at androidx.work.impl.utils.futures.AbstractFuture.getDoneValue(AbstractFuture.java:514) 5.     at androidx.work.impl.utils.futures.AbstractFuture.get(AbstractFuture.java:475) 6.     at androidx.work.impl.WorkerWrapper$2.run(WorkerWrapper.java:264) 7.     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 8.     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 9.     at java.lang.Thread.run(Thread.java:764)

以上是“Android WorkManager的示例分析”这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注亿速云行业资讯频道!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI