一定会执行的WorkManager

3,202 阅读3分钟

场景

  1. 确保后台任务执行如上传,下载,同步数据等(不可作为保活的手段)
  2. 内部有对电量做了优化
  3. 执行延时的任务

使用

public class MainWorker extends Worker {

    public final static String TAG = MainWorker7.class.getSimpleName();



    // 有构造函数
    public MainWorker7(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);

    }

    @SuppressLint("RestrictedApi")
    @NonNull
    @Override
    public Result doWork() {
        Log.d(TAG, "MainWorker doWork: 后台任务执行了 started");

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }



        return new Result.Success(); // 本地执行 doWork 任务时 成功 执行任务完毕
    }


}

任务需要继承 Worker,并且是异步操作

// 约束条件
Constraints constraints = new Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED) // 约束条件,必须是网络连接
        .build();

// 构建Request
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(MainWorker7.class)
        .setConstraints(constraints)
        .build();

// 加入队列
WorkManager.getInstance(this).enqueue(request);

执行,app 存活的时候执行操作,下次打开任务一定执行

初始化

WorkManger 的初始化在打车apk 的时候动态生成了四大组件的provider

<provider
    android:name="androidx.work.impl.WorkManagerInitializer"
    android:exported="false"
    android:multiprocess="true"
    android:authorities="com.derry.workmanagersimple.workmanager-init"
    android:directBootAware="false" />

使用Android 最常用的构造者设计模式进行初始化

WorkManager.initialize(getContext(), new Configuration.Builder().build());

初始化了,配置,异步线程池,和数据库,数据库创建是非常重要是任务再次执行的关键TAG

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

执行任务

执行任务异步的EnqueueRunnable

WorkManager.getInstance(this).enqueue(request);
EnqueueRunnable runnable = new EnqueueRunnable(this);
mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);

调度器执行获取核心逻辑

  1. addToDatabase() 添加到初始化完成的数据库内 获取builder 配置

  2. scheduleWorkInBackground() 调度到后台工作


EnqueueRunnable

@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));
   }
}
  1. Schedulers.schedule 执行调度工作
@VisibleForTesting
public void scheduleWorkInBackground() {
    WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
    Schedulers.schedule(
            workManager.getConfiguration(),
            workManager.getWorkDatabase(),
            workManager.getSchedulers());
}

4. 调度器执行 GreedySchedule WorkManagerImp startWork(调度器模式/策略模式)

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


StartWorkRunnable--mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);

5. workWrapper mWorker.startWork() 最终会调用到 Worker的doWork 我们使用篇继承的就是Worker 最终形成闭环执行,首尾呼应

Processor().startWork

WorkerWrapper workWrapper;

mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);



workWrapper.runWorker()


mWorkTaskExecutor.getMainThreadExecutor()
        .execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Logger.get().debug(TAG, String.format("Starting work for %s",
                            mWorkSpec.workerClassName));
                    mInnerFuture = mWorker.startWork();
                    future.setFuture(mInnerFuture);
                } catch (Throwable e) {
                    future.setException(e);
                }

            }
        });
`


 Worker 执行doWork()

启动任务后退出app进程的再次启动执行

  1. 在使用篇的时候我们定义了网络约束,同时打包过程的时候系统帮助自动注册了网络广播接收者,这也是退出进程再次打开app任务再次执行的发起点
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>
  1. ConstraintProxy 启动服务 SystemAlarmService 系统服务
@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;
}

3 分发器准备工作,更换TAG为ACTION_DELAY_MET 执行 handleDelayMet(intent, startId, dispatcher);

SystemAlarmService

mDispatcher.add(intent, startId);

SystemAlarmDispatcher


processCommand();

 

mCommandHandler.onHandleIntent(mCurrentIntent, startId,

        SystemAlarmDispatcher.this);

  


CommandHandler


void onHandleIntent(

        @NonNull Intent intent,

        int startId,

        @NonNull SystemAlarmDispatcher dispatcher) {

  


    String action = intent.getAction();

  


    if (*ACTION_CONSTRAINTS_CHANGED*.equals(action)) {

        handleConstraintsChanged(intent, startId, dispatcher); // 更换 Tag 标记为 *ACTION_DELAY_MET*

  


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

            }

        }

    }

}
  1. 检查数据库的是否有有id,执行 startWork 回到执行任务的逻辑
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);

  // 这里获取了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

        mWorkConstraintsTracker.replace(Collections.*singletonList*(workSpec));

    }

}


 mDispatcher.getProcessor().startWork(mWorkSpecId);

mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
mDispatcher.getProcessor().startWork(mWorkSpecId);

mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);

总结

存储任务相关配置,存储是永久性配置,也是一定会执行任务的关键点,不管是马上执行还是重新打开app重新执行,读取数据库信息再执行保证任务的准确性