Jetpack系列-WorkManager使用和源碼分析

1 簡介

WorkManager是Jetpack組件庫中的一個組件导匣,主要用于處理立即執(zhí)行、長時間運行秦躯、可延遲執(zhí)行的并且保證必須執(zhí)行的后臺操作任務郑叠。例如上傳日志,定時上傳數據产禾,定時下載數據排作,定時備份數據等等。即使APP退出或者進程被殺亚情,任務依舊可以執(zhí)行妄痪,不過需要注意的是目前WorkManager在Google Pixel手機上可以完美使用,但是在其他品牌手機使用有一定的問題楞件。

WorkManager可處理的3種永久性工作:

  • 立即執(zhí)行:必須立即開始且很快就完成的任務衫生。
  • 長時間運行:運行時間可能較長(有可能超過10分鐘)的任務。
  • 可延期執(zhí)行:延期開始并且可以定期運行的預定任務土浸。
類型 周期 使用方式
立即 一次性 OneTimeWorkRequest 和 Worker罪针。如需處理加急工作,請對 OneTimeWorkRequest 調用 setExpedited()黄伊。
長期運行 一次性或定期 任意 WorkRequest 或 Worker泪酱。在工作器中調用 setForeground() 來處理通知。
可延期 一次性或定期 PeriodicWorkRequest 和 Worker。

Google Developer文檔:https://developer.android.google.cn/topic/libraries/architecture/workmanager

原理:

WorkManager引入后墓阀,使用Room將任務存入數據庫毡惜,然后使用系統(tǒng)服務區(qū)執(zhí)行這些任務,這樣就實現了即使APP集成被殺斯撮,APP被強制退出的時候经伙,任務依然能被執(zhí)行。同時WorkManager會在AndroidManifest.xml中注冊receiver和service吮成,用來接收系統(tǒng)系統(tǒng)的廣播橱乱,比如網絡狀態(tài)辜梳、低電量粱甫、低內存、手機空閑等作瞄,來執(zhí)行附帶約束的任務茶宵。

2 使用

2.1 添加依賴

dependencies {
    
    //WorkManager依賴
    def work_version = "2.7.1"
    implementation "androidx.work:work-runtime:$work_version"
    ...
        
}

示例代碼使用了ViewBinding,需要開啟ViewBinding宗挥。

android{
    ...
        //開啟ViewBinding
        viewBinding {
            enabled = true
        }
}

2.2 創(chuàng)建Activity

class MainActivity : AppCompatActivity() {
    
    private lateinit var binding: ActivityMainBinding
    
    @RequiresApi(Build.VERSION_CODES.M)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        initView()
    }
    
    @RequiresApi(Build.VERSION_CODES.M)
    private fun initView() {
        ...
    }
}

XML中有幾個按鈕測試對應的后臺任務乌庶。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
              tools:context=".MainActivity">
  
  <Button
          android:id="@+id/button1"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_margin="2dp"
          android:text="執(zhí)行單次后臺任務"
          android:textAllCaps="false" />
  
  <Button
          android:id="@+id/button2"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_margin="2dp"
          android:text="執(zhí)行單次后臺任務-發(fā)送數據"
          android:textAllCaps="false" />
  
  <Button
          android:id="@+id/button3"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_margin="2dp"
          android:text="執(zhí)行多個后臺任務-按順序"
          android:textAllCaps="false" />
  
  <Button
          android:id="@+id/button4"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_margin="2dp"
          android:text="執(zhí)行多個后臺任務-集合形式"
          android:textAllCaps="false" />
  
  <Button
          android:id="@+id/button5"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_margin="2dp"
          android:text="周期性重復執(zhí)行后臺任務"
          android:textAllCaps="false" />
  
  <Button
          android:id="@+id/button6"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_margin="2dp"
          android:text="附帶約束條件執(zhí)行后臺任務"
          android:textAllCaps="false" />
  
</LinearLayout>

2.3 功能測試

2.3.1 執(zhí)行單次后臺任務

創(chuàng)建SimpleWorker,繼承Worker契耿,實現dowWork()方法瞒大,dowWork()是一個異步方法,它是在Runnablerun方法中調用的搪桂。doWork()返回的Result會通知 WorkManager服務工作是否成功透敌,以及工作失敗時是否應重試工作。

Result有3種:

  • Result.success():工作成功完成踢械。
  • Result.failure():工作失敗酗电。
  • Result.retry():工作失敗,應根據其重試政策在其他時間嘗試内列。
class SimpleWorker(context: Context, workParams: WorkerParameters) :
Worker(context, workParams) {
    
    companion object {
        const val TAG = "---SimpleWorker"
    }
    
    /**
    *  doWork() 返回的 Result 會通知 WorkManager 服務工作是否成功撵术,以及工作失敗時是否應重試工作。
    *  Result.success():工作成功完成话瞧。
    *  Result.failure():工作失敗嫩与。
    *  Result.retry():工作失敗,應根據其重試政策在其他時間嘗試交排。
    *
    *  這是一個后臺任務蕴纳,是異步的,在Runnable中執(zhí)行个粱。
    */
    override fun doWork(): Result {
        Log.d(TAG, "doWork: work start...")
        
        try {
            Thread.sleep(3000)
        } catch (e: IOException) {
            e.printStackTrace()
            return Result.failure()
        } finally {
            Log.d(TAG, "doWork: work end...")
        }
        
        return Result.success()
    }
}

MainActivityinitView()方法中執(zhí)行該單次任務古毛。

步驟:

1、構建OneTimeWorkRequest

2稻薇、調用WorkManager.getInstance(this).enqueue()提交任務到隊列嫂冻。

//單次后臺任務
binding.button1.setOnClickListener {
    //構建WorkRequest
    val oneTimeWorkRequest =
    OneTimeWorkRequest.Builder(SimpleWorker::class.java).build()
    
    //調用enqueue()方法將WorkRequest提交到WorkManager任務隊列中
    WorkManager.getInstance(this).enqueue(oneTimeWorkRequest)
    
    //取消任務
    //取消所有任務
    WorkManager.getInstance(this).cancelAllWork()
    //取消指定任務
    WorkManager.getInstance(this).cancelWorkById(oneTimeWorkRequest.id)
}

2.3.2 執(zhí)行單次后臺任務-傳遞數據

WorkManager在執(zhí)行任務的時候,可以攜帶數據交給Worker塞椎,Worker中可以接收該數據桨仿,然后在doWork()的返回結果中也可以返回數據給WorkManagerWorkManager可以通過LiveData去接收返回的數據案狠。

創(chuàng)建SimpleDataWorker類:

class SimpleDataWorker(context: Context, private val workParams: WorkerParameters) :
Worker(context, workParams) {
    
    companion object {
        const val TAG = "---SimpleDataWorker"
    }
    
    override fun doWork(): Result {
        Log.d(TAG, "doWork: work start...")
        
        //接收MainActivity中傳過來的數據
        val inputData = workParams.inputData.getString("inputData")
        Log.d(TAG, "doWork: receive data:$inputData")
        
        //將數據再返回給MainActivity
        val outputData = Data.Builder().putString("outputData", "Tree New Bee").build()
        return Result.success(outputData)
    }
}

MainActivityinitView()方法中執(zhí)行該任務服傍。

步驟:

1、使用Data.Builder()構造要傳遞的數據體骂铁。

2吹零、構造OneTimeWorkRequest

3拉庵、用WorkManager.getInstance(this).getWorkInfoByIdLiveData(oneTimeWorkRequest.id).observe(this){}來獲取LiveData去監(jiān)聽狀態(tài)和數據變化灿椅。

4、調用WorkManager.getInstance(this).enqueue()提交任務到隊列钞支。

//執(zhí)行單次后臺任務-傳遞數據
binding.button2.setOnClickListener {
    //要發(fā)傳遞出去的數據
    val inputData = Data.Builder().putString("inputData", "New Bee").build()
    
    //構建WorkRequest
    val oneTimeWorkRequest = OneTimeWorkRequest.Builder(SimpleDataWorker::class.java)
        .setInputData(inputData).build()//設置要傳遞的數據
        
    //通過LiveData接收BackgroundWorker2返回的數據
    WorkManager.getInstance(this).getWorkInfoByIdLiveData(oneTimeWorkRequest.id)
        .observe(this) {
            
            //RUNNING時茫蛹,outputData為null;SUCCEEDED時烁挟,才能獲取到數據婴洼。
            Log.d(
                SimpleDataWorker.TAG,
                "state 1: ${it.state}  outputData:${it.outputData.getString("outputData")}"
            )
            
            //當狀態(tài)處于完成時,也就是SUCCEEDED撼嗓、FAILED柬采、CANCELLED時,任務才結束静稻,才能去拿結果數據警没,不然獲取到的數據是null
            if (it.state.isFinished) {
                Log.d(
                    SimpleDataWorker.TAG,
                    "state 2: ${it.state}  outputData:${it.outputData.getString("outputData")}"
                )
            }
        }
        
    //提交任務,任務加入隊列
    WorkManager.getInstance(this).enqueue(oneTimeWorkRequest)
}

getWorkInfoByIdLiveData方法會回調一個WorkInfo對象振湾,WorkInfo中包含狀態(tài)和Worker返回的數據杀迹。

只要當狀態(tài)是SUCCEEDEDFAILED押搪、CANCELLED這3個的時候树酪,才表示任務結束,這時候才能去獲取數據大州,其他狀態(tài)獲取到的數據是null续语。

2.3.3 執(zhí)行多個后臺任務-按順序

先創(chuàng)建3個任務:

class OrderWorker1(context: Context, workParams: WorkerParameters) :
Worker(context, workParams) {
    
    companion object {
        private const val TAG = "---OrderWorker1"
    }
    
    override fun doWork(): Result {
        Log.d(TAG, "doWork: start...")
        return Result.success()
    }
}
class OrderWorker2(context: Context, workParams: WorkerParameters) :
Worker(context, workParams) {
    
    companion object {
        private const val TAG = "---OrderWorker2"
    }
    
    override fun doWork(): Result {
        Log.d(TAG, "doWork: start...")
        return Result.success()
    }
}
class OrderWorker3(context: Context, workParams: WorkerParameters) :
Worker(context, workParams) {
    
    companion object {
        private const val TAG = "---OrderWorker3"
    }
    
    override fun doWork(): Result {
        Log.d(TAG, "doWork: start...")
        return Result.success()
    }
}

然后按順序執(zhí)行這3個任務,核心方法是beginWith()then()厦画,beginWith()可以做一些前置工作疮茄,比如初始化等等滥朱,then()中可以做一些后續(xù)工作。只有當前一個任務執(zhí)行成功才會執(zhí)行下一個任務力试。

//執(zhí)行多個后臺任務-按順序
binding.button3.setOnClickListener {
    val oneTimeWorkRequest1 =
    OneTimeWorkRequest.Builder(OrderWorker1::class.java).build()
    val oneTimeWorkRequest2 =
    OneTimeWorkRequest.Builder(OrderWorker2::class.java).build()
    val oneTimeWorkRequest3 =
    OneTimeWorkRequest.Builder(OrderWorker3::class.java).build()
    
    //按順序執(zhí)行1,2,3
    WorkManager.getInstance(this)
        .beginWith(oneTimeWorkRequest1)
        .then(oneTimeWorkRequest2)
        .then(oneTimeWorkRequest3)
        .enqueue()
}

2.3.4 執(zhí)行多個后臺任務-集合方式

集合方式和2.3.3中按順序執(zhí)行沒有什么大的區(qū)別徙邻,只不過是把多個任務放在集合中,然后再交給WorkManager畸裳,同樣可以控制順序缰犁。

//執(zhí)行多個后臺任務-集合方式
binding.button4.setOnClickListener {
    val oneTimeWorkRequest1 =
    OneTimeWorkRequest.Builder(OrderWorker1::class.java).build()
    val oneTimeWorkRequest2 =
    OneTimeWorkRequest.Builder(OrderWorker2::class.java).build()
    val oneTimeWorkRequest3 =
    OneTimeWorkRequest.Builder(OrderWorker3::class.java).build()
    
    val oneTimeWorkRequests = mutableListOf<OneTimeWorkRequest>()
    oneTimeWorkRequests.add(oneTimeWorkRequest2)
    oneTimeWorkRequests.add(oneTimeWorkRequest3)
    
    //先執(zhí)行2,3怖糊,再執(zhí)行1
    WorkManager.getInstance(this).beginWith(oneTimeWorkRequests)
        .then(oneTimeWorkRequest1)
        .enqueue()
}

2.3.5 周期性重復執(zhí)行后臺任務

周期性重復執(zhí)行任務就可以實現類似日志上傳的功能帅容,WorkManager要求的任務重復周期不能小于15分鐘。

周期任務的WorkRequest對象是PeriodicWorkRequest伍伤。

//周期性重復執(zhí)行后臺任務
binding.button5.setOnClickListener {
    //重復周期最少設置15分鐘并徘,少于15分鐘編譯器會報錯
    val periodicWorkRequest =
    PeriodicWorkRequest.Builder(SimpleWorker::class.java, 15, TimeUnit.MINUTES)
        .build()
        
    //監(jiān)聽狀態(tài)和數據變化
    WorkManager.getInstance(this).getWorkInfoByIdLiveData(periodicWorkRequest.id)
        .observe(this) {
            Log.d(SimpleWorker.TAG, "state: ${it.state}")
            
            if (it.state.isFinished) {
                Log.d(SimpleWorker.TAG, "work finished...")
            }
        }
        
    //提交任務,任務加入隊列
    WorkManager.getInstance(this).enqueue(periodicWorkRequest)
}

2.3.6 附帶約束條件執(zhí)行后臺任務

附帶約束條件的任務是為了對APP性能進行優(yōu)化嚷缭,比如該任務只在聯網的時候才能進行饮亏,只有在充電的時候才能進行耍贾,只有在空閑時期(沒有其他大量服務在運行)的時候才能進行阅爽,這樣就能降低APP功耗,充分利用手機性能荐开。

//附帶約束條件執(zhí)行后臺任務
binding.button6.setOnClickListener {
    
    //約束條件
    val constraints = Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)//聯網中
        .setRequiresCharging(true)//充電中
        .setRequiresBatteryNotLow(true)//非低電量
    //                .setRequiresDeviceIdle(true)//手機空閑中
        .setRequiresStorageNotLow(true)//非低內存
        .build()
        
    val oneTimeWorkRequest = OneTimeWorkRequest.Builder(SimpleWorker::class.java)
        .setConstraints(constraints)//設置約束
        .build()
        
    //提交任務付翁,任務加入隊列
    WorkManager.getInstance(this).enqueue(oneTimeWorkRequest)
}

完整源碼可以在公號【木水Code】發(fā)送"WorkManager"進行下載。

3 源碼流程圖

源碼流程分析主要分三部分:

  • WorkManager初始化過程
  • WorkManager執(zhí)行無約束條件的任務
  • WorkManager執(zhí)行有約束條件的任務

閱讀源碼的過程中結合該流程圖晃听,能更加快速理解百侧,加深記憶。

4 源碼分析

4.1 WorkManager初始化

MainActivity中能扒,調用了WorkManager.getInstance(this)佣渴。

public static @NonNull WorkManager getInstance(@NonNull Context context) {
    return WorkManagerImpl.getInstance(context);
}

然后又調用了WorkManager的子類WorkManagerImpl.getInstance()方法去初始化。

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

WorkManagerImpl.getInstance()又調用了WorkManagerImpl.initialize()去實例化WorkManagerImpl初斑。

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

但是這里并不是第一次初始化辛润。

反編譯或者直接用AndroidStudio打開APK,點開AndroidManifest.xml文件见秤,能看到引入WorkManager之后生成了一個provider砂竖,在這個InitializationProvider中,有一個WorkManagerInitializer鹃答。

<provider
          android:name="androidx.startup.InitializationProvider"
          android:exported="false"
          android:authorities="cn.zhangmushui.workmanagersample.androidx-startup">
  
  <meta-data
             android:name="androidx.work.WorkManagerInitializer"
             android:value="androidx.startup" />
</provider>

進入WorkManagerInitializer

public final class WorkManagerInitializer implements Initializer<WorkManager> {
    
    private static final String TAG = Logger.tagWithPrefix("WrkMgrInitializer");
    
    @NonNull
    @Override
    public WorkManager create(@NonNull Context context) {
        // Initialize WorkManager with the default configuration.
        Logger.get().debug(TAG, "Initializing WorkManager with default configuration.");
        //WorkManager的初始化工作
        WorkManager.initialize(context, new Configuration.Builder().build());
        return WorkManager.getInstance(context);
    }
    
    @NonNull
    @Override
    public List<Class<? extends androidx.startup.Initializer<?>>> dependencies() {
        return Collections.emptyList();
    }
}

然后調用了WorkManager.initialize()乎澄。

public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
    WorkManagerImpl.initialize(context, configuration);
}

WorkManager.initialize()調用了WorkManagerImpl.initialize(),真正的初始化工作就在這里進行测摔。最終和MainActivity中調用WorkManager.getInstance(this)一樣置济,走到了WorkManagerImpl.initialize()。所以,AndroidManifest.xml中的provider才是WorkManager的第一次實例化浙于。

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

WorkManager初始化的時候會傳入一個默認Configuration修噪,Configuration中包含了任務調度器,任務執(zhí)行器等對象路媚。

initialize方法中黄琼,實例化了一個WorkManagerTaskExecutor傳入WorkManagerImpl的構造方法,用來執(zhí)行任務整慎。

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public WorkManagerImpl(
    @NonNull Context context,
    @NonNull Configuration configuration,
    @NonNull TaskExecutor workTaskExecutor) {
    this(context,
         configuration,
         workTaskExecutor,
         context.getResources().getBoolean(R.bool.workmanager_test_configuration));
}
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public WorkManagerImpl(
    @NonNull Context context,
    @NonNull Configuration configuration,
    @NonNull TaskExecutor workTaskExecutor,
    boolean useTestDatabase) {
    this(context,
         configuration,
         workTaskExecutor,
         WorkDatabase.create(
             context.getApplicationContext(),
             workTaskExecutor.getBackgroundExecutor(),
             useTestDatabase)
        );
}
@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()));
    //創(chuàng)建調度器Scheduler
    List<Scheduler> schedulers =
        createSchedulers(applicationContext, configuration, workTaskExecutor);
    //實例化處理器Processor脏款,處理器可以根據需要智能的去調度和執(zhí)行任務。
    Processor processor = new Processor(
        context,
        configuration,
        workTaskExecutor,
        database,
        schedulers);
    //真正的初始化
    internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
}

在最后一個調用的構造方法中裤园,首先創(chuàng)建了一個調度器Scheduler用于任務調度:

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@NonNull
public List<Scheduler> createSchedulers(
    @NonNull Context context,
    @NonNull Configuration configuration,
    @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, configuration, taskExecutor, this));
}

接著實例化了一個Processor撤师,處理器可以根據需要智能的去調度和執(zhí)行任務。

Processor processor = new Processor(
    context,
    configuration,
    workTaskExecutor,
    database,
    schedulers);

最后調用internalInit進行真正的初始化工作拧揽。

private void internalInit(@NonNull Context context,
                          @NonNull Configuration configuration,
                          @NonNull TaskExecutor workTaskExecutor,
                          @NonNull WorkDatabase workDatabase,
                          @NonNull List<Scheduler> schedulers,
                          @NonNull Processor processor) {
    
    context = context.getApplicationContext();
    mContext = context;
    mConfiguration = configuration;
    mWorkTaskExecutor = workTaskExecutor;
    mWorkDatabase = workDatabase;
    mSchedulers = schedulers;
    mProcessor = processor;
    mPreferenceUtils = new PreferenceUtils(workDatabase);
    mForceStopRunnableCompleted = false;
    
    // Check for direct boot mode
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && context.isDeviceProtectedStorage()) {
        throw new IllegalStateException("Cannot initialize WorkManager in direct boot mode");
    }
    
    // Checks for app force stops.
    //檢查app強制停止
    mWorkTaskExecutor.executeOnBackgroundThread(new ForceStopRunnable(context, this));
}

executeOnBackgroundThread方法傳入了一個ForceStopRunnable對象剃盾,在這里,檢查到app正在執(zhí)行任務的時候淤袜,發(fā)生了閃退崩潰退出了程序痒谴,或者手機關機,就會重試之前的任務铡羡。

WorkManager初始化的過程中积蔚,ForceStopRunnablerun方法中,會檢測app被強制停止運行之前是否還有未完成的任務烦周,如果有尽爆,會繼續(xù)執(zhí)行。

@Override
public void run() {
    try {
        if (!multiProcessChecks()) {
            return;
        }
        while (true) {
            // Migrate the database to the no-backup directory if necessary.
            WorkDatabasePathHelper.migrateDatabase(mContext);
            // Clean invalid jobs attributed to WorkManager, and Workers that might have been
            // interrupted because the application crashed (RUNNING state).
            Logger.get().debug(TAG, "Performing cleanup operations.");
            try {
                forceStopRunnable();
                break;
            } catch (SQLiteCantOpenDatabaseException
                     | SQLiteDatabaseCorruptException
                     | SQLiteDatabaseLockedException
                     | SQLiteTableLockedException
                     | SQLiteConstraintException
                     | SQLiteAccessPermException exception) {
                mRetryCount++;
                if (mRetryCount >= MAX_ATTEMPTS) {
                    // ForceStopRunnable is usually the first thing that accesses a database
                    // (or an app's internal data directory). This means that weird
                    // PackageManager bugs are attributed to ForceStopRunnable, which is
                    // unfortunate. This gives the developer a better error
                    // message.
                    String message = "The file system on the device is in a bad state. "
                        + "WorkManager cannot access the app's internal data store.";
                    Logger.get().error(TAG, message, exception);
                    IllegalStateException throwable = new IllegalStateException(message,
                                                                                exception);
                    InitializationExceptionHandler exceptionHandler =
                        mWorkManager.getConfiguration().getExceptionHandler();
                    if (exceptionHandler != null) {
                        Logger.get().debug(TAG,
                                           "Routing exception to the specified exception handler",
                                           throwable);
                        exceptionHandler.handleException(throwable);
                        break;
                    } else {
                        throw throwable;
                    }
                } else {
                    long duration = mRetryCount * BACKOFF_DURATION_MS;
                    Logger.get()
                        .debug(TAG, String.format("Retrying after %s", duration),
                               exception);
                    sleep(mRetryCount * BACKOFF_DURATION_MS);
                }
            }
        }
    } finally {
        mWorkManager.onForceStopRunnableCompleted();
    }
}

WorkManager初始化過程總結:

  • WorkManager的真正初始化是由ContentProvider提供的WorkManagerInitializer中執(zhí)行的读慎,最終的實現是在WorkManager的子類WorkManagerImpl中漱贱。
  • 初始化的過程中實例化了Configuration、WorkManagerTaskExecutor夭委、WorkDatabase幅狮、GreedyScheduler、Processor闰靴。
  • 會檢測APP發(fā)生強制退出之前是否有未完成的任務彪笼,如果有,會繼續(xù)執(zhí)行蚂且。

4.2 WorkManager無約束條件任務的執(zhí)行

MainActivity中配猫,調用了enqueue()

WorkManager.getInstance(this).enqueue()

進入WorkManager:

@NonNull
public final Operation enqueue(@NonNull WorkRequest workRequest) {
    return enqueue(Collections.singletonList(workRequest));
}

調用WorkManager的抽象方法enqueue

@NonNull
public abstract Operation enqueue(@NonNull List<? extends WorkRequest> requests);

該抽象方法在WorkManagerImpl中實現:

public Operation enqueue(
    @NonNull List<? extends WorkRequest> requests) {
    
    // 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 (requests.isEmpty()) {
        throw new IllegalArgumentException(
            "enqueue needs at least one WorkRequest.");
    }
    return new WorkContinuationImpl(this, requests).enqueue();
}

實例化了一個WorkContinuationImpl對象,然后調用WorkContinuationImplenqueue方法杏死。WorkContinuationImpl繼承了WorkContinuation

public WorkContinuationImpl(
    @NonNull WorkManagerImpl workManagerImpl,
    @NonNull List<? extends WorkRequest> work) {
    this(
        workManagerImpl,
        null,
        ExistingWorkPolicy.KEEP,
        work,
        null);
}

WorkContinuation保存了任務相關的一些信息:

public WorkContinuationImpl(@NonNull WorkManagerImpl workManagerImpl,
                            @Nullable String name,
                            @NonNull ExistingWorkPolicy existingWorkPolicy,
                            @NonNull List<? extends WorkRequest> work,
                            @Nullable List<WorkContinuationImpl> parents) {
    mWorkManagerImpl = workManagerImpl;
    mName = name;
    mExistingWorkPolicy = existingWorkPolicy;
    mWork = work;
    mParents = parents;
    mIds = new ArrayList<>(mWork.size());
    mAllIds = new ArrayList<>();
    if (parents != null) {
        for (WorkContinuationImpl parent : parents) {
            mAllIds.addAll(parent.mAllIds);
        }
    }
    for (int i = 0; i < work.size(); i++) {
        String id = work.get(i).getStringId();
        mIds.add(id);
        mAllIds.add(id);
    }
}

WorkContinuationImplenqueue方法中泵肄,WorkManagerImpl中的TaskExecutor執(zhí)行了EnqueueRunnable

@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 = new EnqueueRunnable(this);
        //TaskExecutor執(zhí)行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()方法如下所示捆交,調用了scheduleWorkInBackground(),這是后臺執(zhí)行任務的核心方法:

@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);
            //在后臺執(zhí)行任務
            scheduleWorkInBackground();
        }
        mOperation.setState(Operation.SUCCESS);
    } catch (Throwable exception) {
        mOperation.setState(new Operation.State.FAILURE(exception));
    }
}

scheduleWorkInBackground()方法如下所示:

@VisibleForTesting
public void scheduleWorkInBackground() {
    WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
    //調用Schedulers.schedule()腐巢,傳入了Configuration品追、WorkDatabase、Scheduler
    Schedulers.schedule(
        workManager.getConfiguration(),
        workManager.getWorkDatabase(),
        workManager.getSchedulers());
}

調用Schedulers.schedule()冯丙,傳入了Configuration肉瓦、WorkDatabaseScheduler

public static void schedule(
    ...
    if (eligibleWorkSpecsForLimitedSlots != null
        && eligibleWorkSpecsForLimitedSlots.size() > 0) {
        
        WorkSpec[] eligibleWorkSpecsArray =
            new WorkSpec[eligibleWorkSpecsForLimitedSlots.size()];
        eligibleWorkSpecsArray =
            eligibleWorkSpecsForLimitedSlots.toArray(eligibleWorkSpecsArray);
        
        //遍歷每個任務進行處理胃惜,由于這里執(zhí)行的是沒有限制條件的任務
        //所以最終調用的是貪婪調度器GreedyScheduler的schedule方法
        for (Scheduler scheduler : schedulers) {
            if (scheduler.hasLimitedSchedulingSlots()) {
                scheduler.schedule(eligibleWorkSpecsArray);
            }
        }
    }
    ...
}

遍歷每個任務去執(zhí)行泞莉,沒有約束條件的任務最終是調用了貪婪調度器GreedyScheduler.schedule()執(zhí)行:

@Override
public void schedule(@NonNull WorkSpec... workSpecs) {
    ...
    Set<WorkSpec> constrainedWorkSpecs = new HashSet<>();
    Set<String> constrainedWorkSpecIds = new HashSet<>();
    
    for (WorkSpec workSpec : workSpecs) {
        long nextRunTime = workSpec.calculateNextRunTime();
        long now = System.currentTimeMillis();
        if (workSpec.state == WorkInfo.State.ENQUEUED) {
            if (now < nextRunTime) {
                // Future work
                if (mDelayedWorkTracker != null) {
                    mDelayedWorkTracker.schedule(workSpec);
                }
            } else if (workSpec.hasConstraints()) {
                if (SDK_INT >= 23 && workSpec.constraints.requiresDeviceIdle()) {
                    // Ignore requests that have an idle mode constraint.
                    Logger.get().debug(TAG,
                                       String.format("Ignoring WorkSpec %s, Requires device idle.",
                                                     workSpec));
                } else if (SDK_INT >= 24 && workSpec.constraints.hasContentUriTriggers()) {
                    // Ignore requests that have content uri triggers.
                    Logger.get().debug(TAG,
                                       String.format("Ignoring WorkSpec %s, Requires ContentUri triggers.",
                                                     workSpec));
                } else {
                    constrainedWorkSpecs.add(workSpec);
                    constrainedWorkSpecIds.add(workSpec.id);
                }
            } else {
                Logger.get().debug(TAG, String.format("Starting work for %s", workSpec.id));
                //無約束條件會直接走這里
                mWorkManagerImpl.startWork(workSpec.id);
            }
        }
    }
    ...
}

沒有約束條件,直接走到了WorkManagerImpl.startWork()

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

進入StartWorkRunnable中:

@Override
public void run() {
    //使用Processor去執(zhí)行任務
    mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);
}

這里使用Processor去執(zhí)行任務船殉,調用了Processor.startWork()

public boolean startWork(
    @NonNull String id,
    @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
    
    WorkerWrapper workWrapper;
    synchronized (mLock) {
        ...
        //創(chuàng)建了一個WorkerWrapper鲫趁,WorkerWrapper是Runnable的包裝類
        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);
    }
    //WorkerWrapper交給WorkTaskExecutor去處理
    mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
    Logger.get().debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
    return true;
}

進入WorkerWrapperrun方法:

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

接著調用了WorkerWrapperrunWorker()方法:

private void runWorker() {
    ...
    if (trySetRunning()) {
        if (tryCheckForInterruptionAndResolve()) {
            return;
        }
        
        final SettableFuture<ListenableWorker.Result> future = SettableFuture.create();
        final WorkForegroundRunnable foregroundRunnable =
            new WorkForegroundRunnable(
            mAppContext,
            mWorkSpec,
            mWorker,
            params.getForegroundUpdater(),
            mWorkTaskExecutor
        );
        mWorkTaskExecutor.getMainThreadExecutor().execute(foregroundRunnable);
        
        final ListenableFuture<Void> runExpedited = foregroundRunnable.getFuture();
        runExpedited.addListener(new Runnable() {
            @Override
            public void run() {
                try {
                    runExpedited.get();
                    Logger.get().debug(TAG,
                                       String.format("Starting work for %s", mWorkSpec.workerClassName));
                    //這里是開始任務的核心方法
                    mInnerFuture = mWorker.startWork();
                    future.setFuture(mInnerFuture);
                } catch (Throwable e) {
                    future.setException(e);
                }
            }
        }, mWorkTaskExecutor.getMainThreadExecutor());
    }
    ...
}

調用mWorker.startWork()來到了ListenableFuturestartWork()

@MainThread
public abstract @NonNull ListenableFuture<Result> startWork();

具體實現在Worker類中:

public abstract class Worker extends ListenableWorker {

    SettableFuture<Result> mFuture;

    @Keep
    @SuppressLint("BanKeepAnnotation")
    public Worker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @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 {
                    Result result = doWork();
                    mFuture.set(result);
                } catch (Throwable throwable) {
                    mFuture.setException(throwable);
                }

            }
        });
        return mFuture;
    }
}

而我們自定義的SimpleWorker等類,繼承的就是這個Worker類利虫,這樣就完成了無約束條件任務的執(zhí)行挨厚。

class SimpleWorker(context: Context, workParams: WorkerParameters) :
Worker(context, workParams) {
    
    companion object {
        const val TAG = "---SimpleWorker"
    }
    
    override fun doWork(): Result {
        Log.d(TAG, "doWork: work start...")
        
        try {
            Thread.sleep(3000)
        } catch (e: IOException) {
            e.printStackTrace()
            return Result.failure()
        } finally {
            Log.d(TAG, "doWork: work end...")
        }
        
        return Result.success()
    }
}

WorkManager無約束條件任務執(zhí)行總結:

  1. WorkManager執(zhí)行了enqueue()后,創(chuàng)建WorkContinuationImpl對象執(zhí)行
    enqueue()方法糠惫。
  2. WorkContinuationImpl持有的EnqueueRunnable對象將任務添加到db疫剃,
    并交給Schedulers去調度。
  3. Schedulers將任務交給每一個Scheduler去處理寞钥,GreedyScheduler會先處
    理這個任務慌申。
  4. GreedyScheduler經過一系列判斷后陌选,調用WorkManager的startWork()方
    法執(zhí)行這種一次性理郑,非延遲,無約束的任務咨油。
  5. WorkManager持有的StartWorkRunnable對象會將任務交給Processor去
    處理您炉,執(zhí)行startWork()方法。
  6. Processor創(chuàng)建一個WorkerWrapper對象役电,由它去調用Worker的
    startWork()方法赚爵。

4.3 WorkManager有約束條件任務的執(zhí)行

反編譯或者直接用AndroidStudio打開APK,點開AndroidManifest.xml文件法瑟,看到添加了多個receiver冀膝,每個receiver對應一個約束條件,比如網絡狀態(tài):

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

點進去霎挟,來到了ConstraintProxy窝剖,它繼承了BroadcastReceiver,所以在網絡變化酥夭、低電量赐纱、低內存脊奋、充電的時候能接收到系統(tǒng)廣播,然后在onReceive進行處理:

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));
        //創(chuàng)建并啟動服務
        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 {
    }
    
    /**
    * Enables/Disables proxies based on constraints in {@link WorkSpec}s
    *
    * @param context   {@link Context}
    * @param workSpecs list of {@link WorkSpec}s to update proxies against
    */
    static void updateAll(Context context, List<WorkSpec> workSpecs) {
        boolean batteryNotLowProxyEnabled = false;
        boolean batteryChargingProxyEnabled = false;
        boolean storageNotLowProxyEnabled = false;
        boolean networkStateProxyEnabled = false;
        
        for (WorkSpec workSpec : workSpecs) {
            Constraints constraints = workSpec.constraints;
            batteryNotLowProxyEnabled |= constraints.requiresBatteryNotLow();
            batteryChargingProxyEnabled |= constraints.requiresCharging();
            storageNotLowProxyEnabled |= constraints.requiresStorageNotLow();
            networkStateProxyEnabled |=
                constraints.getRequiredNetworkType() != NOT_REQUIRED;
            
            if (batteryNotLowProxyEnabled && batteryChargingProxyEnabled
                && storageNotLowProxyEnabled && networkStateProxyEnabled) {
                break;
            }
        }
        
        Intent updateProxyIntent =
            ConstraintProxyUpdateReceiver.newConstraintProxyUpdateIntent(
            context,
            batteryNotLowProxyEnabled,
            batteryChargingProxyEnabled,
            storageNotLowProxyEnabled,
            networkStateProxyEnabled);
        
        // ConstraintProxies are being updated via a separate broadcast receiver.
        // For more information on why we do this look at b/73549299
        context.sendBroadcast(updateProxyIntent);
    }
}

點擊進入CommandHandler.createConstraintsChangedIntent()方法疙描,這里啟動了一個SystemAlarmService服務:

static Intent createConstraintsChangedIntent(@NonNull Context context) {
    Intent intent = new Intent(context, SystemAlarmService.class);
    intent.setAction(ACTION_CONSTRAINTS_CHANGED);
    return intent;
}

再進入SystemAlarmService诚隙,啟動服務后,在onStartCommand中調用SystemAlarmDispatcher.add()

@Override
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) {
        //調用SystemAlarmDispatcher.add()
        mDispatcher.add(intent, startId);
    }
    
    // If the service were to crash, we want all unacknowledged Intents to get redelivered.
    return Service.START_REDELIVER_INTENT;
}

然后在SystemAlarmDispatcheradd方法中調用了processCommand()

@MainThread
public boolean add(@NonNull final Intent intent, final int startId) {
    ...
    intent.putExtra(KEY_START_ID, startId);
    synchronized (mIntents) {
        boolean hasCommands = !mIntents.isEmpty();
        mIntents.add(intent);
        if (!hasCommands) {
            //執(zhí)行命令
            processCommand();
        }
    }
    return true;
}

SystemAlarmDispatcher.processCommand()中起胰,調用了CommandHandler.onHandleIntent()去處理Intent:

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) {
                    mCurrentIntent = mIntents.get(0);
                }
                
                if (mCurrentIntent != null) {
                    final String action = mCurrentIntent.getAction();
                    final int startId = mCurrentIntent.getIntExtra(KEY_START_ID,
                                                                   DEFAULT_START_ID);
                    ...
                    final PowerManager.WakeLock wakeLock = WakeLocks.newWakeLock(
                        mContext,
                        String.format("%s (%s)", action, startId));
                    try {
                        ...                        
                        wakeLock.acquire();
                        //這里是核心方法久又,去處理Intent
                        mCommandHandler.onHandleIntent(mCurrentIntent, startId,
                                                       SystemAlarmDispatcher.this);
                    } catch (Throwable throwable) {
                       ...
                    }  finally {
                       ...
                    }
                }
            }
        });
    } finally {
        processCommandLock.release();
    }
}

CommandHandler.onHandleIntent()會對約束條件的改變作出處理:

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

CommandHandler.handleConstraintsChanged()方法中實例化了一個ConstraintsCommandHandler,調用了ConstraintsCommandHandler.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);
    changedCommandHandler.handleConstraintsChanged();
}

handleConstraintsChanged()中效五,創(chuàng)建了一個DelayMetIntent交給AddRunnable籽孙,然后調用SystemAlarmDispatcher發(fā)送出去:

@WorkerThread
void handleConstraintsChanged() {
    ...
    for (WorkSpec workSpec : eligibleWorkSpecs) {
        String workSpecId = workSpec.id;
        //創(chuàng)建了一個DelayMetIntent,交給AddRunnable火俄,然后SystemAlarmDispatcher發(fā)送出去
        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(
            new SystemAlarmDispatcher.AddRunnable(mDispatcher, intent, mStartId));
    }
    ...
}

然后AddRunnablerun方法中犯建,又調用了SystemAlarmDispatcher.add()

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

SystemAlarmDispatcher.add()中執(zhí)行processCommand(),然后又到了CommandHandler.onHandleIntent()瓜客,而由于這次的Intent是DelayMetIntent适瓦,所以這次走的是handleDelayMet

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

CommandHandler.handleDelayMet()中谱仪,實例化DelayMetCommandHandler

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);
            delayMetCommandHandler.handleProcessWork();
        } else {
            Logger.get().debug(TAG,
                               String.format("WorkSpec %s is already being handled for ACTION_DELAY_MET",
                                             workSpecId));
        }
    }
}

DelayMetCommandHandler中玻熙,會執(zhí)行onAllConstraintsMet方法,onAllConstraintsMet中會從SystemAlarmDispatcher中獲取Processor去開始執(zhí)行任務疯攒。后續(xù)邏輯就和4.2中StartWorkRunnable中的邏輯一樣了嗦随。

@Override
public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
    ...
    synchronized (mLock) {
        if (mCurrentState == STATE_INITIAL) {
            mCurrentState = STATE_START_REQUESTED;
            
            //通過SystemAlarmDispatcher獲取Processor,調用startWork
            boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);
            
            ...
        } else {
            Logger.get().debug(TAG, String.format("Already started work for %s", mWorkSpecId));
        }
    }
}

關注木水小站 (zhangmushui.cn)和微信公眾號【木水Code】敬尺,及時獲取更多最新技術干貨枚尼。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市砂吞,隨后出現的幾起案子署恍,更是在濱河造成了極大的恐慌,老刑警劉巖蜻直,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盯质,死亡現場離奇詭異,居然都是意外死亡概而,警方通過查閱死者的電腦和手機呼巷,發(fā)現死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赎瑰,“玉大人王悍,你說我怎么就攤上這事∠绶叮” “怎么了配名?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵啤咽,是天一觀的道長。 經常有香客問我渠脉,道長宇整,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任芋膘,我火速辦了婚禮鳞青,結果婚禮上,老公的妹妹穿的比我還像新娘为朋。我一直安慰自己臂拓,他們只是感情好,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布习寸。 她就那樣靜靜地躺著胶惰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪霞溪。 梳的紋絲不亂的頭發(fā)上孵滞,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音鸯匹,去河邊找鬼坊饶。 笑死,一個胖子當著我的面吹牛殴蓬,可吹牛的內容都是我干的匿级。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼染厅,長吁一口氣:“原來是場噩夢啊……” “哼痘绎!你這毒婦竟也來了?” 一聲冷哼從身側響起糟秘,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤简逮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后尿赚,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡蕉堰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年凌净,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屋讶。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡冰寻,死狀恐怖,靈堂內的尸體忽然破棺而出皿渗,到底是詐尸還是另有隱情斩芭,我是刑警寧澤轻腺,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站划乖,受9級特大地震影響贬养,放射性物質發(fā)生泄漏。R本人自食惡果不足惜琴庵,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一误算、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧迷殿,春花似錦儿礼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至懦尝,卻和暖如春这橙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背导披。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工屈扎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人撩匕。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓鹰晨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親止毕。 傳聞我的和親對象是個殘疾皇子模蜡,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內容