编程知识 cdmana.com

Android Jetpack series (9) : the WorkManager (source)

WorkManager的原理

The previous article explained how to use itWorkManager;In this article, we will follow the steps in the previous article to analyze the source code

生成WorkRequest的源码

The first step generates a to-be-executed onerequest请求

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

we look at the classWorkRequest的源码部分:

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

  	}
}

  • It adopts the builder design pattern,内部成员变量mWorkSpec使用的是Room数据库,可以持久化存储,除非被clear data,So it can be guaranteed even if the system is restarted,It can also ensure that tasks are executed.我们查看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;
    }
}

We see above usedRoom的Entity注解来注解WorkSpec,Indicates that this is a class that holds storage,will be stored in the database; It has one stored in itWorkRequestalmost all information,包括唯一标识id,workerClassName,input,output,constraints等,This information is generated by usWorkRequest中都有涉及

WorkManager.getInstance(this)源码

WorkManager是一个抽象类,它的实现类是WorkManagerImpl,Return in singleton modeWorkManagerImpl对象; 下面注释1的地方调用getInstanceIn fact, the return value is no longer empty,下面我们来分析:

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已经不为空,Where is it assigned?

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

There is such a class in the source codeWorkManagerInitializer,它继承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;
    }
}    

我们都知道ContentProviderThe creation time is at the entry of the programActivityThread.main中,通过调用thread.attach最终回调到activityThread.handleBindApplication方法,在这个方法中ActivityThread会创建Application对象并加载ContentProvider,但是有一点要注意,It just loads firstContentProvider然后在调用Application的onCreate方法

This will be called before the program startsWorkManagerInitializer的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;
    }
}

我们截取了ConfigurationSome important source code; It is the builder pattern adopted,From the source code above,它是一个配置类,里面保存了很多配置信息,比如Executor,WorkerFactory,InputMergerFactory等,这里的ExecutorDefault is one adoptedExecutors.newFixedThreadPool的线程池

Then we move on to the comments above2where initialized using the constructorsDefaultInstance,并将其赋值给了sDelegatedInstance,So the program initializes these two objects at the very beginning,It's not null when we call it

We look at the internal implementation:

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

  • 注释1A new class is provided hereWorkManagerTaskExecutor,It internally provides main thread and child thread switching and operations performed

  • 注释2A flag bit is configured,Used to mark whether to use the test database,This parameter will be used later

  • 注释3Start creating the databaseWorkDatabase,它的源码如下,我们看到如果useTestDatabase为true的话,就会采用Room.inMemoryDatabaseBuilder创建数据库,And allows querying on the main thread

@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对象,The official explanation is that it can intelligently schedule and execute work as needed,Perfect to see that it holds almost all configuration information in objects,包括配置configuration,workTaskExecutor, 数据库database,调度schedulers.

  • 注释6The point is to assign the object created above to WorkManagerImpl成员变量中,到此处WorkManager.getInstance(context)The process of creating the instance is over

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

  • 在注释1where one is generatedEnqueueRunnable,它继承Runnable,然后通过之前在Configuration中创建的线程池,将EnqueueRunnableJoin the thread pool for execution,返回一个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加入到队列中,We mainly analyze this category here:

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

首先在注释1Check whether there are constraints,There are tasks and tasks in itid加入到集合中,If not, execute the annotation directly2处的startWork方法.Let's focus first on when there are no constraintsstartWork方法:

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

Then it is called through the thread poolStartWorkRunnable,它是一个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);
    }
}

Next note above1处的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,Then execute the annotation1处的方法, 我们看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);
                        }

                    }
                });
	}
}

  • 在注释1by class name,Obtained by reflection mechanismListenableWorker对象.其中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;
    }
}

  • 上面我们看到startWorkThe method is actually called by our own implementationdoWork方法,At this point, the logic we need is finally called.

有约束Constraintshow the task is performed?

I decompiled mineapk,拿到了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" />

Comment at the beginning1The place has oneprovider,It is our analysis initializationworkManager的时候的那个Provider.

And we've seen a lot moreReceiver,我们以注释2处的NetworkStateProxy为例进行分析,它有一个action是CONNECTIVITY_CHANGE,So this is triggered when the network state changesReceiver.Let's look at the source code section:

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

  • The above annotation will be executed when the network state changes1的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();
    }
}

  • 上面注释1Got what was added beforeIntent.在注释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));
            }
        }
    }
}

  • In this method, it will judge the incomingaction,进行相应的方法调用.因为这里的action是ACTION_CONSTRAINTS_CHANGED,So the annotation will be executed1处的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();
}

  • The above comment is invoked1处的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);
}

  • The annotation is called immediately after1处的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);
   }
}

  • Then the annotation is called1处的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);
   }
}

  • 到这里,will be found to start executingmWorkManagerImpl.startWork方法了:
public void startWork(
       @NonNull String workSpecId,
       @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
    
   mWorkTaskExecutor
           .executeOnBackgroundThread(
                   new StartWorkRunnable(this, workSpecId, runtimeExtras));
}

这里的StartWorkRunnableIt is connected to the previous analysis,Will eventually call our customdoWork方法

There are students who need the complete code in the article现在私信发送 :“底层源码” 即可免费获取

现在私信发送 “进阶” 还可以获取《更多 Android 源码解析+核心笔记+面试真题》

总结

WorkManager是一个很优秀的框架,使用起来很方便,只需要自定义Worker,创建请求,加入workManagerThe queue waits for the task to be executed,And can ensure that the task is definitely executed,至于其中的原理,Need to study the source code carefully

最后我想说:

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

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

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

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

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

版权声明
本文为[Android technology stack]所创,转载请带上原文链接,感谢
https://cdmana.com/2022/218/202208060641180181.html

Scroll to Top