Android Jetpack系列(九):WorkManager(源码篇)

WorkManager的原理

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

生成WorkRequest的源码

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

val request = OneTimeWorkRequestBuilder()                .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 mTags;        public abstract static class Builder, W extends WorkRequest> {                boolean mBackoffCriteriaSet = false;        UUID mId;        WorkSpec mWorkSpec;        Set 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 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 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 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 schedulers) {    if (schedulers == null || schedulers.size() == 0) {        return;    }    WorkSpecDao workSpecDao = workDatabase.workSpecDao();    List 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 constrainedWorkSpecs = new ArrayList<>();   List 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 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 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

                                                                                                                                                                                                                                                                                                                

在最开始注释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 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 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 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 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 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 架构师之路还很漫长,与君共勉

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章