WorkManager解讀

WorkManager解讀

##一.官方定義:

1.簡介:

​ 使用 WorkManager API 可以輕鬆地調度即使在應用退出或設備重啓時仍應運行的可延遲異步任務。它屬於Android Jetpack的一部分。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-O9bnIkMw-1587623472602)(/Users/tianchuangxin1/Library/Application Support/typora-user-images/image-20200108101944656.png)]

2.主要功能

  • 最高向後兼容到 API 14
    • 在運行 API 23 及以上級別的設備上使用 JobScheduler
    • 在運行 API 14-22 的設備上結合使用 BroadcastReceiver 和 AlarmManager
  • 添加網絡可用性或充電狀態等工作約束
  • 調度一次性或週期性異步任務
  • 監控和管理計劃任務
  • 將任務鏈接起來
  • 確保任務執行,即使應用或設備重啓也同樣執行任務
  • 遵循低電耗模式等省電功能

WorkManager 旨在用於可延遲運行(即不需要立即運行)並且在應用退出或設備重啓時必須能夠可靠運行的任務。目前經過測試得到的結果是,當把後臺應用殺死後,任務停止運行(並不能保活進程)。

二.基本使用

worker的基本使用

###1.創建後臺任務


/**
 * TestWork簡介
 *
 * @author tianchuangxin1
 * @date 2020-01-02 18:11
 */
public class TestWork extends Worker {
    public TestWork(
            @NonNull Context context,
            @NonNull WorkerParameters params) {
        super(context, params);
    }
    @NonNull
    @Override
    public Result doWork() {
      //業務執行
        
       //已成功完成構建
       return Result.success();
       //需要稍後重試
       //return Result.retry();
       //已失敗
       //return Result.failure();
    }
}

2.配置運行任務方式和時間

        //一次性任務
        OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(TestWork.class)
                .addTag("a")
//                .keepResultsForAtLeast()
//                .
                .build();

2.1定義任務運行條件

自定義工作請求常見用例:

  • 處理網絡可用性等任務約束
  • 保證任務執行的延遲時間最短
  • 處理任務重試和退避
  • 處理任務輸入和輸出
  • 使用標記對任務進行分組
2.1.1工作約束

Constraints ,指明工作何時可以運行(比如,設備接通電源,或者有網的情況)

//約束(如果指定多個約束,任務將需要滿足所有約束纔會運行)
Constraints constraints = new Constraints.Builder()
        .setRequiresDeviceIdle(true)//指定{@link WorkRequest}運行時設備是否爲空閒
        .setRequiresCharging(true)//指定要運行的{@link WorkRequest}是否應該插入設備
        .setRequiredNetworkType(NetworkType.NOT_ROAMING)
        .setRequiresBatteryNotLow(true)//指定設備電池是否不應低於臨界閾值
        .setRequiresCharging(true)//網絡狀態
        .setRequiresDeviceIdle(true)//指定{@link WorkRequest}運行時設備是否爲空閒
        .setRequiresStorageNotLow(true)//指定設備可用存儲是否不應低於臨界閾值
        .addContentUriTrigger(myUri,false)//指定內容{@link android.net.Uri}時是否應該運行{@link WorkRequest}更新
        .build();
//一次性任務
OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(TestWork.class)
        .setConstraints(constraints) //WorkRequest 中加入約束
        .build();
2.1.2初始延遲
//任務加入執行隊列後,至少10分鐘之後才能執行(爲啥是至少10分鐘呢?)       
OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(TestWork.class)
//                .setConstraints(constraints)
                .setInitialDelay(10, TimeUnit.MINUTES)
                .build();
2.1.3重試和退避政策
//odWork 中添加重試方法。那麼在之後的重試中會按照規定的約束條件進行延遲退避執行       
OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(TestWork.class)
//                .setConstraints(constraints)
//                .setInitialDelay(10, TimeUnit.MINUTES)
                .setBackoffCriteria(BackoffPolicy.LINEAR,100,TimeUnit.SECONDS) //LINEAR:退避延遲時間線型增長的方式;EXPONENTIAL:退避延遲時間指數型增長
                .build();
2.1.4任務的輸入/輸出(類型)
 
//定義輸入類型
Data data = new Data.Builder()
                .putInt("result",0)
                .build();

        //一次性任務
        OneTimeWorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(TestWork.class)
//                .setConstraints(constraints)
//                .setInitialDelay(10, TimeUnit.MINUTES)
//                .setBackoffCriteria(BackoffPolicy.LINEAR,100,TimeUnit.SECONDS) //LINEAR:退避延遲時間線型增長的方式;EXPONENTIAL:退避延遲時間指數型增長
                .setInputData(data)
                .build();


///////////////////////////////////////////////////////////////////////////////////////////////
//doWork中獲取對應值
 count = getInputData().getInt("result",0);
//執行成功(返回結果)
 Result.success(data)


2.1.5任務標記
   
//對任務添加標記(會按照標記進行分組),就可以對使用特定標記的所有任務執行操作。
//例如,WorkManager.cancelAllWorkByTag(String)
OneTimeWorkRequest cacheCleanupTask =
            new OneTimeWorkRequest.Builder(UploadWork.class)
        .setConstraints(constraints)
        .addTag("test")
        .build();
2.2其他用法
  • 實時觀察任務進度
  • 獲取任務執行的狀態
  • 實時更改work工作進度

主要是配合LiveData綁定數據,監聽同步更新

3.任務添加/取消

//加入隊列
WorkManager.getInstance(this).enqueue(uploadWorkRequest);

//取消所有work
 WorkManager.getInstance(this).cancelAllWork();

 //取消tag爲test的所有任務
WorkManager.getInstance(this).cancelAllWorkByTag("test");  



三、運行機制

UML圖如下:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-X47aP0bs-1587623472606)(/Users/tianchuangxin1/Library/Application Support/typora-user-images/image-20200114155645461.png)]

針對類圖中的內容,總結如下:

  • Worker 類是一個抽象類,我們需要繼承 Worker 類,並實現 doWork()方法,在 doWork()方法中實現我們需要在後臺執行的業務邏輯,我們可以將業務封裝在多個 Woker 的實現類中。
  • WorkRequest 代表一個單獨的後臺任務。WorkRequest 類也是一個抽象類,我們通常會直接使用它的兩個子類,OneTimeWorkRequest 和 PeriodicWorkRequest。
  • WorkRequest 還有一個內部類 WorkRequest.Builder,OneTimeWorkRequest.Builder 和 PeriodicWorkRequest.Builder都是 WorkRequest.Builder 的子類,Builder 使用的是創建者模式,我們通過 Builder 實例來創建具體的 WorkRequest 對象。
  • WorkRequest 可以指定那個 Worker 來執行任務。每一個 WorkRequest 實例都會生成唯一的id,可以通過 id 取消任務,或者獲取任務的狀態信息等。
  • Constraints 類用來設定執行任務的條件,比如設備空閒時、充電時、電量充足時等等,通過 WorkRequest 的 setConstraints() 方法將 Constraints 對象傳入。
  • WorkInfo 用來記錄任務信息,WorkInfo 有一個內部枚舉State,用來保存任務的狀態,一個後臺任務有以下六種狀態、分別是 ENQUEUED、RUNNING、SUCCEEDED、FAILED、BLOCKED 及 CANCELLED。
  • 如果後臺任務需要接收一些輸入的參數數據,可以通過 Data 對象,Data.Builder 針對不同數據類型封裝了一系列 putXXX()方法。後臺任務執行後返回的結果,通過 Data 對象封裝的一系列 getXXX()方法獲取。
  • WorkContinuation,很多 WorkManager 的高級用法都與這個類有關,比如建立任務鏈等,後面在 WorkManager 的高級用法中詳細介紹。
  • WorkManager,後臺任務的調度器,它負責將我們創建好的 WorkRequest 對象添加到隊列中,根據我們設定的約束條件(Constraints對象),並且會綜合考慮設備資源的使用情況,進行任務的調度。

四、源碼分析

##一、WorkManager的初始化

####1、WorkManager實例化(手動觸發):

WorkManager.getInstance(this).enqueue(uploadWorkRequest);

進入getInstance看看WorkManager實例化

public static @NonNull WorkManager getInstance(@NonNull Context context) {
  	//WorkManager是抽象類,他的實現類是WorkManagerImpl
    return WorkManagerImpl.getInstance(context);
}

進入WorkManagerImpl看它的實現

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static @NonNull WorkManagerImpl getInstance(@NonNull Context context) {
    synchronized (sLock) {
      	//加鎖,單例模式
        WorkManagerImpl instance = getInstance();
        if (instance == null) {// 爲空
            Context appContext = context.getApplicationContext();
            if (appContext instanceof Configuration.Provider) {
              	//進行初始化
                initialize(
                        appContext,
                        ((Configuration.Provider) appContext).getWorkManagerConfiguration());
                instance = getInstance(appContext);
            } else {
                throw new IllegalStateException("WorkManager is not initialized properly.  You "
                        + "have explicitly disabled WorkManagerInitializer in your manifest, "
                        + "have not manually called WorkManager#initialize at this point, and "
                        + "your Application does not implement Configuration.Provider.");
            }
        }

        return instance;
    }
}

initialize方法:

@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) {
                sDefaultInstance = new WorkManagerImpl(
                        context,
                        configuration,
                        new WorkManagerTaskExecutor(configuration.getTaskExecutor())); //實例化
            }
            sDelegatedInstance = sDefaultInstance;
        }
    }
}

WorkManager基本實例化結束,這是我們手動調用的。

在應用進程被殺死後,重新打開應用,未執行完成的WorkReques或者排隊狀態的WorkRequest會重新開始執行。

###那這個時候WorkManager是怎麼的啓動初始化的呢?

解壓APP,我們在AndroidManifest.xml文件中找到了一個provider的配置項。WorkManagerInitializer類又繼承自ContentProvider,關於ContentProvider的啓動過程不過多介紹,在應用的啓動時候,會通過ActivityThread初始化ContentProvider(WorkManagerInitializer),即執行了onCreate方法完成了WorkManager的初始化。

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

###初始化過程

initialize方法內部通過調用WorkManagerImpl的構造方法完成初始化任務。 (1)WorkDatabase創建數據庫擦操作,內部使用的是Room框架。 (2)createSchedulers創建調度者集合。這裏面主要有兩種:GreedyScheduler和SystemJobScheduler(如果系統版本號大於23的話)。 (3)創建Processor類。

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
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()));
  	//configuration、workTaskExecutor、database已經在前幾次創建完成
  	//接下來纔是重要的
  	//createSchedulers開始創建任務調度
    List<Scheduler> schedulers = createSchedulers(applicationContext, workTaskExecutor);
  	//創建Processor 可以根據需要有計劃的處理Work
    Processor processor = new Processor(
            context,
            configuration,
            workTaskExecutor,
            database,
            schedulers);
  	//
    internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
}

createSchedulers方法會創建兩種調度方式

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@NonNull
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.
      			//貪婪調度,指定直接執行,如果沒有約束條件
            new GreedyScheduler(context, taskExecutor, this));
}


Schedulers.createBestAvailableBackgroundScheduler(context, this)會根據api等級選擇使用SystemJobScheduler或者SystemAlarmScheduler

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

    Scheduler scheduler;
		//判斷api等級
    if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
      	//>23使用JobScheduler
        scheduler = new SystemJobScheduler(context, workManager);
        setComponentEnabled(context, SystemJobService.class, true);
        Logger.get().debug(TAG, "Created SystemJobScheduler and enabled SystemJobService");
    } else {
      //否則使用AlarmScheduler
        scheduler = tryCreateGcmBasedScheduler(context);
        if (scheduler == null) {
            scheduler = new SystemAlarmScheduler(context);
            setComponentEnabled(context, SystemAlarmService.class, true);
            Logger.get().debug(TAG, "Created SystemAlarmScheduler");
        }
    }
    return scheduler;
}

到這裏WorkManagerImpl基本實例化完成。

###2.執行隊列WorkManager.getInstance().enqueue()方法

WorkManager.getInstance返回的是WorkManagerImpl實例,所以我們進入到enqueue方法中看看。調用WorkContinuationImpl實例的enqueue方法。

@Override
@NonNull
public Operation enqueue(
        @NonNull List<? extends WorkRequest> workRequests) {

    // This error is not being propagated as part of the Operation, as we want the
    // app to crash during development. Having no workRequests is always a developer error.
    if (workRequests.isEmpty()) {
        throw new IllegalArgumentException(
                "enqueue needs at least one WorkRequest.");
    }
  	//實例化WorkContinuationImpl
    return new WorkContinuationImpl(this, workRequests).enqueue();
}

調用WorkContinuationImpl實例的enqueue方法,直接進入WorkContinuationImpl.enqueue方法看看。

@Override
public @NonNull Operation enqueue() {
    // Only enqueue if not already enqueued.
    if (!mEnqueued) {
        // The runnable walks the hierarchy of the continuations
        // and marks them enqueued using the markEnqueued() method, parent first.
      	//創建EnqueueRunnable繼承自Runnable
        EnqueueRunnable runnable = new EnqueueRunnable(this);
      	//getWorkTaskExecutor獲取WorkManagerTaskExecutor對象,通過之前在Configuration創建的線程池中執行EnqueueRunnable任務
        mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);
        mOperation = runnable.getOperation();
    } else {
        Logger.get().warning(TAG,
                String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
    }
    return mOperation;
}

EnqueueRunnable的run方法:

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);
            //調度安排中
          	scheduleWorkInBackground();
        }
        mOperation.setState(Operation.SUCCESS);
    } catch (Throwable exception) {
        mOperation.setState(new Operation.State.FAILURE(exception));
    }
}

開始任務調度

/**
 * Schedules work on the background scheduler.
 */
@VisibleForTesting
public void scheduleWorkInBackground() {
    WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
  	//Schedulers類對安排的work進行選擇出最優執行方案
    Schedulers.schedule(
            workManager.getConfiguration(),
            workManager.getWorkDatabase(),
            workManager.getSchedulers());
}

Schedulers類是控制調度的一個類,查看他的Schedulers.schedule方法:

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;
		
  	//對需要進行調度執行的worker進行狀態更新,挑選出適合合乎規則的worker
    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();
    }
		
  //執行合乎規則的worker
    if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
        WorkSpec[] eligibleWorkSpecsArray = eligibleWorkSpecs.toArray(new WorkSpec[0]);
        // 調用初始化的scheduler
        for (Scheduler scheduler : schedulers) {
            scheduler.schedule(eligibleWorkSpecsArray);
        }
    }
}

從上面初始化的過程中,我們會創建兩個Scheduler:GreedyScheduler和SystemJobScheduler,再查看這兩個類中的schedule方法:

@VisibleForTesting
public void scheduleInternal(WorkSpec workSpec, int jobId) {
    //WorkSpec轉化爲JobInfo(JobInfo是與SystemJobService綁定的)
    JobInfo jobInfo = mSystemJobInfoConverter.convert(workSpec, jobId);
    try {
      	//SystemJobService開始執行任務
        mJobScheduler.schedule(jobInfo);
    } catch (IllegalStateException e) {
      
        // Rethrow a more verbose exception.
        throw new IllegalStateException(message, e);
    } catch (Throwable throwable) {
        // OEM implementation bugs in JobScheduler cause the app to crash. Avoid crashing.
        Logger.get().error(TAG, String.format("Unable to schedule %s", workSpec), throwable);
    }
}
@Override
public boolean onStartJob(@NonNull JobParameters params) {
    if (mWorkManagerImpl == null) {
        Logger.get().debug(TAG, "WorkManager is not initialized; requesting retry.");
        jobFinished(params, true);
        return false;
    }

    String workSpecId = getWorkSpecIdFromJobParameters(params);
    if (TextUtils.isEmpty(workSpecId)) {
        Logger.get().error(TAG, "WorkSpec id not found!");
        return false;
    }

    synchronized (mJobParameters) {
        if (mJobParameters.containsKey(workSpecId)) {
            // This condition may happen due to our workaround for an undesired behavior in API
            // 23.  See the documentation in {@link SystemJobScheduler#schedule}.
            Logger.get().debug(TAG, String.format(
                    "Job is already being executed by SystemJobService: %s", workSpecId));
            return false;
        }

        mJobParameters.put(workSpecId, params);
    }

    WorkerParameters.RuntimeExtras runtimeExtras = null;
    if (Build.VERSION.SDK_INT >= 24) {
        runtimeExtras = new WorkerParameters.RuntimeExtras();
        if (params.getTriggeredContentUris() != null) {
            runtimeExtras.triggeredContentUris =
                    Arrays.asList(params.getTriggeredContentUris());
        }
        if (params.getTriggeredContentAuthorities() != null) {
            runtimeExtras.triggeredContentAuthorities =
                    Arrays.asList(params.getTriggeredContentAuthorities());
        }
        if (Build.VERSION.SDK_INT >= 28) {
            runtimeExtras.network = params.getNetwork();
        }
    }

  	//回到WorkManagerImpl調用startWork方法
    mWorkManagerImpl.startWork(workSpecId, runtimeExtras);
    return true;
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public void startWork(
        @NonNull String workSpecId,
        @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
    mWorkTaskExecutor
            .executeOnBackgroundThread(
      							//分配到StartWorkRunnable執行
                    new StartWorkRunnable(this, workSpecId, runtimeExtras));
}

StartWorkRunnable是一個實現Runnable接口,run方法執行的是Processor類的startWork方法

@Override
public void run() {
    mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);
}
public boolean startWork(
        @NonNull String id,
        @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {

    WorkerWrapper workWrapper;
    synchronized (mLock) {
        if (mEnqueuedWorkMap.containsKey(id)) {
            return false;
        }
				//實例化了WorkWrapper
        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);
    }
  	//實例化的WorkWrapper加入了後臺線程池WorkTaskExecutor
    mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
    Logger.get().debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
    return true;
}

WorkWrapper也是一個實現Runnable接口的類,在其run方法調調用了Worker的startWork方法,至此整個流程結束:

@WorkerThread
@Override
public void run() {
    mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
    mWorkDescription = createWorkDescription(mTags);
    runWorker();
}

private void runWorker() {
   //..........省略n行代碼
        mWorkTaskExecutor.getMainThreadExecutor()
                .execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Logger.get().debug(TAG, String.format("Starting work for %s",
                                    mWorkSpec.workerClassName));
                          //調用Worker的startWork方法
                            mInnerFuture = mWorker.startWork();
                            future.setFuture(mInnerFuture);
                        } catch (Throwable e) {
                            future.setException(e);
                        }

                    }
                });

        
    } else {
        resolveIncorrectStatus();
    }
}
@WorkerThread
public abstract @NonNull Result doWork();

@Override
public final @NonNull ListenableFuture<Result> startWork() {
    mFuture = SettableFuture.create();
    getBackgroundExecutor().execute(new Runnable() {
        @Override
        public void run() {
            try {
              //調用doWork
                Result result = doWork();
                mFuture.set(result);
            } catch (Throwable throwable) {
                mFuture.setException(throwable);
            }

        }
    });
    return mFuture;
}

至此,我們從創建work 到把work添加到WorkRequest,在WorkManagerImpl實例化過程中,會創建完成數據庫WorkDatabase.create,線程池的創建workTaskExecutor,任務調度集合List,執行者Processor。enqueue()方法會創建創建的EnqueueRunnable,在EnqueueRunnable的run方法中進行了Worker的數據庫保存,然後執行scheduleWorkInBackground進行任務調度,當沒有約束條件或者當前情況已經滿足Worker執行,則會選擇GreedyScheduler.schedule調度立即執行,如果有延遲任務或者一次性任務執行中斷則會由SystemJobScheduler來執行,而SystemJobScheduler會把WorkSpec轉爲JobInfo,最終在SystemJobService中執行onStartJob方法,再調用WorkManagerImpl的startWork方法,startWork方法會在WorkRaskExcutor線程中添加實現了Runnable接口的StartWorkRunnable方法,StartWorkRunnable會接着調用Processor的額startWork方法,接着workWrapper實例化出來執行,最終在WorkWrapper中執行runWorker方法流程結束。

###那問題又來了,約束條件怎麼觸發,導致work的運行呢?

我們解壓生成apk包,打開AndroidManifest.xml文件會發現有很多的約束的廣播:


        <receiver
            android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy"
            android:enabled="false"
            android:exported="false"
            android:directBootAware="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:enabled="false"
            android:exported="false"
            android:directBootAware="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:enabled="false"
            android:exported="false"
            android:directBootAware="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
            android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$NetworkStateProxy"
            android:enabled="false"
            android:exported="false"
            android:directBootAware="false">

            <intent-filter>

                <action
                    android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            </intent-filter>
        </receiver>

        <receiver
            android:name="androidx.work.impl.background.systemalarm.RescheduleReceiver"
            android:enabled="false"
            android:exported="false"
            android:directBootAware="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:enabled="@ref/0x7f030003"
            android:exported="false"
            android:directBootAware="false">

            <intent-filter>

                <action
                    android:name="androidx.work.impl.background.systemalarm.UpdateProxies" />
            </intent-filter>
        </receiver>

        <service
            android:name="androidx.room.MultiInstanceInvalidationService"
            android:exported="false" />
    </application>
</manifest>

ConstraintProxy類是廣播,在收到通知的時候,會啓動SystemAlarmService這個服務,其中ACTION 爲ACTION_CONSTRAINTS_CHANGED

@Override
public void onReceive(Context context, Intent intent) {
    Logger.get().debug(TAG, String.format("onReceive : %s", intent));
    Intent constraintChangedIntent = CommandHandler.createConstraintsChangedIntent(context);
    context.startService(constraintChangedIntent);
}
static Intent createConstraintsChangedIntent(@NonNull Context context) {
    Intent intent = new Intent(context, SystemAlarmService.class);
    intent.setAction(ACTION_CONSTRAINTS_CHANGED);
    return intent;
}

SystemAlarmService類 內部調用mDispatcher.add方法

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);

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

SystemAlarmDispatcher類 內部調用processCommand方法

@MainThread
public boolean add(@NonNull final Intent intent, final int startId) {
    Logger.get().debug(TAG, String.format("Adding command %s (%s)", intent, startId));
    assertMainThread();
		//....省略n行代碼

    intent.putExtra(KEY_START_ID, startId);
    synchronized (mIntents) {
        boolean hasCommands = !mIntents.isEmpty();
        mIntents.add(intent);
        if (!hasCommands) {
           //執行
            processCommand();
        }
    }
    return true;
}
@MainThread
@SuppressWarnings("FutureReturnValueIgnored")
private void processCommand() {
    assertMainThread();
   //....省略n行代碼
    try {
        processCommandLock.acquire();
        // Process commands on the background thread.
        mWorkManager.getWorkTaskExecutor().executeOnBackgroundThread(new Runnable() {
            @Override
            public void run() {
               					//交由CommandHandler處理
                        mCommandHandler.onHandleIntent(mCurrentIntent, startId,
                                SystemAlarmDispatcher.this);
                    } catch (Throwable throwable) {
                       
                    }  finally {
                       
                        wakeLock.release();
                        // Check if we have processed all commands
                        postOnMainThread(
                                new DequeueAndCheckForCompletion(SystemAlarmDispatcher.this));
                    }
                }
            }
        });
    } finally {
        processCommandLock.release();
    }
}

進行資源的重新調度

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

onHandleIntent方法 onHandleIntent傳入的Action 是ACTION_CONSTRAINTS_CHANGED。然後執行handleConstraintsChanged方法,在該方法內部經過一系列轉化,會回到onHandleIntent方法中,而且ACTION爲ACTION_DELAY_MET。DelayMetCommandHandler 經過成成調用,會調用到DelayMetCommandHandler類onAllConstraintsMet方法。在該方法內部會調用startWork方法。而startWork方法正式Processor的方法。又回到了上面分析的正常work的工作流程了。

private void handleDelayMet(
        @NonNull Intent intent,
        int startId,
        @NonNull SystemAlarmDispatcher dispatcher) {

    Bundle extras = intent.getExtras();
    synchronized (mLock) {
        String workSpecId = extras.getString(KEY_WORKSPEC_ID);
        
        if (!mPendingDelayMet.containsKey(workSpecId)) {
            DelayMetCommandHandler delayMetCommandHandler =
                    new DelayMetCommandHandler(mContext, startId, workSpecId, dispatcher);
            mPendingDelayMet.put(workSpecId, delayMetCommandHandler);
          	//交由Process處理
            delayMetCommandHandler.handleProcessWork();
        } else {
           
        }
    }
}

實現原理就是通過監聽各種約束條件變化的廣播,然後經過層層轉化,最終的處理邏輯和無限制條件的work流程一致。

####完結!感謝觀看~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章