JetPack源码分析之WorkManager原理(一)

95 阅读3分钟

WorkManager 是jetpack 家族中非常重要的一环

针对 WorkManager 所使用的场景有一下几种 1: 立即执行 ,可以使用加急策

2: 周期性任务

3:各种约束条件性的任务

下面写一个比较简单的任务,我们来看一下他的使用


        // 创建约束条件
        val constraints=Constraints.Builder()
//            .setRequiresDeviceIdle(true)//空闲
//            .setRequiredNetworkType(NetworkType.CONNECTED)// 链接网络
//            .setRequiresBatteryNotLow(true)//不是低电量
//            .setRequiresCharging(true)//是否充电
//            .setRequiresStorageNotLow(true)//磁盘空间是否充足
            .build()

        // 为任务设置参数
        var dataInput= Data.Builder().putString("tsm","1111").build();


        // 创建任务  单次任务,  PeriodicWorkRequestBuilder 为周期性任务
        var oneTimeWorkRequest= PeriodicWorkRequestBuilder<TsmWork1>(1,TimeUnit.DAYS)
            .setConstraints(constraints)//设置约束条件
            .setInputData(dataInput)
//            .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)// 加急策略,没有加急配额,加急任务回退成普通任务,也可以将任务丢弃
            .build()




        // 使用LiveData 监听任务状态,同时获取 任务执行结果
        WorkManager.getInstance(this).getWorkInfoByIdLiveData(oneTimeWorkRequest.id).observe(this){workInfo->
            // 需要根据任务状态来判断是否可以获取到单数, 这个方法会多次调用,只有成功会返回 Data,
            if(workInfo.state ==WorkInfo.State.SUCCEEDED){
               var result =  workInfo.outputData.getBoolean("tsm",false)
            }
        }

 //如果周期性任务重复添加,那么就会出现在一个运行周期内,这个任务被执行了多次
 // 这里就是不希望重复添加相同的任务,可以使用这个方法,     WorkManager.getInstance(this).enqueueUniquePeriodicWork("tsmTag",ExistingPeriodicWorkPolicy.KEEP,oneTimeWorkRequest)

这里在使用过程中有一个非常需要注意的点,那就是周期性任务重复添加的问题,使用WorkManager.getInstance(this).enqueueUniquePeriodicWork 这个方法可以规避

WorkManager.getInstance(this).getWorkInfosForUniqueWorkLiveData("tsmTag1").observe(this){
    it.forEach { item->
        Log.i("tian.shm","getWorkInfosByTagLiveData:${item.id}")
    }
}

使用上面代码可以查看周期性任务是否存在,并且根据实际情况来判断是否更新这个任务

我在刚开始看源码的时候,WorkManager.getInstance(this)第一次调用是我主动发起的,但是在查看官方文档的时候发现,他会在app启动的时候自动执行初始化的操作,我们来看他是如何执行的

这里有一个非常重要的关键点那就是在使用了WorkManager 之后, 会向你的App 中添加非常多的 service prvoider receiver 等标签

service 标签可以执行后台任务

prvoider 可以让WorkManager 被他人唤醒

receiver 可以接收到电量 网络等状态

大致的粘贴几个上来

<service
    android:name="androidx.work.impl.background.gcm.WorkManagerGcmService"
    android:directBootAware="false"
    android:exported="@bool/enable_gcm_scheduler_default"
    android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE" >
    <intent-filter>
        <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY" />
    </intent-filter>
</service>

<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="com.tsm.tsmworkmananger.androidx-startup"
    android:exported="false" >
    <meta-data
        android:name="androidx.work.WorkManagerInitializer"
        android:value="androidx.startup" />
</provider>

<service
    android:name="androidx.work.impl.background.systemalarm.SystemAlarmService"
    android:directBootAware="false"
    android:enabled="@bool/enable_system_alarm_service_default"
    android:exported="false" />

这个AndroidManifast 文件可以使用 Analyze Apk 这个插件来看,但是我这边不可以,但是我们可以在 /build/intermediates/merged_manifest/debug 这个目录下看到他, 从代码上可以看到,我们注册了一个 provider,那么他的执行时机就一定是 Application 的生命周期方法之前,先来看看 WorkManagerInitializer 这个类干了什么

//Initializes WorkManager using androidx.startup.
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.initialize(context, new Configuration.Builder().build());
        return WorkManager.getInstance(context);
    }

    @NonNull
    @Override
    public List<Class<? extends androidx.startup.Initializer<?>>> dependencies() {
        return Collections.emptyList();
    }
}

一个段非常简单的代码就是初始化了 WorkManager ,这也就代表着 WorkManager 的初始化工作肯定不是由我们的 WorkManager.getInstance(this).enqueue 等操作来初始化的,而且初始化的是 WorkManagerImpl ,可以看到 WorkManagerImpl 是 WorkManager 的代理实现类

那么他在初始化完成时,都做了哪些操作呢

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;
    // 任务查找是通过Room 来完成的
    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");
    }

    // 强制停止执行
    mWorkTaskExecutor.executeOnBackgroundThread(new ForceStopRunnable(context, this));
}

关于各个模块都负责哪些功能,可以根据主线流程后续查看,