WorkManage源码研究

题外话

首先说明下,work的request都是存储在数据库里的,用的是room库

WorkManager 的初始化,是系统自动实现的,如下是通过ContentProvider实现的

public class WorkManagerInitializer extends ContentProvider {
    @Override
    public boolean onCreate() {
        // Initialize WorkManager with the default configuration.
        WorkManager.initialize(getContext(), new Configuration.Builder().build());
        return true;
    }

1.简单理解

1.1 过程
WorkManager 管理一堆WorkRequest【系统实现的有两种,一种一次性的OneTimeWorkRequest,另外一种可以重复的PeriodicWorkRequest】

如下,通过begin方法返回一个WorkContinuation对象

var continuation = WorkManager.getInstance(mContext)
                    .beginUniqueWork(Constants.IMAGE_MANIPULATION_WORK_NAME,
                            ExistingWorkPolicy.REPLACE,
                            OneTimeWorkRequest.from(CleanupWorker::class.java))

或者,这种unique name是空

var continuation = WorkManager.getInstance(mContext).beginWith(OneTimeWorkRequest.from(CleanupWorker::class.java))

看下第二个参数的意义,
分三种,有同名的任务的情况下执行 替换,不替换,添加操作

public enum ExistingWorkPolicy {

    /**
     * If there is existing pending (uncompleted) work with the same unique name, cancel and delete
     * it.  Then, insert the newly-specified work.
     */
    REPLACE,

    /**
     * If there is existing pending (uncompleted) work with the same unique name, do nothing.
     * Otherwise, insert the newly-specified work.
     */
    KEEP,

    /**
     * If there is existing pending (uncompleted) work with the same unique name, append the
     * newly-specified work as a child of all the leaves of that work sequence.  Otherwise, insert
     * the newly-specified work as the start of a new sequence.
     */
    APPEND
}

如下是beginWith的实现,返回一个WorkContinuationImpl对象,和beginUniqueWork的区别,名字是null,policy是KEEP类型

    WorkContinuationImpl(
            @NonNull WorkManagerImpl workManagerImpl,
            @NonNull List work) {
        this(
                workManagerImpl,
                null,
                ExistingWorkPolicy.KEEP,
                work,
                null);
    }

完整的构造方法如下
mIds是新加的work request的id集合,mAllIds是包括parent【也就是之前添加的request】的id集合

    WorkContinuationImpl(@NonNull WorkManagerImpl workManagerImpl,
            String name,
            ExistingWorkPolicy existingWorkPolicy,
            @NonNull List work,
            @Nullable List parents) {
        mWorkManagerImpl = workManagerImpl;
        mName = name;
        mExistingWorkPolicy = existingWorkPolicy;
        mWork = work;
        mParents = parents;
        mIds = new ArrayList<>(mWork.size());
        mAllIds = new ArrayList<>();
        if (parents != null) {
            for (WorkContinuationImpl parent : parents) {
                mAllIds.addAll(parent.mAllIds);
            }
        }
        for (int i = 0; i < work.size(); i++) {
            String id = work.get(i).getStringId();
            mIds.add(id);
            mAllIds.add(id);
        }
    }

上边的 parent是啥,往下看,这个类还有个then方法,可以继续添加request

    public @NonNull WorkContinuation then(@NonNull List work) {
        return new WorkContinuationImpl(mWorkManagerImpl,
                mName,
                ExistingWorkPolicy.KEEP,
                work,
                Collections.singletonList(this));
    }

效果类似这种


WorkManage源码研究_第1张图片
image.png

等到request都添加完了,执行如下的方法

    public @NonNull Operation enqueue() {
        // Only enqueue if not already enqueued.
        if (!mEnqueued) {
            // The runnable walks the hierarchy of the continuations
            // and marks them enqueued using the markEnqueued() method, parent first.
            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;
    }

可以看到,主要就是里边那个Runnable,我们去看下,Runnable都干啥了

    public void run() {
        try {
//忽略
            boolean needsScheduling = addToDatabase();//添加到数据库
            if (needsScheduling) {
                // Enable RescheduleReceiver, only when there are Worker's that need scheduling.
                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));
        }
    }

addToDatabase()一路往下点,最后可以看到就是把request里的数据存到数据库里

            workDatabase.workSpecDao().insertWorkSpec(workSpec);

            if (hasPrerequisite) {
                for (String prerequisiteId : prerequisiteIds) {
                    Dependency dep = new Dependency(work.getStringId(), prerequisiteId);
                    workDatabase.dependencyDao().insertDependency(dep);
                }
            }

            for (String tag : work.getTags()) {
                workDatabase.workTagDao().insert(new WorkTag(tag, work.getStringId()));
            }

            if (isNamed) {
                workDatabase.workNameDao().insert(new WorkName(name, work.getStringId()));
            }

后台任务执行

    public void scheduleWorkInBackground() {
        WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
        Schedulers.schedule(
                workManager.getConfiguration(),
                workManager.getWorkDatabase(),
                workManager.getSchedulers());
    }

然后里边就是查找出workSpec数据,

  public static void schedule(
            @NonNull Configuration configuration,
            @NonNull WorkDatabase workDatabase,
            List schedulers) {
//省略查找..
        if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
            WorkSpec[] eligibleWorkSpecsArray = eligibleWorkSpecs.toArray(new WorkSpec[0]);
            // Delegate to the underlying scheduler.
            for (Scheduler scheduler : schedulers) {
//看来还得找下Scheduler的实现,
                scheduler.schedule(eligibleWorkSpecsArray);
            }
        }

根据Scheduler的来源workManager.getSchedulers())往上层查找
在WorkManagerImpl构造方法里

List schedulers = createSchedulers(applicationContext, workTaskExecutor);
    public @NonNull List createSchedulers(Context context, TaskExecutor taskExecutor) {
        return Arrays.asList(
                Schedulers.createBestAvailableBackgroundScheduler(context, this),
                // Specify the task executor directly here as this happens before internalInit.
                // GreedyScheduler creates ConstraintTrackers and controllers eagerly.
                new GreedyScheduler(context, taskExecutor, this));
    }

先看第一个,这里就是23以上的用的JobScheduler,23以下的用的alarm了

    static Scheduler createBestAvailableBackgroundScheduler(
            @NonNull Context context,
            @NonNull WorkManagerImpl workManager) {

        Scheduler scheduler;

        if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
            scheduler = new SystemJobScheduler(context, workManager);
            setComponentEnabled(context, SystemJobService.class, true);
            Logger.get().debug(TAG, "Created SystemJobScheduler and enabled SystemJobService");
        } else {
            scheduler = tryCreateGcmBasedScheduler(context);
            if (scheduler == null) {
                scheduler = new SystemAlarmScheduler(context);
                setComponentEnabled(context, SystemAlarmService.class, true);
                Logger.get().debug(TAG, "Created SystemAlarmScheduler");
            }
        }
        return scheduler;
    }

继续一路往下看23以上的实现Processor类

    public boolean startWork(String id, WorkerParameters.RuntimeExtras runtimeExtras) {
        WorkerWrapper workWrapper;
//...........最后其实是执行了WorkerWrapper里的runWrok方法
            workWrapper =
                    new WorkerWrapper.Builder(
                            mAppContext,
                            mConfiguration,
                            mWorkTaskExecutor,
                            mWorkDatabase,
                            id)
                            .withSchedulers(mSchedulers)
                            .withRuntimeExtras(runtimeExtras)
                            .build();
            ListenableFuture future = workWrapper.getFuture();
            future.addListener(
                    new FutureListener(this, id, future),
                    mWorkTaskExecutor.getMainThreadExecutor());
            mEnqueuedWorkMap.put(id, workWrapper);
        }
        mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
        Logger.get().debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
        return true;
    }

doWork代码太多就不详细研究了
大致过程就是把worker实例化出来,然后执行里边的


  mInnerFuture = mWorker.startWork();
                                future.setFuture(mInnerFuture);

简单的执行过程看完了

看下request

  1. OneTimeWorkRequest
    实例化有3个方法
OneTimeWorkRequest.from(CleanupWorker::class.java)

OneTimeWorkRequest.Builder(WaterColorFilterWorker::class.java)

 OneTimeWorkRequestBuilder()

第三种是worker-runtime-ktx包下的,正常运行需要在gradle的android属性里添加

    kotlinOptions {
        jvmTarget = "1.8"
    }
PeriodicWorkRequest.Builder(WaterColorFilterWorker::class.java,15,TimeUnit.MINUTES)

workerManager

1.beginWith或者beginUniqueWork
如果同时进行的request比较多,可以使用beginWith或者beginUniqueWork,如下,返回的

    public @NonNull WorkContinuation beginWith(@NonNull List work) {
//..
        return new WorkContinuationImpl(this, work);
    }

而WorkContinuationImpl,可以通过then方法添加新的request

    public @NonNull WorkContinuation then(@NonNull List work) {
        return new WorkContinuationImpl(mWorkManagerImpl,
                mName,
                ExistingWorkPolicy.KEEP,
                work,
                Collections.singletonList(this));
    }
  1. enqueue或者enqueueUniquePeriodicWork
    可以看到最终和上边的一样,执行的是WorkContinuationImpl的enqueue方法
    public Operation enqueue(
            @NonNull List workRequests) {
        return new WorkContinuationImpl(this, workRequests).enqueue();
    }
  1. 取消任务
    取消未完成的任务,任务还在数据库里,只是状态变成了success
cancelAllWork()
cancelAllWorkByTag(@NonNull final String tag)//tag就是class的name 》workerClass.getName()
cancelUniqueWork(@NonNull String uniqueWorkName) //uniqueWork那个name
cancelWorkById(@NonNull UUID id)//uuid自动生成的,可以get拿到
  1. 删除任务
    删除已经完成的work,包括success,failed,cancelled
     * Prunes all eligible finished work from the internal database.  Eligible work must be finished
     * ({@link WorkInfo.State#SUCCEEDED}, {@link WorkInfo.State#FAILED}, or
     * {@link WorkInfo.State#CANCELLED}), with zero unfinished dependents.

pruneWork()
  1. 获取worker
    分3种,和cancel差不多,根据标示来查找workInfo
public @NonNull ListenableFuture getWorkInfoById(@NonNull UUID id)

public @NonNull LiveData getWorkInfoByIdLiveData(@NonNull UUID id) 

public @NonNull ListenableFuture> getWorkInfosByTag(@NonNull String tag)

public @NonNull LiveData> getWorkInfosByTagLiveData(@NonNull String tag)

public ListenableFuture> getWorkInfosForUniqueWork(@NonNull String name)

public LiveData> getWorkInfosForUniqueWorkLiveData(@NonNull String name)

  1. WorkRequest.Build的方法
    create workRequest的时候可以传一些参数的,key value形式,也就是个Data,就是个map
Map mValues;

setInputData(@NonNull Data inputData)

kotin下生成data比较简单,如下

private fun createInputData(): Data {
            return workDataOf(Constants.KEY_IMAGE_URI to mImageUri.toString())
        }

实现如下

inline fun workDataOf(vararg pairs: Pair): Data {
    val dataBuilder = Data.Builder()
    for (pair in pairs) {
        dataBuilder.put(pair.first, pair.second)
    }
    return dataBuilder.build()
}

如何获取这个数据?
在worker里直接get即可

    public final @NonNull Data getInputData() {
        return mWorkerParams.getInputData();
    }
  1. Worker
    实现doWork方法,返回Result,
class MyWoker(context: Context ,workerParameters: WorkerParameters) :Worker(context,workerParameters){
    override fun doWork(): Result {

        inputData

        return Result.success()
    }
}

实例

写个简单的实例来说明下数据的传递

class MyWoker(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
    override fun doWork(): Result {
        val value = inputData.getString("key")
        println("doWork==========${javaClass.simpleName}===$value")
        val data = Data.Builder().apply {
            putString("key", "from My Worker");
        }.build()
        return Result.success(data)
    }
}
class MyWoker2(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
    override fun doWork(): Result {
        val value = inputData.getString("key")
        println("doWork==========${javaClass.simpleName}===$value")
        val data = Data.Builder().apply {
            putString("key", "from My Worker2");
        }.build()
        return Result.success(data)
    }
}
    private fun startWork() {

        WorkManager.getInstance(this).beginWith(OneTimeWorkRequest.Builder(MyWoker::class.java).setInputData(createData()).build())
                .then(OneTimeWorkRequestBuilder().setInputData(createData()).build())
                .enqueue()

        println("name===============${MyWoker::class.java.name}")
        WorkManager.getInstance(this).getWorkInfosByTagLiveData(MyWoker::class.java.name).observe(this, Observer {
            println("observer===========${it.size}")
            it.iterator().forEach {
                println("work info=======${it.state}=======${it.outputData.getString("key")}")
            }
        })
        WorkManager.getInstance(this).getWorkInfosByTagLiveData(MyWoker2::class.java.name).observe(this, Observer {
            println("observer2===========${it.size}")
            it.iterator().forEach {
                println("work info2=======${it.state}=======${it.outputData.getString("key")}")
            }
        })
    }

    private fun createData():Data{
       return workDataOf("key" to "default value")
    }

日志如下
分析下,第一个任务state是euqueued,其他的默认是blocked,然后第一个任务在success之前还有个running的state,等到success之后,第二个任务状态改为enqueued,
Result.success(data)里边的outputData,下一个任务会通过inputData接收到,默认的inputData覆盖了

11-12 17:21:02.821 I/System.out: anme===============com.charliesong.demo0327.work.MyWoker
11-12 17:21:02.891 I/System.out: observer===========1
11-12 17:21:02.891 I/System.out: work info=======ENQUEUED=======null
11-12 17:21:02.901 I/System.out: observer2===========1
11-12 17:21:02.901 I/System.out: work info2=======BLOCKED=======null
11-12 17:21:02.911 I/System.out: doWork==========MyWoker===default value
11-12 17:21:02.921 I/System.out: observer===========1
11-12 17:21:02.921 I/System.out: work info=======RUNNING=======null
11-12 17:21:02.981 I/System.out: observer===========1
11-12 17:21:02.981 I/System.out: work info=======SUCCEEDED=======from My Worker
11-12 17:21:02.991 I/System.out: observer2===========1
11-12 17:21:02.991 I/System.out: work info2=======ENQUEUED=======null
11-12 17:21:03.021 I/System.out: doWork==========MyWoker2===from My Worker
11-12 17:21:03.061 I/System.out: observer2===========1
11-12 17:21:03.061 I/System.out: work info2=======SUCCEEDED=======from My Worker2

Constraints

WorkRequest的约束条件添加

WorkRequest.Build
setConstraints(@NonNull Constraints constraints) 
    public static final class Builder {
        boolean mRequiresCharging = false;//是否充电的时候执行
        boolean mRequiresDeviceIdle = false;//是否设备空闲的时候才执行
        NetworkType mRequiredNetworkType = NOT_REQUIRED;//网络条件
        boolean mRequiresBatteryNotLow = false;//是否低电量不触发
        boolean mRequiresStorageNotLow = false;//是否存储空间低的时候不触发
        // Same defaults as JobInfo
        long mTriggerContentUpdateDelay = -1;//监听到contentUrl改变以后多久执行work
        long mTriggerContentMaxDelay = -1;
        ContentUriTriggers mContentUriTriggers = new ContentUriTriggers();//监听的contentUri @RequiresApi(24)

举例 content Uri
还有自定义的contentProvider之类等

所有联系人的Uri: content://contacts/people
    某个联系人的Uri: content://contacts/people/5
    所有图片Uri: content://media/external
    某个图片的Uri:content://media/external/images/media/4

举个例子
如下,把网络断了,第一个work执行完以后,第二个work的state一直是ENQUEUED,知道把网连上,才开始执行里边的doWork方法
我们这里的beginWith,然后then是有顺序的,依次执行的
如果要几个request一起执行,那么放到集合里一起beginWith或者then即可

        WorkManager.getInstance(this).beginWith(OneTimeWorkRequest.Builder(MyWoker::class.java)
                .setInputData(createData())
                .build())
                .then(OneTimeWorkRequestBuilder()
                        .setInputData(createData())
                        .setConstraints(Constraints.Builder()
                                .setRequiredNetworkType(NetworkType.CONNECTED)
                                .build())
                        .build())
                .enqueue()

先发表,以防丢失

你可能感兴趣的:(WorkManage源码研究)