美文网首页
jetpack系列——WorkManager源码分析

jetpack系列——WorkManager源码分析

作者: Peakmain | 来源:发表于2021-11-06 14:28 被阅读0次

    相关文章系列

    WorkManager的作用

    • 确保有重要的后台任务,一定会被执行(例如:非及时性的上传、下载,同步数据等)
    • 内部对电量进行了优化,不需要我们去处理电量优化
    • API14到最新版本,都可以使用WorkManager来管理后台任务
    • WorkManager不能做保活操作
    • 调度,管理,执行的后台任务的常见,通常是可延迟的后台任务
    image.png
       def work_version = "2.3.4"
        // Kotlin + coroutines
        implementation "androidx.work:work-runtime-ktx:$work_version"
        // optional - GCMNetworkManager support
        implementation "androidx.work:work-gcm:$work_version"
        // optional - Test helpers
        androidTestImplementation "androidx.work:work-testing:$work_version"
    
    class MyWork(@NonNull context: Context, @NonNull workerParams: WorkerParameters)
        : Worker(context, workerParams) {
        override fun doWork(): Result {
            val value = inputData.getString("in_key")
            Log.e("TAG", "value = $value")
            Log.e("TAG", "开始睡眠")
            Thread.sleep(3000)
            Log.e("TAG", "结束睡眠")
            return Result.success(workDataOf("out_key" to "$value result"))
        }
    }
    
             val constraints = Constraints.Builder()
                     //NetworkType.NOT_REQUIRED:对网络没有要求
                     //NetworkType.CONNECTED:网络的时候执行
                     //NetworkType.UNMETERED:不计费的网络比如Wi-Fi
                     //NetworkType.NOT_ROAMING:非漫游网络
                     //NetworkType.METERED:计费网络比如3g,4g下执行
                     //注意:不代表恢复网络,就立马执行
                     .setRequiredNetworkType(NetworkType.METERED)//👈设置条件为网络连接情况
                     .build()
            val workReuest =
                   //PeriodicWorkRequest:周期性任务
                    OneTimeWorkRequestBuilder<MyWork>()//👈一次性任务
                            //.setConstraints(constraints)//👈设置条件
                            .setInputData(workDataOf("in_key" to "Peakmain"))//👈输入简单数据
                            .setInitialDelay(5L,TimeUnit.SECONDS)//设置5s延迟执行
                            .addTag("peakmain")
                            .build()
            workManager.enqueue(workReuest)
         //监听数据变化
            workManager.getWorkInfoByIdLiveData(workReuest.id)
                    .observe(this, Observer {
                        Log.e("TAG", "onCreate:" + it.state)
                        if (it.state == WorkInfo.State.SUCCEEDED) {
                            Log.e("TAG", "onCreate" + it.outputData.getString("out_key"))
                        }
                    })
     //取消任务
     workManager.cancelWorkById(workRequest.getId());
    
    • 任务链和任务组合
      • 责任链


        责任链.png
          WorkManager.getInstance(this)
                  .beginWith(A)
                  .then(B).enqueue()
    
    • AB,CD先执行最后执行E


      情况二.png
       var workManager1: WorkContinuation=WorkManager.getInstance(this)
                  .beginWith(A)
                  .then(B).enqueue()
       var workManager2: WorkContinuation=WorkManager.getInstance(this)
                  .beginWith(A)
                  .then(B).enqueue()
    var takeList = ArrayList<WorkContinuation>()
    takeList.add(workManager1)
    takeList.add(workManager2)
    var workManager: WorkContinuation = WorkContinuation.combine(takeList)
            .then(E)
            .enqeue()
    

    源码分析

    初始化操作getInstance源码分析

         public static @NonNull WorkManager getInstance(@NonNull Context context) {
            return WorkManagerImpl.getInstance(context);
        }
    
        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 的实现类

    initialize源码分析

        public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
            synchronized (sLock) {
                if (sDelegatedInstance != null && sDefaultInstance != null) {
                    //抛出异常
                }
    
                if (sDelegatedInstance == null) {
                    context = context.getApplicationContext();
                    if (sDefaultInstance == null) {
                        sDefaultInstance = new WorkManagerImpl(
                                context,
                                configuration,
                                new WorkManagerTaskExecutor(configuration.getTaskExecutor()));
                    }
                    sDelegatedInstance = sDefaultInstance;
                }
            }
        }
    
    • initialize目的是new WorkManagerImpl实例
        public WorkManagerImpl(
                @NonNull Context context,
                @NonNull Configuration configuration,
                @NonNull TaskExecutor workTaskExecutor) {
            this(context,
                    configuration,
                    workTaskExecutor,
                    context.getResources().getBoolean(R.bool.workmanager_test_configuration));
        }
        public WorkManagerImpl(
                @NonNull Context context,
                @NonNull Configuration configuration,
                @NonNull TaskExecutor workTaskExecutor,
                boolean useTestDatabase) {
            this(context,
                    configuration,
                    workTaskExecutor,
                    WorkDatabase.create(
                            context.getApplicationContext(),
                            workTaskExecutor.getBackgroundExecutor(),
                            useTestDatabase)//👈创建了Room数据库
            );
        }
        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()));
            List<Scheduler> schedulers = createSchedulers(applicationContext, workTaskExecutor);
            Processor processor = new Processor(
                    context,
                    configuration,
                    workTaskExecutor,
                    database,
                    schedulers);
            internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
        }
        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;
    
            // Checks for app force stops.
            mWorkTaskExecutor.executeOnBackgroundThread(new ForceStopRunnable(context, this));
        }
    
    
    • 创建数据库Room
    • 创建任务池
    • 创建Processor
    • Checks for app force stops.检测被停止的事情(如:关机、死机等),百分百会执行ForceStopRunnable
    • ForceStopRunnable目的是内部做容错处理,手机发送意外,内部必须知道

    加入队列执行源码分析

        public final Operation enqueue(@NonNull WorkRequest workRequest) {
            return enqueue(Collections.singletonList(workRequest));
        }
       public abstract Operation enqueue(@NonNull List<? extends WorkRequest> requests);
    

    上面分析,我们知道实现类是WorkManagerImpl,所以会调用WorkManagerImpl的enqueue方法

        public Operation enqueue(
                @NonNull List<? extends WorkRequest> workRequests) {
            //代码省略
            return new WorkContinuationImpl(this, workRequests).enqueue();
        }
        public @NonNull Operation enqueue() {
            // Only enqueue if not already enqueued.
            if (!mEnqueued) {
                EnqueueRunnable runnable = new EnqueueRunnable(this);
                mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);
                mOperation = runnable.getOperation();
            } else {
               //代码省略
            }
            return mOperation;
        }
        public void run() {
            try {
                //代码省略
                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));
            }
        }
    

    开始执行后台任务

        public void scheduleWorkInBackground() {
            WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
            Schedulers.schedule(
                    workManager.getConfiguration(),//👈配置
                    workManager.getWorkDatabase(),//👈数据库
                    workManager.getSchedulers());
        }
        public static void schedule(
                @NonNull Configuration configuration,
                @NonNull WorkDatabase workDatabase,
                List<Scheduler> schedulers) {
             //获得Room
            WorkSpecDao workSpecDao = workDatabase.workSpecDao();
            List<WorkSpec> eligibleWorkSpecs;
             //开启事务
            workDatabase.beginTransaction();
            try {
                eligibleWorkSpecs = workSpecDao.getEligibleWorkForScheduling(
                        configuration.getMaxSchedulerLimit());
                if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
                    long now = System.currentTimeMillis();
                    for (WorkSpec workSpec : eligibleWorkSpecs) {
                        workSpecDao.markWorkSpecScheduled(workSpec.id, now);
                    }
                }
                workDatabase.setTransactionSuccessful();
            } finally {
                workDatabase.endTransaction();
            }
    
            if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
                WorkSpec[] eligibleWorkSpecsArray = eligibleWorkSpecs.toArray(new WorkSpec[0]);
                for (Scheduler scheduler : schedulers) {
                    scheduler.schedule(eligibleWorkSpecsArray);
                }
            }
        }
        void schedule(@NonNull WorkSpec... workSpecs);
    
    image.png

    我们会发现有5个实现类,我们看GreedyScheduler

        public void schedule(@NonNull WorkSpec... workSpecs) {
                   //代码省略
            registerExecutionListenerIfNeeded();
            List<WorkSpec> constrainedWorkSpecs = new ArrayList<>();
            List<String> constrainedWorkSpecIds = new ArrayList<>();
              //遍历所有的任务
            for (WorkSpec workSpec : workSpecs) {
                if (workSpec.state == WorkInfo.State.ENQUEUED
                        && !workSpec.isPeriodic()
                        && workSpec.initialDelay == 0L
                        && !workSpec.isBackedOff()) {
                    if (workSpec.hasConstraints()) {
                           //是否设置了约束条件
                        if (SDK_INT >= 23 && workSpec.constraints.requiresDeviceIdle()) {
                            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);
                    }
                }
            }
            synchronized (mLock) {
                if (!constrainedWorkSpecs.isEmpty()) {
                    mConstrainedWorkSpecs.addAll(constrainedWorkSpecs);
                    mWorkConstraintsTracker.replace(mConstrainedWorkSpecs);
                }
            }
        }
    

    一、当我们没有设置约束的时候,会走到startWork

        public void startWork(@NonNull String workSpecId) {
            startWork(workSpecId, null);
        }
        public void startWork(
                @NonNull String workSpecId,
                @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
            mWorkTaskExecutor
                    .executeOnBackgroundThread(
                            new StartWorkRunnable(this, workSpecId, runtimeExtras));
        }
        public void run() {
            mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);
        }
        public boolean startWork(
                @NonNull String id,
                @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
            WorkerWrapper workWrapper;
            synchronized (mLock) {
              //WorkerWrapper实际是个子线程
                workWrapper =
                        new WorkerWrapper.Builder(
                                mAppContext,
                                mConfiguration,
                                mWorkTaskExecutor,
                                this,
                                mWorkDatabase,
                                id)
                                .withSchedulers(mSchedulers)
                                .withRuntimeExtras(runtimeExtras)
                                .build();
    
            }
            mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
            Logger.get().debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
            return true;
        }
        public void run() {
            mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
            mWorkDescription = createWorkDescription(mTags);
            runWorker();
        }
        private void runWorker() {
            if (trySetRunning()) {
                mWorkTaskExecutor.getMainThreadExecutor()
                        .execute(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    mInnerFuture = mWorker.startWork();
                                    future.setFuture(mInnerFuture);
                                } catch (Throwable e) {
                                    future.setException(e);
                                }
    
                            }
                        });
    
                 
                }, mWorkTaskExecutor.getBackgroundExecutor());
            } 
              //代码省略
        }
        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;
        }
     public abstract @NonNull Result doWork();
    

    到这里我们就会走到自己代码中的doWork方法了

    二、设置约束条件源码分析

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

        public int onStartCommand(Intent intent, int flags, int startId) {
            super.onStartCommand(intent, flags, startId);
            if (mIsShutdown) {
                mDispatcher.onDestroy();
                // Create a new dispatcher to setup a new lifecycle.
                initializeDispatcher();
                // Set mIsShutdown to false, to correctly accept new commands.
                mIsShutdown = false;
            }
    
            if (intent != null) {
                mDispatcher.add(intent, startId);
            }
            return Service.START_REDELIVER_INTENT;
        }
        public boolean add(@NonNull final Intent intent, final int startId) {
            String action = intent.getAction();
            intent.putExtra(KEY_START_ID, startId);
            synchronized (mIntents) {
                boolean hasCommands = !mIntents.isEmpty();
                mIntents.add(intent);
                if (!hasCommands) {
                    processCommand();
                }
            }
            return true;
        }
        private void processCommand() {
            assertMainThread();
            PowerManager.WakeLock processCommandLock =
                    WakeLocks.newWakeLock(mContext, PROCESS_COMMAND_TAG);
            try {
                processCommandLock.acquire();
                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();
                                mCommandHandler.onHandleIntent(mCurrentIntent, startId,
                                        SystemAlarmDispatcher.this);
                            } catch (Throwable throwable) {
                            }  finally {
                            }
                        }
                    }
                });
            } finally {
                processCommandLock.release();
            }
        }
        void onHandleIntent(
                @NonNull Intent intent,
                int startId,
                @NonNull SystemAlarmDispatcher dispatcher) {
    
            String action = intent.getAction();
    
            if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {
                handleConstraintsChanged(intent, startId, dispatcher);
            }
              //代码省略
        }
        private void handleConstraintsChanged(
                @NonNull Intent intent, int startId,
                @NonNull SystemAlarmDispatcher dispatcher) {
            ConstraintsCommandHandler changedCommandHandler =
                    new ConstraintsCommandHandler(mContext, startId, dispatcher);
            changedCommandHandler.handleConstraintsChanged();
        }
        //改变约束条件
        void handleConstraintsChanged() {
              //后的所有数据库的任务数据
            List<WorkSpec> candidates = mDispatcher.getWorkManager().getWorkDatabase()
                    .workSpecDao()
                    .getScheduledWork();
            ConstraintProxy.updateAll(mContext, candidates);
            //遍历所有数据库的数据
            for (WorkSpec workSpec : candidates) {
                String workSpecId = workSpec.id;
                long triggerAt = workSpec.calculateNextRunTime();
                if (now >= triggerAt && (!workSpec.hasConstraints()
                        || mWorkConstraintsTracker.areAllConstraintsMet(workSpecId))) {
                    eligibleWorkSpecs.add(workSpec);
                }
            }
             //遍历集合
            for (WorkSpec workSpec : eligibleWorkSpecs) {
                String workSpecId = workSpec.id;
                Intent intent = CommandHandler.createDelayMetIntent(mContext, workSpecId);
                mDispatcher.postOnMainThread(
                        new SystemAlarmDispatcher.AddRunnable(mDispatcher, intent, mStartId));
            }
            mWorkConstraintsTracker.reset();
        }
         //修改约束条件
        static Intent createDelayMetIntent(@NonNull Context context, @NonNull String workSpecId) {
            Intent intent = new Intent(context, SystemAlarmService.class);
            intent.setAction(ACTION_DELAY_MET);
            intent.putExtra(KEY_WORKSPEC_ID, workSpecId);
            return intent;
        }
    

    此时又会走一遍SystemAlarmService的onStartCommand,最后会再次来到onHandleIntent方法

        void onHandleIntent(
                @NonNull Intent intent,
                int startId,
                @NonNull SystemAlarmDispatcher dispatcher) {
    
            String action = intent.getAction();
           else if (ACTION_DELAY_MET.equals(action)) {
                        handleDelayMet(intent, startId, dispatcher);
                    }
                }
            }
        }
        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));
                if (!mPendingDelayMet.containsKey(workSpecId)) {
                    DelayMetCommandHandler delayMetCommandHandler =
                            new DelayMetCommandHandler(mContext, startId, workSpecId, dispatcher);
                    mPendingDelayMet.put(workSpecId, delayMetCommandHandler);
                    delayMetCommandHandler.handleProcessWork();
                } else {
                }
            }
        }
        void handleProcessWork() {
            mWakeLock = WakeLocks.newWakeLock(
                    mContext,
                    String.format("%s (%s)", mWorkSpecId, mStartId));
            mWakeLock.acquire();
    
            WorkSpec workSpec = mDispatcher.getWorkManager()
                    .getWorkDatabase()
                    .workSpecDao()
                    .getWorkSpec(mWorkSpecId);
    
      
            if (workSpec == null) {
                stopWork();
                return;
            }
            mHasConstraints = workSpec.hasConstraints();
    
            if (!mHasConstraints) {
                onAllConstraintsMet(Collections.singletonList(mWorkSpecId));
            } else {
                mWorkConstraintsTracker.replace(Collections.singletonList(workSpec));
            }
        }
        public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
            if (!workSpecIds.contains(mWorkSpecId)) {
                return;
            }
    
            synchronized (mLock) {
                if (mCurrentState == STATE_INITIAL) {
                    mCurrentState = STATE_START_REQUESTED;
                    boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);
                } else {
                    Logger.get().debug(TAG, String.format("Already started work for %s", mWorkSpecId));
                }
            }
        }
        public boolean startWork(@NonNull String id) {
            return startWork(id, null);
        }
    

    我们发现回到了没有设置约束的时候

    相关文章

      网友评论

          本文标题:jetpack系列——WorkManager源码分析

          本文链接:https://www.haomeiwen.com/subject/dtirzltx.html