WorkManger流程分析(一)

本篇分析的是WorkManger版本2.3.1的流程
因为jetpack目前更新频率较快,可能过几周这部分代码会有变动

1.初始化

private fun test(){
 	WorkManager.getInstance(this)
		.enqueue(OneTimeWorkRequest.Builder(Worker::class.java)
		.build())
 }

方法调用只有两步,获取单例以及插入任务队列

public static @NonNull WorkManager getInstance(@NonNull Context context) {
        return WorkManagerImpl.getInstance(context);
    }
    
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;
        }
    }

看似是单例初始化入口,但这的applicationConext并不匹配Configuration.Provider,所以这里是不会走的
真正初始化方法是在这里

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

这个ContentProvider会随着应用启动而初始化,然后去调用WorkManger的第二个初始化方法

public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
        WorkManagerImpl.initialize(context, configuration);
    }
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
        synchronized (sLock) {
            if (sDelegatedInstance != null && sDefaultInstance != null) {
                throw new IllegalStateException("WorkManager is already initialized.  Did you "
                        + "try to initialize it manually without disabling "
                        + "WorkManagerInitializer? See "
                        + "WorkManager#initialize(Context, Configuration) or the class level "
                        + "Javadoc for more information.");
            }

            if (sDelegatedInstance == null) {
                context = context.getApplicationContext();
                if (sDefaultInstance == null) {
                    sDefaultInstance = new WorkManagerImpl(
                            context,
                            configuration,
                            new WorkManagerTaskExecutor(configuration.getTaskExecutor()));
                }
                sDelegatedInstance = sDefaultInstance;
            }
        }
    }
-----------------------------------------------------------------------------------------
public class WorkManagerTaskExecutor implements TaskExecutor {

    private final SerialExecutor mBackgroundExecutor;

    public WorkManagerTaskExecutor(@NonNull Executor backgroundExecutor) {
        // Wrap it with a serial executor so we have ordering guarantees on commands
        // being executed.
        mBackgroundExecutor = new SerialExecutor(backgroundExecutor);
    }
    ...
}

这个方法中会创建一个TaskExecutor,内部指定的Executor是SerialExecutor,也就是说后续的任务都是以串行的形式提交执行

创建构造方法的同时会创建一个Schedulers的集合,同时会创建一个Processor对象,这个对象中会有
startWork方法,后面会用到;同时这里会有一个WorkDatabase用作数据存储,而这个DataBase也用到了jetpack的RoomDatabase数据库

public abstract class WorkDatabase extends RoomDatabase { ... }
------------------------------------------------------------------
 public WorkManagerImpl(
            @NonNull Context context,
            @NonNull Configuration configuration,
            @NonNull TaskExecutor workTaskExecutor,
            boolean useTestDatabase) {
        this(context,
                configuration,
                workTaskExecutor,
                WorkDatabase.create(
                        context.getApplicationContext(),
                        workTaskExecutor.getBackgroundExecutor(),
                        useTestDatabase)
        );
    }
    
	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);
    }

而这个schedulers里会放两个Scehduler对象,一个是GreedyScheduler,另一个会根据版本选择SystemJobScheduler或者是SystemAlarmScheduler

 public List<Scheduler> createSchedulers(
            @NonNull Context context,
            @NonNull TaskExecutor taskExecutor) {

        return Arrays.asList(
                Schedulers.createBestAvailableBackgroundScheduler(context, this),
                new GreedyScheduler(context, taskExecutor, this));
    }

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

        Scheduler scheduler;

        if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {//api 23
            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;
    }

GreedyScheduler的schedule方法会去检查当前的约束,如果没有则马上去执行任务,但应用必须活跃才能执行;而另外两个运行时间比第一个稍慢,但存活周期比第一个要长

2.执行任务

 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;
    }

enqueue方法会有是否已经插入的判断,然后用上面创建的SerialExecutor去执行任务, 并把当前的操作对象返回

public class EnqueueRunnable implements Runnable {
	 public void run() {
        try {
            if (mWorkContinuation.hasCycles()) {
                throw new IllegalStateException(
                        String.format("WorkContinuation has cycles (%s)", mWorkContinuation));
            }
            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));
        }
    }
    
	public void scheduleWorkInBackground() {
        WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
        Schedulers.schedule(
                workManager.getConfiguration(),
                workManager.getWorkDatabase(),
                workManager.getSchedulers());
    }
 }

addToDatabase方法主要会进行一些约束和id校验,然后录入数据信息
比如之前插入的任务处于FAILED,CANCELED 等状态时,返回false ;
或者设置enqueueUniqueWork唯一id标示任务执行时,当Policy设置成KEEP,而任务队列里已经有同id的任务处于ENQUEUED或者RUNNING状态时也返回false 等;

当满足条件时,就会插入任务队列

  public static void schedule(
            @NonNull Configuration configuration,
            @NonNull WorkDatabase workDatabase,
            List<Scheduler> schedulers) {
        if (schedulers == null || schedulers.size() == 0) {
            return;
        }

        WorkSpecDao workSpecDao = workDatabase.workSpecDao();
        List<WorkSpec> eligibleWorkSpecs;

        workDatabase.beginTransaction();
        try {
            eligibleWorkSpecs = workSpecDao.getEligibleWorkForScheduling(
                    configuration.getMaxSchedulerLimit());
            if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
                long now = System.currentTimeMillis();

                // Mark all the WorkSpecs as scheduled.
                // Calls to Scheduler#schedule() could potentially result in more schedules
                // on a separate thread. Therefore, this needs to be done first.
                for (WorkSpec workSpec : eligibleWorkSpecs) {
                    workSpecDao.markWorkSpecScheduled(workSpec.id, now);
                }
            }
            workDatabase.setTransactionSuccessful();
        } finally {
            workDatabase.endTransaction();
        }

        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);
            }
        }
    }

这个方法前面都是些数据库操作,到最后会遍历满足的条件的WorkSpec,用上面创建的两个Scheduler的集合遍历去处理这个;
注意这里有个eligibleWorkSpecs的集合,这个会最终转成WorkSpec集合交付scheduler去处理,而这个处理方式也就是串并行的区别,串行每次都只会获取到一个,而并行会获取多个
同时这里也指定了同时能获取到的最大个数getMaxSchedulerLimit,也是数据库查询的限制,默认是20,但在SDK版本为23的时候限制是10个

比如GreedyScheduler的schedule方法

 public void schedule(@NonNull WorkSpec... workSpecs) {
        ......
        List<WorkSpec> constrainedWorkSpecs = new ArrayList<>();
        List<String> constrainedWorkSpecIds = new ArrayList<>();
        for (WorkSpec workSpec : workSpecs) {
            if (workSpec.state == WorkInfo.State.ENQUEUED
                    && !workSpec.isPeriodic()
                    && workSpec.initialDelay == 0L
                    && !workSpec.isBackedOff()) {
                if (workSpec.hasConstraints()) {
                    if (SDK_INT >= 23 && workSpec.constraints.requiresDeviceIdle()) {
                        // Ignore requests that have an idle mode constraint.
                        Logger.get().debug(TAG,
                                String.format("Ignoring WorkSpec %s, Requires device idle.",
                                        workSpec));
                    } else if (SDK_INT >= 24 && workSpec.constraints.hasContentUriTriggers()) {
                        // Ignore requests that have content uri triggers.
                        Logger.get().debug(TAG,
                                String.format("Ignoring WorkSpec %s, Requires ContentUri triggers.",
                                        workSpec));
                    } else {
                        constrainedWorkSpecs.add(workSpec);
                        constrainedWorkSpecIds.add(workSpec.id);
                    }
                } else {
                    Logger.get().debug(TAG, String.format("Starting work for %s", workSpec.id));
                    mWorkManagerImpl.startWork(workSpec.id);
                }
            }
        }
    	   ......
    }

这里会有一个循环遍历,根据上面传递过来的集合数可以确定是单任务串行,还是多任务并行处理;
当任务状态是ENQUEUED,而且也没有做定时,延迟执行或者有重试的,都会进这个方法
然后先判断约束条件,如果没有约束条件都会马上执行startWork方法,相反如果有约束条件则是另一种情况,会走广播
又比如SystemJobService的onStartJob方法,最终也会走到startWork方法中,SystemJobScheduler的schedule种也有对多任务的遍历逻辑,这里就不列出了

 public boolean onStartJob(@NonNull JobParameters params) {
        if (mWorkManagerImpl == null) {
            Logger.get().debug(TAG, "WorkManager is not initialized; requesting retry.");
            jobFinished(params, true);
            return false;
        }
        String workSpecId = getWorkSpecIdFromJobParameters(params);
        if (TextUtils.isEmpty(workSpecId)) {
            Logger.get().error(TAG, "WorkSpec id not found!");
            return false;
        }
        synchronized (mJobParameters) {
            if (mJobParameters.containsKey(workSpecId)) {
                Logger.get().debug(TAG, String.format(
                        "Job is already being executed by SystemJobService: %s", workSpecId));
                return false;
            }
            Logger.get().debug(TAG, String.format("onStartJob for %s", workSpecId));
            mJobParameters.put(workSpecId, params);
        }
        WorkerParameters.RuntimeExtras runtimeExtras = null;
        if (Build.VERSION.SDK_INT >= 24) {
            runtimeExtras = new WorkerParameters.RuntimeExtras();
            if (params.getTriggeredContentUris() != null) {
                runtimeExtras.triggeredContentUris =
                        Arrays.asList(params.getTriggeredContentUris());
            }
            if (params.getTriggeredContentAuthorities() != null) {
                runtimeExtras.triggeredContentAuthorities =
                        Arrays.asList(params.getTriggeredContentAuthorities());
            }
            if (Build.VERSION.SDK_INT >= 28) {
                runtimeExtras.network = params.getNetwork();
            }
        }
        mWorkManagerImpl.startWork(workSpecId, runtimeExtras);
        return true;
    }

startWork方法会用之前创建的TaskExecutor,也就是单次顺序执行的Executor

 public void startWork(
            @NonNull String workSpecId,
            @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
        mWorkTaskExecutor
                .executeOnBackgroundThread(
                        new StartWorkRunnable(this, workSpecId, runtimeExtras));
    }

然后这个会走到Processor的startWork方法中

public class Processor implements ExecutionListener, ForegroundProcessor {
 ......
 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);
        }
        mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
        return true;
    }
}

然后在WorkWrapper的run方法中, 最终会执行到Worker的startWork方法中

public class WorkerWrapper implements Runnable {
	...... 
    @Override
    public void run() {
        mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
        mWorkDescription = createWorkDescription(mTags);
        runWorker();
    }

    private void runWorker() {
        ......
	 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));
                                mInnerFuture = mWorker.startWork();
                                future.setFuture(mInnerFuture);
                            } catch (Throwable e) {
                                future.setException(e);
                            }
                        }
                    });

            final String workDescription = mWorkDescription;
            future.addListener(new Runnable() {
                @Override
                public void run() {
                    try {
                        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 {
                            Logger.get().debug(TAG, String.format("%s returned a %s result.",
                                    mWorkSpec.workerClassName, result));
                            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();
        }
    }

    // Package-private for synthetic accessor.
    void onWorkFinished() {
        boolean isWorkFinished = false;
        if (!tryCheckForInterruptionAndResolve()) {
            mWorkDatabase.beginTransaction();
            try {
                WorkInfo.State state = mWorkSpecDao.getState(mWorkSpecId);
                mWorkDatabase.workProgressDao().delete(mWorkSpecId);
                if (state == null) {
                    resolve(false);
                    isWorkFinished = true;
                } else if (state == RUNNING) {
                    handleResult(mResult);
                    state = mWorkSpecDao.getState(mWorkSpecId);
                    isWorkFinished = state.isFinished();
                } else if (!state.isFinished()) {
                    rescheduleAndResolve();
                }
                mWorkDatabase.setTransactionSuccessful();
            } finally {
                mWorkDatabase.endTransaction();
            }
        }
        
         if (mSchedulers != null) {
            if (isWorkFinished) {
                for (Scheduler scheduler : mSchedulers) {
                    scheduler.cancel(mWorkSpecId);
                }
            }
            Schedulers.schedule(mConfiguration, mWorkDatabase, mSchedulers);
        }
}
------------------------------------------------------------------------------
 public final @Nullable ListenableWorker createWorkerWithDefaultFallback(
            @NonNull Context appContext,
            @NonNull String workerClassName,
            @NonNull WorkerParameters workerParameters) {

        ListenableWorker worker = createWorker(appContext, workerClassName, workerParameters);
        if (worker == null) {
            // Fallback to reflection
            Class<? extends ListenableWorker> clazz = null;
            try {
                clazz = Class.forName(workerClassName).asSubclass(ListenableWorker.class);
            } catch (ClassNotFoundException e) {
                Logger.get().error(TAG, "Class not found: " + workerClassName);
            }
            if (clazz != null) {
                try {
                    Constructor<? extends ListenableWorker> constructor =
                            clazz.getDeclaredConstructor(Context.class, WorkerParameters.class);
                    worker = constructor.newInstance(
                            appContext,
                            workerParameters);
                } catch (Exception e) {
                    Logger.get().error(TAG, "Could not instantiate " + workerClassName, e);
                }
            }
        }

        if (worker != null && worker.isUsed()) {
            String factoryName = this.getClass().getName();
            String message = String.format("WorkerFactory (%s) returned an instance of a "
                            + "ListenableWorker (%s) which has already been invoked. "
                            + "createWorker() must always return a new instance of a "
                            + "ListenableWorker.",
                    factoryName, workerClassName);

            throw new IllegalStateException(message);
        }

        return worker;
    }

这里会先调用createWorkerWithDefaultFallback方法反射创建一个Worker,而这里的workerClassName就是我们自定义Work

当操作完成后,会走onWorkFinished方法,这里会检查任务的状态;这里的isWorkFinished字段表示中,SUCCEEDED,FAILED,CANCELLED都会返回true,标记的是一个任务的执行情况,只要执行了一遍,不论是否正常执行完都是Finish标记;
最后会回到Schedulers.schedule方法中,继续判断下一步的逻辑,是否有下一个任务需要执行等,也就形成了一个循环,而正常的串行多任务执行就是走的这个循环,具体条件判断WorkManager内部已经处理完毕了,这里就不细扣了

这里区分一下串行和并行,Work的创建也是有些区别的
串行是按我们配置的顺序依次执行进这个方法的,也就是会按我们自定义的Worker;而并行是开始创建了一个CombineContinuationsWorker,同时这个Worker会放到一个OneTimeWorkRequest中,然后这个的doWork方法中什么都不做,直接返回Success,也就是说并行首次反射创建的是CombineContinuationsWorker这个,马上返回成功,然后走到Schedulers.schedule方法进入下一轮循环时候,才会去取多任务并行,而这之后才会去执行我们的Worker

而Worker的startWork方法最终也就调用了我们重写的doWork方法

  @Override
    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;
    }

具体className赋值是在Builder中

public abstract static class Builder<B extends Builder<?, ?>, W extends WorkRequest> {

        boolean mBackoffCriteriaSet = false;
        UUID mId;
        WorkSpec mWorkSpec;
        Set<String> mTags = new HashSet<>();
        Class<? extends ListenableWorker> mWorkerClass;

        Builder(@NonNull Class<? extends ListenableWorker> workerClass) {
            mId = UUID.randomUUID();
            mWorkerClass = workerClass;
            mWorkSpec = new WorkSpec(mId.toString(), workerClass.getName());
            addTag(workerClass.getName());
        }
		......
}

大致流程图是这样
在这里插入图片描述
这里应该少了一个每个任务执行完循环schedule的路线

3.总结

1.WorkManager初始化在WorkManagerInitializer中,而这个是继承ContentProvider,也就是应用一启动就会初始化
2.创建WorkRequest时会传入我们创建的Work的类名,而这个类名同时也会添加到WorkRequest的Tag集合中
3.每次创建新的WorkRequest都会调用 UUID.randomUUID()创建一个唯一id,并把这个id和上面传的类名一起保存在WorkSpec成员变量中
4.初始化时会创建一个Schedulers集合,里面放的一个是GreedyScheduler,而另一个会根据版本区分,当版本>=23时,用的是SystemJobScheduler,否则用的是SystemAlarmService
5.每次插入新的任务都会从数据查询进行校验,比如约束条件或者之前任务状态,唯一性等的校验,然后把任务以及相应的状态插入或更新到数据库中
6.初始化会创建一个WorkManagerTaskExecutor,而这个默认的后台执行者是SerialExecutor,也就是默认会是按照顺序依次执行任务
7.开始执行任务前会去校验约束,延迟,以及任务状态Status等信息,不满足会等待满足条件再执行
8.执行任务时会遍历上面的两个Scheduler都去执行,一个是立即执行,如果有延迟等信息,则会用第二个延迟执行
9.任务的最终执行时从数据库取得任务信息,然后继续对任务进行一系列校验,如果满足,那么就会反射创建我们自定义的Worker对象,并执行doWork方法
10.执行完当前任务后,会调用Schedulers.schedule方法继续下一个任务的逻辑判断,非指定则任务会是一个个按顺序执行,这里的顺序是指阻塞的,指的是任务执行的顺序是依次的,也就是说要等第一个任务执行完才执行第二个
11.并行任务首次执行会走CombineContinuationsWorker,而这个Worker会马上返回成功,之后循环回来执行我们自定义的Worker

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值