JetPack WorkManager源码分析
2021/12/7 22:16:53
本文主要是介绍JetPack WorkManager源码分析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
前言
Android中执行后台任务有JobScheduler、Loader、Service等方案 ,WorkManager的出现用来替换以上所有的 Android 后台任务方案,为后台任务提供了一套统一的解决方案,保证了api的一致性和稳定性,同时谷歌在开发WorkManager时也考虑到后台任务对电池续航的影响。WorkManager 能保证任务一定会执行,即使用户导航离开屏幕、退出应用或重启设备也不影响。
基本使用
使用 WorkManager 需要继承 Worker
类,后台任务在doWork()
方法中实现
doWork()
方法的返回值Resutl有三种类型:
Result.success()
:执行成功。Result.failure()
:执行失败。Result.retry()
:执行失败,重试。
class MainWorker (context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) { companion object { const val TAG = "MainWorker" } @SuppressLint("RestrictedApi") override fun doWork(): Result { Log.d(TAG, "MainWorker doWork: 后台任务执行了") return Result.Success() // doWork成功 执行任务完毕 } }
定义任务后,可以使用Constraints设置约束条件,例如定义手机必须联网、必须在充电中、必须是空闲时才执行该后台任务,最后须用 WorkManager 进行调度该任务才能运行。更多进阶使用可以看这里。
fun testBackgroundWork(view: View?) { //设置约束条件 val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) // 必须是联网中 .setRequiresCharging(true) // 必须是充电中 .setRequiresDeviceIdle(true) // 必须是空闲时 .build() // 请求对象 val request = OneTimeWorkRequest.Builder(MainWorker::class.java) .setConstraints(constraints) // Request 关联 约束条件 .build() // 加入队列 WorkManager.getInstance(this).enqueue(request) }
源码
1.初始化
上面例子中是通过WorkManager.getInstance获得单例对象,然后再执行后台任务,看一下这个方法的源码。
public static @NonNull WorkManagerImpl getInstance(@NonNull Context context) { synchronized (sLock) { WorkManagerImpl instance = getInstance();//这里不为空,直接返回 if (instance == null) { Context appContext = context.getApplicationContext(); if (appContext instanceof Configuration.Provider) { initialize( appContext, ((Configuration.Provider) appContext).getWorkManagerConfiguration()); instance = getInstance(appContext); } else { throw new IllegalStateException("WorkManager is not initialized properly. You " + "have explicitly disabled WorkManagerInitializer in your manifest, " + "have not manually called WorkManager#initialize at this point, and " + "your Application does not implement Configuration.Provider."); } } return instance; } }
getInstance方法会调用到WorkManager的实现类WorkManagerImpl中,默认情况下这里第一行代码其实会直接返回,因为getInstance不为空,初始化工作在app启动时就通过ContentProvider完成了。
<provider android:name="androidx.work.impl.WorkManagerInitializer" android:exported="false" android:multiprocess="true" android:authorities="com.derry.workmanager.workmanager-init" android:directBootAware="false" />
public class WorkManagerInitializer extends ContentProvider { @Override public boolean onCreate() { // Initialize WorkManager with the default configuration. WorkManager.initialize(getContext(), new Configuration.Builder().build()); return true; } }
找到编译后的build文件夹中的apk,查看其清单文件可以发现注册了一个provider,这个类的onCreate方法中就调用了WorkManager的初始化方法,我们知道ContentProvider在Application生命周期方法onCreate之前就会初始化,所以WorkManager也在这时就被初始化了。
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) { synchronized (sLock) { ... if (sDelegatedInstance == null) { context = context.getApplicationContext(); if (sDefaultInstance == null) { sDefaultInstance = new WorkManagerImpl( context, configuration, new WorkManagerTaskExecutor(configuration.getTaskExecutor())); } sDelegatedInstance = sDefaultInstance; } } }
initialize方法里,configuration.getTaskExecutor()内部返回一个固定线程数量的线程池,创建WorkManagerTaskExecutor用来执行线程池的任务,然后调用WorkManagerImpl的构造方法。
public WorkManagerImpl( @NonNull Context context, @NonNull Configuration configuration, @NonNull TaskExecutor workTaskExecutor, @NonNull WorkDatabase database) { Context applicationContext = context.getApplicationContext(); Logger.setLogger(new Logger.LogcatLogger(configuration.getMinimumLoggingLevel())); List<Scheduler> schedulers = createSchedulers(applicationContext, workTaskExecutor); Processor processor = new Processor( context, configuration, workTaskExecutor, database, schedulers); internalInit(context, configuration, workTaskExecutor, database, schedulers, processor); }
WorkManagerImpl构造方法中做了以下几件事
1.创建了Room数据库,Room也是对sqlite的再封装,数据库用来记录每一个后台任务的信息,包括执行顺序 、执行时间等信息;
2.创建schedulers调度器,主要有三种GreedyScheduler、SystemAlarmScheduler、SystemJobScheduler,根据系统版本来选择用哪种;
3.创建Processor处理器,Processor用来管理Schedulers的执行,启动或停止任务;
2.任务入队方法enqueue
进入WorkManagerImpl.enqueue方法,创建一个WorkContinuationImpl对象执行enqueue方法。
public Operation enqueue( @NonNull List<? extends WorkRequest> workRequests) { // This error is not being propagated as part of the Operation, as we want the // app to crash during development. Having no workRequests is always a developer error. if (workRequests.isEmpty()) { throw new IllegalArgumentException( "enqueue needs at least one WorkRequest."); } return new WorkContinuationImpl(this, workRequests).enqueue(); }
WorkContinuationImpl.enqueue方法,创建EnqueueRunnable并在后台线程池中执行
public @NonNull Operation enqueue() { if (!mEnqueued) { EnqueueRunnable runnable = new EnqueueRunnable(this); mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable); mOperation = runnable.getOperation(); } else { Logger.get().warning(TAG, String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds))); } return mOperation; }
EnqueueRunnable.run方法,将workSpec添加到数据库并对任务状态进行校验,将RescheduleReceiver注册到AndroidManifest.xml内,在scheduleWorkInBackground方法中执行调度工作
public void run() { try { if (mWorkContinuation.hasCycles()) { throw new IllegalStateException( String.format("WorkContinuation has cycles (%s)", mWorkContinuation)); } //将任务信息添加到数据库 boolean needsScheduling = addToDatabase(); if (needsScheduling) { //启用 RescheduleReceiver,仅当有需要调度的Worker时。 final Context context = mWorkContinuation.getWorkManagerImpl().getApplicationContext(); //允许将RescheduleReceiver注册到AndroidManifest.xml内 PackageManagerHelper.setComponentEnabled(context, RescheduleReceiver.class, true); scheduleWorkInBackground(); } mOperation.setState(Operation.SUCCESS); } catch (Throwable exception) { mOperation.setState(new Operation.State.FAILURE(exception)); } }
3.Schedulers调度器
EnqueueRunnable. scheduleWorkInBackground方法,调用了Schedulers.schedule方法,传入了Configuration, WorkDatabase, Schedulers三个对象
public void scheduleWorkInBackground() { WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl(); Schedulers.schedule( workManager.getConfiguration(), workManager.getWorkDatabase(), workManager.getSchedulers()); }
Schedulers.schedule方法,先进行了一系列的数据库操作,主要是查询数据库中未执行的任务,然后根据条件对每个任务进行调度。Schedulers将任务交给每一个Scheduler去处理,GreedyScheduler会先处理这个任务。
public static void schedule( @NonNull Configuration configuration, @NonNull WorkDatabase workDatabase, List<Scheduler> schedulers) { List<WorkSpec> eligibleWorkSpecs; ... if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) { WorkSpec[] eligibleWorkSpecsArray = eligibleWorkSpecs.toArray(new WorkSpec[0]); // 交给底层调度器去调度 for (Scheduler scheduler : schedulers) { scheduler.schedule(eligibleWorkSpecsArray); } } }
GreedyScheduler类,判断是否有约束条件,有则将任务入集合,没有则调用startWork方法。假如有低电量的约束条件,则会在清单文件生成名为BatteryNotLowProxy的receiver,实现原理就是通过监听约束条件变化的广播,然后通过一系列处理,最后也是调用到startWork方法,其他约束条件也是大同小异。
public void schedule(@NonNull WorkSpec... workSpecs) { ... for (WorkSpec workSpec : workSpecs) { if (workSpec.state == WorkInfo.State.ENQUEUED && !workSpec.isPeriodic() && workSpec.initialDelay == 0L && !workSpec.isBackedOff()) { if (workSpec.hasConstraints()) { ... //有约束条件 constrainedWorkSpecs.add(workSpec); constrainedWorkSpecIds.add(workSpec.id); } else { //无约束条件 mWorkManagerImpl.startWork(workSpec.id); } } } ... }
WorkManagerImpl.startWork方法,WorkTaskExecutor执行了Runnnable,下面进入 StartWorkRunnable的run()的实现
public void startWork( @NonNull String workSpecId, @Nullable WorkerParameters.RuntimeExtras runtimeExtras) { mWorkTaskExecutor .executeOnBackgroundThread( new StartWorkRunnable(this, workSpecId, runtimeExtras)); }
StartWorkRunnable.run方法,将任务的信息交给Processor,由Processor调用startWork()去执行任务
public void run() { mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras); }
4.Processor处理器
processor处理器可以智能地按需安排和执行后台任务
processor.startWork方法,这里会将任务包装成一个WorkWrapper,WorkWrapper也是一个Runnable,然后再次调用WorkTaskExecutor执行Runnnable。
public boolean startWork(@NonNull String id,@Nullable WorkerParameters.RuntimeExtras runtimeExtras) { WorkerWrapper workWrapper; synchronized (mLock) { ... workWrapper = new WorkerWrapper.Builder( mAppContext, mConfiguration, mWorkTaskExecutor, this, mWorkDatabase, id) .withSchedulers(mSchedulers) .withRuntimeExtras(runtimeExtras) .build(); ListenableFuture<Boolean> future = workWrapper.getFuture(); future.addListener( new FutureListener(this, id, future), mWorkTaskExecutor.getMainThreadExecutor()); mEnqueuedWorkMap.put(id, workWrapper); } //执行Ruunable mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper); return true; }
WorkWrapper类的run方法,通过反射获取到ListenableWorker对象也就是Worker类的父类,然后调用ListenableWorker.startWork方法,也就是调用Worker类的startWork方法。
public void run() { mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId); mWorkDescription = createWorkDescription(mTags); runWorker(); } private void runWorker() { ... //反射获取ListenableWorker对象 if (mWorker == null) { mWorker = mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback( mAppContext, mWorkSpec.workerClassName, params); } if (trySetRunning()) { if (tryCheckForInterruptionAndResolve()) { return; } final SettableFuture<ListenableWorker.Result> future = SettableFuture.create(); mWorkTaskExecutor.getMainThreadExecutor() .execute(new Runnable() { @Override public void run() { try { Logger.get().debug(TAG, String.format("Starting work for %s", mWorkSpec.workerClassName)); //这里调用startWork方法 mInnerFuture = mWorker.startWork(); future.setFuture(mInnerFuture); } catch (Throwable e) { future.setException(e); } } }); }
Worker.startWork方法,这里即调用到我们自己实现的doWork方法,至此整个流程结束。
public abstract @NonNull Result doWork(); public final @NonNull ListenableFuture<Result> startWork() { mFuture = SettableFuture.create(); getBackgroundExecutor().execute(new Runnable() { @Override public void run() { try { Result result = doWork(); mFuture.set(result); } catch (Throwable throwable) { mFuture.setException(throwable); } } }); return mFuture; }
小结
WorkManager是一个很优秀的框架使用方法也很简单,适用于可延迟执行的任务,即使应用程序或设备重新启动也能保证一定会执行。WorkManager会针对不同Android版本的选择不同的执行策略,所以可以的话推荐使用WorkManager替代原有的方案。
以上不足之处请指正,欢迎留言,谢谢。
这篇关于JetPack WorkManager源码分析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-26RocketMQ入门指南:搭建与使用全流程详解
- 2024-11-26RocketMQ入门教程:轻松搭建与使用指南
- 2024-11-26手写RocketMQ:从入门到实践的简单教程
- 2024-11-25【机器学习(二)】分类和回归任务-决策树(Decision Tree,DT)算法-Sentosa_DSML社区版
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享