Android Jetpack系列(九):WorkManager

作者: 程序老秃子 | 来源:发表于2022-08-05 14:29 被阅读0次

    WorkManager的原理

    上一篇讲解了如何使用WorkManager;本篇我们就按照上一篇的使用步骤来分析源码

    生成WorkRequest的源码

    第一步生成一个待执行的request请求

    val request = OneTimeWorkRequestBuilder<CountWorker>()
                    .setConstraints(constraints)
                    .addTag("tagCountWorker")
                    .setInputData(Data.Builder().putString("parameter1", "value of parameter1").build())
                    .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.HOURS)
                    .build()
    
    

    我们看类WorkRequest的源码部分:

    public abstract class WorkRequest {
    
        private @NonNull UUID mId;
        private @NonNull WorkSpec mWorkSpec;
        private @NonNull Set<String> mTags;
        
        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());
            }
            
            public final @NonNull W build() {
                W returnValue = buildInternal();
                // Create a new id and WorkSpec so this WorkRequest.Builder can be used multiple times.
                mId = UUID.randomUUID();
                mWorkSpec = new WorkSpec(mWorkSpec);
                mWorkSpec.id = mId.toString();
                return returnValue;
            }
    
            abstract @NonNull W buildInternal();
    
            abstract @NonNull B getThis();
    
        }
    }
    
    
    • 它采用了建造者设计模式,内部成员变量mWorkSpec使用的是Room数据库,可以持久化存储,除非被clear data,所以能保证系统即使被重启,也可以确保任务得到执行。我们查看WorkSpec的源码:
    @Entity(
        indices = {
                @Index(value = {"schedule_requested_at"}),
                @Index(value = {"period_start_time"})
        }
    )
    public final class WorkSpec {
        private static final String TAG = Logger.tagWithPrefix("WorkSpec");
        public static final long SCHEDULE_NOT_REQUESTED_YET = -1;
    
        @ColumnInfo(name = "id")
        @PrimaryKey
        @NonNull
        public String id;
    
        @ColumnInfo(name = "state")
        @NonNull
        public WorkInfo.State state = ENQUEUED;
    
        @ColumnInfo(name = "worker_class_name")
        @NonNull
        public String workerClassName;
    
        @ColumnInfo(name = "input_merger_class_name")
        public String inputMergerClassName;
    
        @ColumnInfo(name = "input")
        @NonNull
        public Data input = Data.EMPTY;
    
        @ColumnInfo(name = "output")
        @NonNull
        public Data output = Data.EMPTY;
    
        @ColumnInfo(name = "initial_delay")
        public long initialDelay;
    
        @ColumnInfo(name = "interval_duration")
        public long intervalDuration;
    
        @ColumnInfo(name = "flex_duration")
        public long flexDuration;
    
        @Embedded
        @NonNull
        public Constraints constraints = Constraints.NONE;
    
        @ColumnInfo(name = "run_attempt_count")
        @IntRange(from = 0)
        public int runAttemptCount;
    
        @ColumnInfo(name = "backoff_policy")
        @NonNull
        public BackoffPolicy backoffPolicy = BackoffPolicy.EXPONENTIAL;
    
        @ColumnInfo(name = "backoff_delay_duration")
        public long backoffDelayDuration = WorkRequest.DEFAULT_BACKOFF_DELAY_MILLIS;
    
        @ColumnInfo(name = "period_start_time")
        public long periodStartTime;
    
        @ColumnInfo(name = "minimum_retention_duration")
        public long minimumRetentionDuration;
    
        @ColumnInfo(name = "schedule_requested_at")
        public long scheduleRequestedAt = SCHEDULE_NOT_REQUESTED_YET;
    
        /**
         * This is {@code true} when the WorkSpec needs to be hosted by a foreground service.
         */
        @ColumnInfo(name = "run_in_foreground")
        public boolean runInForeground;
    
        public WorkSpec(@NonNull String id, @NonNull String workerClassName) {
            this.id = id;
            this.workerClassName = workerClassName;
        }
    
        public WorkSpec(@NonNull WorkSpec other) {
            id = other.id;
            workerClassName = other.workerClassName;
            state = other.state;
            inputMergerClassName = other.inputMergerClassName;
            input = new Data(other.input);
            output = new Data(other.output);
            initialDelay = other.initialDelay;
            intervalDuration = other.intervalDuration;
            flexDuration = other.flexDuration;
            constraints = new Constraints(other.constraints);
            runAttemptCount = other.runAttemptCount;
            backoffPolicy = other.backoffPolicy;
            backoffDelayDuration = other.backoffDelayDuration;
            periodStartTime = other.periodStartTime;
            minimumRetentionDuration = other.minimumRetentionDuration;
            scheduleRequestedAt = other.scheduleRequestedAt;
            runInForeground = other.runInForeground;
        }
    }
    
    

    我们看到上面使用了Room的Entity注解来注解WorkSpec,说明这是一个持有化存储的类,会被存储在数据库中; 它里面存储了一个WorkRequest的几乎所有信息,包括唯一标识id,workerClassName,input,output,constraints等,这些信息在我们生成WorkRequest中都有涉及

    WorkManager.getInstance(this)源码

    WorkManager是一个抽象类,它的实现类是WorkManagerImpl,采用单例模式返回WorkManagerImpl对象; 下面注释1的地方调用getInstance其实返回值已经不为空了,下面我们来分析:

    public static @NonNull WorkManager getInstance(@NonNull Context context) {
        return WorkManagerImpl.getInstance(context);
    }
    
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public static @NonNull WorkManagerImpl getInstance(@NonNull Context context) {
        synchronized (sLock) {
            //-----1-----
            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源码如下,我们看到有两个WorkManagerImpl对象,这里sDelegatedInstance已经不为空,它是在哪里赋值的呢?

    private static WorkManagerImpl sDelegatedInstance = null;
    private static WorkManagerImpl sDefaultInstance = null;
    
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public static @Nullable WorkManagerImpl getInstance() {
        synchronized (sLock) {
            if (sDelegatedInstance != null) {
                return sDelegatedInstance;
            }
    
            return sDefaultInstance;
        }
    }
    
    

    在源码中有这么一个类WorkManagerInitializer,它继承ContentProvider,源码如下:

    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    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的创建时机是在程序的入口ActivityThread.main中,通过调用thread.attach最终回调到activityThread.handleBindApplication方法,在这个方法中ActivityThread会创建Application对象并加载ContentProvider,但是有一点要注意,就是会先加载ContentProvider然后在调用Application的onCreate方法

    这样在程序启动前就会调用WorkManagerInitializer的onCreate方法,从而调用WorkManager的initialize方法,我们来看一下这个方法:

    public boolean onCreate() {
        // Initialize WorkManager with the default configuration.
        //-----1-----
        WorkManager.initialize(getContext(), new Configuration.Builder().build());
        return true;
    }
    
    public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
        WorkManagerImpl.initialize(context, configuration);
    }
    
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    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) {
                    //-----2-----
                    sDefaultInstance = new WorkManagerImpl(
                            context,
                            configuration,
                            new WorkManagerTaskExecutor(configuration.getTaskExecutor()));
                }
                sDelegatedInstance = sDefaultInstance;
            }
        }
    }
    
    

    在注释1的地方,传入了一个参数Configuration, 下面我们先来看一下Configuration的源码:

    public final class Configuration {
    
        public static final int MIN_SCHEDULER_LIMIT = 20;
    
        final @NonNull Executor mExecutor;
        
        final @NonNull Executor mTaskExecutor;
       
        final @NonNull WorkerFactory mWorkerFactory;
        
        final @NonNull InputMergerFactory mInputMergerFactory;
        
        final int mLoggingLevel;
        
        final int mMinJobSchedulerId;
        
        final int mMaxJobSchedulerId;
       
        final int mMaxSchedulerLimit;
        private final boolean mIsUsingDefaultTaskExecutor;
    
        Configuration(@NonNull Configuration.Builder builder) {
            if (builder.mExecutor == null) {
                mExecutor = createDefaultExecutor();
            } else {
                mExecutor = builder.mExecutor;
            }
    
            if (builder.mTaskExecutor == null) {
                mIsUsingDefaultTaskExecutor = true;
                mTaskExecutor = createDefaultExecutor();
            } else {
                mIsUsingDefaultTaskExecutor = false;
                mTaskExecutor = builder.mTaskExecutor;
            }
    
            if (builder.mWorkerFactory == null) {
                mWorkerFactory = WorkerFactory.getDefaultWorkerFactory();
            } else {
                mWorkerFactory = builder.mWorkerFactory;
            }
    
            if (builder.mInputMergerFactory == null) {
                mInputMergerFactory = InputMergerFactory.getDefaultInputMergerFactory();
            } else {
                mInputMergerFactory = builder.mInputMergerFactory;
            }
    
            mLoggingLevel = builder.mLoggingLevel;
            mMinJobSchedulerId = builder.mMinJobSchedulerId;
            mMaxJobSchedulerId = builder.mMaxJobSchedulerId;
            mMaxSchedulerLimit = builder.mMaxSchedulerLimit;
        }
    }
    
    

    我们截取了Configuration部分重要的源码; 它是采用的建造者模式,从上面的源码看出,它是一个配置类,里面保存了很多配置信息,比如Executor,WorkerFactory,InputMergerFactory等,这里的Executor默认是一个采用Executors.newFixedThreadPool的线程池

    然后我们接着看上面注释2的地方使用构造方法初始化了sDefaultInstance,并将其赋值给了sDelegatedInstance,所以程序在一开始就初始化了这两个对象,当我们调用的时候就不为空了

    我们看内部实现:

    sDefaultInstance = new WorkManagerImpl(
                            context,
                            configuration,
                            //-----1-----
                            new WorkManagerTaskExecutor(configuration.getTaskExecutor()));
    
    public WorkManagerImpl(
            @NonNull Context context,
            @NonNull Configuration configuration,
            @NonNull TaskExecutor workTaskExecutor) {
        this(context,
                configuration,
                workTaskExecutor,
                //-----2-----
                context.getResources().getBoolean(R.bool.workmanager_test_configuration));
    }
    
    public WorkManagerImpl(
            @NonNull Context context,
            @NonNull Configuration configuration,
            @NonNull TaskExecutor workTaskExecutor,
            boolean useTestDatabase) {
        this(context,
                configuration,
                workTaskExecutor,
                //-----3-----
                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()));
        //-----4-----
        List<Scheduler> schedulers = createSchedulers(applicationContext, workTaskExecutor);
        /-----5-----
        Processor processor = new Processor(
                context,
                configuration,
                workTaskExecutor,
                database,
                schedulers);
        //-----6-----
        internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
    }
    
    
    • 注释1处提供了一个新的类WorkManagerTaskExecutor,它内部提供了主线程和子线程切换以及执行的操作

    • 注释2配置了一个标志位,用来标记是否采用测试数据库,后面会使用到这个参数

    • 注释3就开始创建数据库WorkDatabase,它的源码如下,我们看到如果useTestDatabase为true的话,就会采用Room.inMemoryDatabaseBuilder创建数据库,并且允许在主线程查询

    @Database(entities = {
        Dependency.class,
        WorkSpec.class,
        WorkTag.class,
        SystemIdInfo.class,
        WorkName.class,
        WorkProgress.class,
        Preference.class},
        version = 10)
    @TypeConverters(value = {Data.class, WorkTypeConverters.class})
    public abstract class WorkDatabase extends RoomDatabase {
    ...
    }
    public static WorkDatabase create(
            @NonNull final Context context,
            @NonNull Executor queryExecutor,
            boolean useTestDatabase) {
        RoomDatabase.Builder<WorkDatabase> builder;
        if (useTestDatabase) {
            builder = Room.inMemoryDatabaseBuilder(context, WorkDatabase.class)
                    .allowMainThreadQueries();
        } else {
            String name = WorkDatabasePathHelper.getWorkDatabaseName();
            builder = Room.databaseBuilder(context, WorkDatabase.class, name);
            builder.openHelperFactory(new SupportSQLiteOpenHelper.Factory() {
                @NonNull
                @Override
                public SupportSQLiteOpenHelper create(
                        @NonNull SupportSQLiteOpenHelper.Configuration configuration) {
                    SupportSQLiteOpenHelper.Configuration.Builder configBuilder =
                            SupportSQLiteOpenHelper.Configuration.builder(context);
                    configBuilder.name(configuration.name)
                            .callback(configuration.callback)
                            .noBackupDirectory(true);
                    FrameworkSQLiteOpenHelperFactory factory =
                            new FrameworkSQLiteOpenHelperFactory();
                    return factory.create(configBuilder.build());
                }
            });
        }
    }
    
    
    • 注释4处调用了createSchedulers来创建一个Scheduler的List,后面会使用到
    public List<Scheduler> createSchedulers(
            @NonNull Context context,
            @NonNull 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));
    }
    
    
    • 注释5处创建了一个Process对象,官方的解释是它可以根据需要智能地调度和执行工作,完美看到它几乎把所有的配置信息都保存在对象中,包括配置configuration,workTaskExecutor, 数据库database,调度schedulers.

    • 注释6处就是将上面创建的对象赋值到WorkManagerImpl成员变量中,到此处WorkManager.getInstance(context)创建实例的过程就结束了

    enqueue(request)源码分析

    enqueue实际调用的是WorkContinuationImpl的enqueue方法,源码如下:

    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.
               //-----1-----
               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;
    }
    
    
    • 在注释1的地方生成了一个EnqueueRunnable,它继承Runnable,然后通过之前在Configuration中创建的线程池,将EnqueueRunnable加入到线程池中执行,返回一个Operation对象。我们关注一下EnqueueRunnable的run方法:
    public class EnqueueRunnable implements Runnable {
    
       private static final String TAG = Logger.tagWithPrefix("EnqueueRunnable");
    
       private final WorkContinuationImpl mWorkContinuation;
       private final OperationImpl mOperation;
    
       public EnqueueRunnable(@NonNull WorkContinuationImpl workContinuation) {
           mWorkContinuation = workContinuation;
           mOperation = new OperationImpl();
       }
    
       @Override
       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);
                   //-----1-----
                   scheduleWorkInBackground();
               }
               mOperation.setState(Operation.SUCCESS);
           } catch (Throwable exception) {
               mOperation.setState(new Operation.State.FAILURE(exception));
           }
       }
     
       public Operation getOperation() {
           return mOperation;
       }
    }
    
    

    我们关注注释1处的scheduleWorkInBackground方法:

    public void scheduleWorkInBackground() {
        WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
        Schedulers.schedule(
                workManager.getConfiguration(),
                workManager.getWorkDatabase(),
                workManager.getSchedulers());
    }
    
    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) {
                //-----1-----
                scheduler.schedule(eligibleWorkSpecsArray);
            }
        }
    }
    
    

    在注释1处调用了scheduler.schedule方法,我们前面将GreedyScheduler加入到队列中,我们这里主要分析该类:

     public void schedule(@NonNull WorkSpec... workSpecs) {
       if (mIsMainProcess == null) {
           // The default process name is the package name.
           mIsMainProcess = TextUtils.equals(mContext.getPackageName(), getProcessName());
       }
    
       if (!mIsMainProcess) {
           Logger.get().info(TAG, "Ignoring schedule request in non-main process");
           return;
       }
    
       registerExecutionListenerIfNeeded();
    
       List<WorkSpec> constrainedWorkSpecs = new ArrayList<>();
       List<String> constrainedWorkSpecIds = new ArrayList<>();
       for (WorkSpec workSpec : workSpecs) {
            //-----1-----
           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));
                   //-----2-----
                   mWorkManagerImpl.startWork(workSpec.id);
               }
           }
       }
    
       synchronized (mLock) {
           if (!constrainedWorkSpecs.isEmpty()) {
               Logger.get().debug(TAG, String.format("Starting tracking for [%s]",
                       TextUtils.join(",", constrainedWorkSpecIds)));
               mConstrainedWorkSpecs.addAll(constrainedWorkSpecs);
               mWorkConstraintsTracker.replace(mConstrainedWorkSpecs);
           }
       }
    }
    
    

    首先在注释1处判断是否有约束条件,有的话将其任务和任务id加入到集合中,没有的话直接执行注释2处的startWork方法。我们先关注没有约束条件时候的startWork方法:

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

    接着通过线程池调用了StartWorkRunnable,它是一个Runnable:

    public class StartWorkRunnable implements Runnable {
    
        private WorkManagerImpl mWorkManagerImpl;
        private String mWorkSpecId;
        private WorkerParameters.RuntimeExtras mRuntimeExtras;
    
        public StartWorkRunnable(
                WorkManagerImpl workManagerImpl,
                String workSpecId,
                WorkerParameters.RuntimeExtras runtimeExtras) {
            mWorkManagerImpl = workManagerImpl;
            mWorkSpecId = workSpecId;
            mRuntimeExtras = runtimeExtras;
        }
    
        @Override
        public void run() {
            //-----1-----
            mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);
        }
    }
    
    

    接着上面注释1处的getProcessor().startWork方法,Processor的startWorker源码如下:

    public boolean startWork(
            @NonNull String id,
            @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
    
        WorkerWrapper workWrapper;
        synchronized (mLock) {
            // Work may get triggered multiple times if they have passing constraints
            // and new work with those constraints are added.
            if (mEnqueuedWorkMap.containsKey(id)) {
                Logger.get().debug(
                        TAG,
                        String.format("Work %s is already enqueued for processing", id));
                return false;
            }
    
            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);
        }
        //-----1-----
        mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
        Logger.get().debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
        return true;
    }
    
    

    它是一个work的包装类WorkWrapper,然后执行注释1处的方法, 我们看WorkWrapper的源码:

    public class WorkerWrapper implements Runnable {
        public void run() {
            mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
            mWorkDescription = createWorkDescription(mTags);
            runWorker();
        }
        private void runWorker() {
            ...
            if (mWorker == null) {
                //-----1-----
                mWorker = mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback(
                        mAppContext,
                        mWorkSpec.workerClassName,
                        params);
            }
             mWorkTaskExecutor.getMainThreadExecutor()
                    .execute(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                Logger.get().debug(TAG, String.format("Starting work for %s",
                                        mWorkSpec.workerClassName));
                                //-----2-----
                                mInnerFuture = mWorker.startWork();
                                future.setFuture(mInnerFuture);
                            } catch (Throwable e) {
                                future.setException(e);
                            }
    
                        }
                    });
        }
    }
    
    
    • 在注释1处通过类名,采用反射机制获取到ListenableWorker对象。其中Worker类继承自ListenableWorker类。
    • 注释2调用ListenableWorker.startWork,它实际上是调用Worker类的startWork方法,Worker的源码如下:
    public abstract class Worker extends ListenableWorker {
    
        public abstract @NonNull Result 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;
        }
    }
    
    
    • 上面我们看到startWork方法实际上调用的是我们自己实现的doWork方法,到这里终于调用了我们需要的逻辑。

    有约束Constraints的任务是如何被执行的?

    我反编译了我的apk,拿到了androidManifest.xml

        <provider
            //-----1-----
            android:name="androidx.work.impl.WorkManagerInitializer"
            android:authorities="com.oman.forward.workmanager-init"
            android:directBootAware="false"
            android:exported="false"
            android:multiprocess="true" />
    
        <service
            android:name="androidx.work.impl.background.systemalarm.SystemAlarmService"
            android:directBootAware="false"
            android:enabled="@bool/enable_system_alarm_service_default"
            android:exported="false" />
        <service
            android:name="androidx.work.impl.background.systemjob.SystemJobService"
            android:directBootAware="false"
            android:enabled="@bool/enable_system_job_service_default"
            android:exported="true"
            android:permission="android.permission.BIND_JOB_SERVICE" />
        <service
            android:name="androidx.work.impl.foreground.SystemForegroundService"
            android:directBootAware="false"
            android:enabled="@bool/enable_system_foreground_service_default"
            android:exported="false" />
    
        <receiver
            android:name="androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver"
            android:directBootAware="false"
            android:enabled="true"
            android:exported="false" />
        <receiver
            android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy"
            android:directBootAware="false"
            android:enabled="false"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
                <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
            </intent-filter>
        </receiver>
        <receiver
            android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy"
            android:directBootAware="false"
            android:enabled="false"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BATTERY_OKAY" />
                <action android:name="android.intent.action.BATTERY_LOW" />
            </intent-filter>
        </receiver>
        <receiver
            android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$StorageNotLowProxy"
            android:directBootAware="false"
            android:enabled="false"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.DEVICE_STORAGE_LOW" />
                <action android:name="android.intent.action.DEVICE_STORAGE_OK" />
            </intent-filter>
        </receiver>
        <receiver
            //-----2-----
            android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$NetworkStateProxy"
            android:directBootAware="false"
            android:enabled="false"
            android:exported="false">
            <intent-filter>
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            </intent-filter>
        </receiver>
        <receiver
            android:name="androidx.work.impl.background.systemalarm.RescheduleReceiver"
            android:directBootAware="false"
            android:enabled="false"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.TIME_SET" />
                <action android:name="android.intent.action.TIMEZONE_CHANGED" />
            </intent-filter>
        </receiver>
        <receiver
            android:name="androidx.work.impl.background.systemalarm.ConstraintProxyUpdateReceiver"
            android:directBootAware="false"
            android:enabled="@bool/enable_system_alarm_service_default"
            android:exported="false">
            <intent-filter>
                <action android:name="androidx.work.impl.background.systemalarm.UpdateProxies" />
            </intent-filter>
        </receiver>
    
        <service
            android:name="androidx.room.MultiInstanceInvalidationService"
            android:exported="false" />
        <provider
            android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
            android:authorities="com.oman.forward.lifecycle-process"
            android:exported="false"
            android:multiprocess="true" />
    
    

    在最开始注释1的地方有一个provider,正是我们分析初始化workManager的时候的那个Provider。

    而且我们还看到有很多的Receiver,我们以注释2处的NetworkStateProxy为例进行分析,它有一个action是CONNECTIVITY_CHANGE,所以当网络状态发生变化的时候会触发这个Receiver。我们看源码部分:

    abstract class ConstraintProxy extends BroadcastReceiver {
        private static final String TAG = Logger.tagWithPrefix("ConstraintProxy");
    
        @Override
        public void onReceive(Context context, Intent intent) {
            Logger.get().debug(TAG, String.format("onReceive : %s", intent));
            //-----1-----
            Intent constraintChangedIntent = CommandHandler.createConstraintsChangedIntent(context);
            context.startService(constraintChangedIntent);
        }
    
        /**
         * Proxy for Battery Not Low constraint
         */
        public static class BatteryNotLowProxy extends ConstraintProxy {
        }
    
        /**
         * Proxy for Battery Charging constraint
         */
        public static class BatteryChargingProxy extends ConstraintProxy {
        }
    
        /**
         * Proxy for Storage Not Low constraint
         */
        public static class StorageNotLowProxy extends ConstraintProxy {
        }
    
        /**
         * Proxy for Network State constraints
         */
        public static class NetworkStateProxy extends ConstraintProxy {
        }   
    }
    
    
    • 当网络状态发生变化的时候会执行上面注释1的CommandHandler的createConstraintsChangedIntent(context)方法,源码如下:
    static final String ACTION_CONSTRAINTS_CHANGED = "ACTION_CONSTRAINTS_CHANGED";
    
    static Intent createConstraintsChangedIntent(@NonNull Context context) {
        Intent intent = new Intent(context, SystemAlarmService.class);
        intent.setAction(ACTION_CONSTRAINTS_CHANGED);
        return intent;
    }
    
    
    • 这里启动了一个SystemAlarmService,它是一个Service,我们关注它的onStartCommand方法:
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        if (mIsShutdown) {
            Logger.get().info(TAG,
                    "Re-initializing SystemAlarmDispatcher after a request to shut-down.");
    
            // Destroy the old dispatcher to complete it's lifecycle.
            mDispatcher.onDestroy();
            // Create a new dispatcher to setup a new lifecycle.
            initializeDispatcher();
            // Set mIsShutdown to false, to correctly accept new commands.
            mIsShutdown = false;
        }
        
        if (intent != null) {
            mDispatcher.add(intent, startId);
        }
    
        // If the service were to crash, we want all unacknowledged Intents to get redelivered.
        return Service.START_REDELIVER_INTENT;
    }
    
    
    • 调用了mDispatcher.add(intent, startId)方法:
    public boolean add(@NonNull final Intent intent, final int startId) {
        Logger.get().debug(TAG, String.format("Adding command %s (%s)", intent, startId));
        assertMainThread();
        String action = intent.getAction();
        if (TextUtils.isEmpty(action)) {
            Logger.get().warning(TAG, "Unknown command. Ignoring");
            return false;
        }
        if (CommandHandler.ACTION_CONSTRAINTS_CHANGED.equals(action)
                && hasIntentWithAction(CommandHandler.ACTION_CONSTRAINTS_CHANGED)) {
            return false;
        }
    
        intent.putExtra(KEY_START_ID, startId);
        synchronized (mIntents) {
            boolean hasCommands = !mIntents.isEmpty();
            //-----1-----
            mIntents.add(intent);
            if (!hasCommands) {
                // Only call processCommand if this is the first command.
                // The call to dequeueAndCheckForCompletion will process the remaining commands
                // in the order that they were added.
                //-----2-----
                processCommand();
            }
        }
        return true;
    }
    
    
    • 注释1处将intent添加到了mIntents中,注释2处调用了processCommand方法:
    private void processCommand() {
        assertMainThread();
        PowerManager.WakeLock processCommandLock =
                WakeLocks.newWakeLock(mContext, PROCESS_COMMAND_TAG);
        try {
            processCommandLock.acquire();
            // Process commands on the background thread.
            mWorkManager.getWorkTaskExecutor().executeOnBackgroundThread(new Runnable() {
                @Override
                public void run() {
                    synchronized (mIntents) {
                        //-----1-----
                        mCurrentIntent = mIntents.get(0);
                    }
    
                    if (mCurrentIntent != null) {
                        final String action = mCurrentIntent.getAction();
                        final int startId = mCurrentIntent.getIntExtra(KEY_START_ID,
                                DEFAULT_START_ID);
                        Logger.get().debug(TAG,
                                String.format("Processing command %s, %s", mCurrentIntent,
                                        startId));
                        final PowerManager.WakeLock wakeLock = WakeLocks.newWakeLock(
                                mContext,
                                String.format("%s (%s)", action, startId));
                        try {
                            Logger.get().debug(TAG, String.format(
                                    "Acquiring operation wake lock (%s) %s",
                                    action,
                                    wakeLock));
    
                            wakeLock.acquire();
                            //-----2-----
                            mCommandHandler.onHandleIntent(mCurrentIntent, startId,
                                    SystemAlarmDispatcher.this);
                        } catch (Throwable throwable) {
                            Logger.get().error(
                                    TAG,
                                    "Unexpected error in onHandleIntent",
                                    throwable);
                        }  finally {
                            Logger.get().debug(
                                    TAG,
                                    String.format(
                                            "Releasing operation wake lock (%s) %s",
                                            action,
                                            wakeLock));
                            wakeLock.release();
                            // Check if we have processed all commands
                            postOnMainThread(
                                    new DequeueAndCheckForCompletion(SystemAlarmDispatcher.this));
                        }
                    }
                }
            });
        } finally {
            processCommandLock.release();
        }
    }
    
    
    • 上面注释1得到了之前加入的Intent。在注释2处调用了mCommandHandler.onHandleIntent
    void onHandleIntent(
            @NonNull Intent intent,
            int startId,
            @NonNull SystemAlarmDispatcher dispatcher) {
    
        String action = intent.getAction();
        
        if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {
            handleConstraintsChanged(intent, startId, dispatcher);
        } else if (ACTION_RESCHEDULE.equals(action)) {
            handleReschedule(intent, startId, dispatcher);
        } else {
            Bundle extras = intent.getExtras();
            if (!hasKeys(extras, KEY_WORKSPEC_ID)) {
                Logger.get().error(TAG,
                        String.format("Invalid request for %s, requires %s.",
                                action,
                                KEY_WORKSPEC_ID));
            } else {
                if (ACTION_SCHEDULE_WORK.equals(action)) {
                    handleScheduleWorkIntent(intent, startId, dispatcher);
                } else if (ACTION_DELAY_MET.equals(action)) {
                    handleDelayMet(intent, startId, dispatcher);
                } else if (ACTION_STOP_WORK.equals(action)) {
                    handleStopWork(intent, dispatcher);
                } else if (ACTION_EXECUTION_COMPLETED.equals(action)) {
                    handleExecutionCompleted(intent, startId);
                } else {
                    Logger.get().warning(TAG, String.format("Ignoring intent %s", intent));
                }
            }
        }
    }
    
    
    • 在这个方法中会判断传入进来的action,进行相应的方法调用。因为这里的action是ACTION_CONSTRAINTS_CHANGED,所以会执行注释1处的handleConstraintsChanged方法:
    private void handleConstraintsChanged(
            @NonNull Intent intent, int startId,
            @NonNull SystemAlarmDispatcher dispatcher) {
    
        Logger.get().debug(TAG, String.format("Handling constraints changed %s", intent));
        // Constraints changed command handler is synchronous. No cleanup
        // is necessary.
        ConstraintsCommandHandler changedCommandHandler =
                new ConstraintsCommandHandler(mContext, startId, dispatcher);
        //-----1-----
        changedCommandHandler.handleConstraintsChanged();
    }
    
    
    • 调用了上面注释1处的changedCommandHandler.handleConstraintsChanged()方法,源码如下:
    void handleConstraintsChanged() {
        List<WorkSpec> candidates = mDispatcher.getWorkManager().getWorkDatabase()
                .workSpecDao()
                .getScheduledWork();
    
        // Update constraint proxy to potentially disable proxies for previously
        // completed WorkSpecs.
        ConstraintProxy.updateAll(mContext, candidates);
    
        // This needs to be done to populate matching WorkSpec ids in every constraint controller.
        mWorkConstraintsTracker.replace(candidates);
    
        List<WorkSpec> eligibleWorkSpecs = new ArrayList<>(candidates.size());
        // Filter candidates should have already been scheduled.
        long now = System.currentTimeMillis();
        for (WorkSpec workSpec : candidates) {
            String workSpecId = workSpec.id;
            long triggerAt = workSpec.calculateNextRunTime();
            if (now >= triggerAt && (!workSpec.hasConstraints()
                    || mWorkConstraintsTracker.areAllConstraintsMet(workSpecId))) {
                eligibleWorkSpecs.add(workSpec);
            }
        }
    
        for (WorkSpec workSpec : eligibleWorkSpecs) {
            String workSpecId = workSpec.id;
            //-----1-----
            Intent intent = CommandHandler.createDelayMetIntent(mContext, workSpecId);
            Logger.get().debug(TAG, String.format(
                    "Creating a delay_met command for workSpec with id (%s)", workSpecId));
            mDispatcher.postOnMainThread(
                    //-----2-----
                    new SystemAlarmDispatcher.AddRunnable(mDispatcher, intent, mStartId));
        }
    
        mWorkConstraintsTracker.reset();
    }
    
    
    • 注释1处创建了一个action为ACTION_DELAY_MET的Intent。
    • 注释2处将这个intent加入到一个Runnable中,将这个Runnable切换到主线程执行。AddRunnable的源码如下:
    static class AddRunnable implements Runnable {
        private final SystemAlarmDispatcher mDispatcher;
        private final Intent mIntent;
        private final int mStartId;
    
        AddRunnable(@NonNull SystemAlarmDispatcher dispatcher,
                @NonNull Intent intent,
                int startId) {
            mDispatcher = dispatcher;
            mIntent = intent;
            mStartId = startId;
        }
    
        @Override
        public void run() {
            mDispatcher.add(mIntent, mStartId);
        }
    }
    
    

    我们看到又调用了mDispatcher.add方法,最终还是会 -> processCommand -> mCommandHandler.onHandleIntent , 但是这时候在onHandleIntent中的action就不是ACTION_CONSTRAINTS_CHANGED,而是ACTION_DELAY_MET了,所以要执行handleDelayMet方法:

    private void handleDelayMet(
            @NonNull Intent intent,
            int startId,
            @NonNull SystemAlarmDispatcher dispatcher) {
    
        Bundle extras = intent.getExtras();
        synchronized (mLock) {
            String workSpecId = extras.getString(KEY_WORKSPEC_ID);
            Logger.get().debug(TAG, String.format("Handing delay met for %s", workSpecId));
    
            // Check to see if we are already handling an ACTION_DELAY_MET for the WorkSpec.
            // If we are, then there is nothing for us to do.
            if (!mPendingDelayMet.containsKey(workSpecId)) {
                DelayMetCommandHandler delayMetCommandHandler =
                        new DelayMetCommandHandler(mContext, startId, workSpecId, dispatcher);
                mPendingDelayMet.put(workSpecId, delayMetCommandHandler);
                //-----1-----
                delayMetCommandHandler.handleProcessWork();
            } else {
                Logger.get().debug(TAG,
                        String.format("WorkSpec %s is already being handled for ACTION_DELAY_MET",
                                workSpecId));
            }
        }
    }
    
    
    • 在注释1处调用了delayMetCommandHandler.handleProcessWork()方法,如下:
    void handleProcessWork() {
        mWakeLock = WakeLocks.newWakeLock(
                mContext,
                String.format("%s (%s)", mWorkSpecId, mStartId));
        Logger.get().debug(TAG,
                String.format("Acquiring wakelock %s for WorkSpec %s", mWakeLock, mWorkSpecId));
        mWakeLock.acquire();
    
        WorkSpec workSpec = mDispatcher.getWorkManager()
                .getWorkDatabase()
                .workSpecDao()
                .getWorkSpec(mWorkSpecId);
    
        // This should typically never happen. Cancelling work should remove alarms, but if an
        // alarm has already fired, then fire a stop work request to remove the pending delay met
        // command handler.
        if (workSpec == null) {
            stopWork();
            return;
        }
    
        // Keep track of whether the WorkSpec had constraints. This is useful for updating the
        // state of constraint proxies when onExecuted().
        mHasConstraints = workSpec.hasConstraints();
    
        if (!mHasConstraints) {
            Logger.get().debug(TAG, String.format("No constraints for %s", mWorkSpecId));
            onAllConstraintsMet(Collections.singletonList(mWorkSpecId));
        } else {
            // Allow tracker to report constraint changes
            //-----1-----
            mWorkConstraintsTracker.replace(Collections.singletonList(workSpec));
        }
    }
    
    
    • 因为有约束条件,所以会进入注释1处的replace方法:
     *
    * @param workSpecs A list of {@link WorkSpec}s to monitor constraints for
    */
    @SuppressWarnings("unchecked")
    public void replace(@NonNull Iterable<WorkSpec> workSpecs) {
       synchronized (mLock) {
           for (ConstraintController<?> controller : mConstraintControllers) {
               controller.setCallback(null);
           }
    
           for (ConstraintController<?> controller : mConstraintControllers) {
                //-----1-----
               controller.replace(workSpecs);
           }
    
           for (ConstraintController<?> controller : mConstraintControllers) {
               controller.setCallback(this);
           }
       }
    }
    
    public void replace(@NonNull Iterable<WorkSpec> workSpecs) {
       mMatchingWorkSpecIds.clear();
    
       for (WorkSpec workSpec : workSpecs) {
           if (hasConstraint(workSpec)) {
               mMatchingWorkSpecIds.add(workSpec.id);
           }
       }
    
       if (mMatchingWorkSpecIds.isEmpty()) {
           mTracker.removeListener(this);
       } else {
           mTracker.addListener(this);
       }
       //-----2-----
       updateCallback(mCallback, mCurrentValue);
    }
    
    
    • 紧接着调用了注释1处的replace到注释2处的updateCallback
    private void updateCallback(
           @Nullable OnConstraintUpdatedCallback callback,
           @Nullable T currentValue) {
    
       // We pass copies of references (callback, currentValue) to updateCallback because public
       // APIs on ConstraintController may be called from any thread, and onConstraintChanged() is
       // called from the main thread.
       if (mMatchingWorkSpecIds.isEmpty() || callback == null) {
           return;
       }
    
       if (currentValue == null || isConstrained(currentValue)) {
           callback.onConstraintNotMet(mMatchingWorkSpecIds);
       } else {
        //-----1-----
           callback.onConstraintMet(mMatchingWorkSpecIds);
       }
    }
    
    
    • 接着调用到了注释1处的onConstraintMet方法。接着会执行到GreedyScheduler的onAllConstraintsMet方法中:
    public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
       for (String workSpecId : workSpecIds) {
           Logger.get().debug(
                   TAG,
                   String.format("Constraints met: Scheduling work ID %s", workSpecId));
           mWorkManagerImpl.startWork(workSpecId);
       }
    }
    
    
    • 到这里,就会发现开始执行mWorkManagerImpl.startWork方法了:
    public void startWork(
           @NonNull String workSpecId,
           @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
       mWorkTaskExecutor
               .executeOnBackgroundThread(
                       new StartWorkRunnable(this, workSpecId, runtimeExtras));
    }
    
    

    这里的StartWorkRunnable就和前面分析的连接上了,最终会调用我们自定义的doWork方法

    有需要文章中完整代码的同学 现在点击此处传送门 即可免费获取

    现在点击链接还可以获取《更多 Android 源码解析+核心笔记+面试真题》

    总结

    WorkManager是一个很优秀的框架,使用起来很方便,只需要自定义Worker,创建请求,加入workManager队列等待执行任务即可,并且能保证任务肯定执行,至于其中的原理,需要好好研究一下源码

    最后我想说:

    学习没有捷径可言,我们要注意记学习,不仅要记,还要写心得体会,文字笔记、画图、总结等,方式很多,但是一定要自己认真去做,不要太相信自己的记忆,只有反复记忆,加深理解才行

    同时,对于程序员而言,不单单是死记硬背,我们有更好的方式去学习,比如写demo去验证。复习知识点时,要及时跟你做过的项目结合起来,这样在面试时就知道怎么聊了,由项目讲到知识点,由一个知识点串联到另一个知识点。复习到一定阶段,可以尝试着去把这些东西串联起来,由点及面,形成知识体系

    对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们

    技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面

    Android 架构师之路还很漫长,与君共勉

    相关文章

      网友评论

        本文标题:Android Jetpack系列(九):WorkManager

        本文链接:https://www.haomeiwen.com/subject/brspwrtx.html