美文网首页
jetpack的workmanage简单原理分析

jetpack的workmanage简单原理分析

作者: nich | 来源:发表于2022-06-07 14:17 被阅读0次

    workmanage使用场景

    非及时性的执行任务或者定时性重复任务,要求及时性/时效性不高的比如上传日志什么的。Google说WorkManager是保证你的任务一定会执行的(国内手机不一定慎重)

    简单使用

    class MyWorkManage(context:Context ,workerParams: WorkerParameters):Worker(context,workerParams) {
    
        override fun doWork(): Result {
            
            return Result.success()
        }
    
    }
    
    val constraint = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
            val request = OneTimeWorkRequest.Builder(MyWorkManage::class.java).setConstraints(constraint).build()
            WorkManager.getInstance().enqueue(request)
    
    

    简单看WorkManager.getInstance().enqueue(request)
    1.workmanager.getinstance()

    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
       public static @Nullable WorkManagerImpl getInstance() {
           synchronized (sLock) {
               if (sDelegatedInstance != null) {
                   return sDelegatedInstance;
               }
    
               return sDefaultInstance;
           }
       }
    
       /**
        * Initializes the singleton instance of {@link WorkManagerImpl}.  You should only do this if
        * you want to use a custom {@link Configuration} object and have disabled
        * WorkManagerInitializer.
        *
        * @param context A {@link Context} object for configuration purposes. Internally, this class
        *                will call {@link Context#getApplicationContext()}, so you may safely pass in
        *                any Context without risking a memory leak.
        * @param configuration The {@link Configuration} for used to set up WorkManager.
        *
        * @hide
        */
       @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());
                   }
                   sDelegatedInstance = sDefaultInstance;
               }
           }
       }
    

    看到sDelegatedInstance,sDefaultInstance都为null这两个变量只有在initialize方法中可以初始化,那什么时候调用的呢,打开apk包下面的androidmanifest

    <provider
                android:name="androidx.work.impl.WorkManagerInitializer"
                android:exported="false"
                android:multiprocess="true"
                android:authorities="com.example.day.workmanager-init"
                android:directBootAware="false" />
    
    看到provider里面有个WorkManagerInitializer点击进去
    
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public class WorkManagerInitializer extends ContentProvider {
        @Override
        public boolean onCreate() {
            // Initialize WorkManager with the default configuration.
            WorkManager.initialize(getContext(), new Configuration.Builder().build());
            return true;
        }
    
    在oncreat里面初始化调用
    WorkManager.initialize(getContext(), new Configuration.Builder().build());
    点击进去就会看到初始化
    再看上文initialize()方法里面的
    sDefaultInstance = new WorkManagerImpl(
                                context,
                                configuration,
                                new WorkManagerTaskExecutor());
    
    查看初始化方法
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        public WorkManagerImpl(
                @NonNull Context context,
                @NonNull Configuration configuration,
                @NonNull TaskExecutor workTaskExecutor,
                boolean useTestDatabase) {
    
            Context applicationContext = context.getApplicationContext();
            WorkDatabase database = WorkDatabase.create(applicationContext, useTestDatabase);
            Logger.setLogger(new Logger.LogcatLogger(configuration.getMinimumLoggingLevel()));
            List<Scheduler> schedulers = createSchedulers(applicationContext);
            Processor processor = new Processor(
                    context,
                    configuration,
                    workTaskExecutor,
                    database,
                    schedulers);
            internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
        }
    
    *WorkDatabase:room数据库
    * schedulers:调度器
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        public @NonNull List<Scheduler> createSchedulers(Context context) {
            return Arrays.asList(
                    Schedulers.createBestAvailableBackgroundScheduler(context, this),
                    new GreedyScheduler(context, this));
        }
     *processor:创建processor下面会说
     *internalInit:这个方法里面主要看
     // Checks for app force stops.
         mWorkTaskExecutor.executeOnBackgroundThread(new ForceStopRunnable(context, this));
    
    

    2.查看.enqueue(request)
    一路狂下去走

     @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);
                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 runnable = new EnqueueRunnable(this);
    mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);
    workmanager里面的线程池执行后台线程,主要看EnqueueRunnable里面 run方法
    
     @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));
            }
        }
    
    主要看scheduleWorkInBackground
    
      @VisibleForTesting
        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) {
            if (schedulers == null || schedulers.size() == 0) {
                return;
            }
    
            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();
    
                    // Mark all the WorkSpecs as scheduled.
                    // Calls to Scheduler#schedule() could potentially result in more schedules
                    // on a separate thread. Therefore, this needs to be done first.
                    for (WorkSpec workSpec : eligibleWorkSpecs) {
    //记录状态id时间到数据库里面去
                        workSpecDao.markWorkSpecScheduled(workSpec.id, now);
                    }
                }
                workDatabase.setTransactionSuccessful();
            } finally {
                workDatabase.endTransaction();
            }
    
            if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
                WorkSpec[] eligibleWorkSpecsArray = eligibleWorkSpecs.toArray(new WorkSpec[0]);
                // Delegate to the underlying scheduler.
                for (Scheduler scheduler : schedulers) {
               //调度器开始调度
                    scheduler.schedule(eligibleWorkSpecsArray);
                }
            }
        }
    
    主要看 scheduler.schedule(eligibleWorkSpecsArray);
    
     @Override
        public void schedule(WorkSpec... workSpecs) {
            registerExecutionListenerIfNeeded();
    
            // Keep track of the list of new WorkSpecs whose constraints need to be tracked.
            // Add them to the known list of constrained WorkSpecs and call replace() on
            // WorkConstraintsTracker. That way we only need to synchronize on the part where we
            // are updating mConstrainedWorkSpecs.
            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()) {
                        // Exclude content URI triggers - we don't know how to handle them here so the
                        // background scheduler should take care of them.
                        if (Build.VERSION.SDK_INT < 24
                                || !workSpec.constraints.hasContentUriTriggers()) {
                            constrainedWorkSpecs.add(workSpec);
                            constrainedWorkSpecIds.add(workSpec.id);
                        }
                    } else {
                        Logger.get().debug(TAG, String.format("Starting work for %s", workSpec.id));
                        mWorkManagerImpl.startWork(workSpec.id);
                    }
                }
            }
    
            // onExecuted() which is called on the main thread also modifies the list of mConstrained
            // WorkSpecs. Therefore we need to lock here.
            synchronized (mLock) {
                if (!constrainedWorkSpecs.isEmpty()) {
                    Logger.get().debug(TAG, String.format("Starting tracking for [%s]",
                            TextUtils.join(",", constrainedWorkSpecIds)));
                    mConstrainedWorkSpecs.addAll(constrainedWorkSpecs);
                    mWorkConstraintsTracker.replace(mConstrainedWorkSpecs);
                }
            }
        }
    里面主要看mWorkManagerImpl.startWork(workSpec.id);
    
      @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        public void startWork(String workSpecId, WorkerParameters.RuntimeExtras runtimeExtras) {
            mWorkTaskExecutor
                    .executeOnBackgroundThread(
                            new StartWorkRunnable(this, workSpecId, runtimeExtras));
        }
    
     public StartWorkRunnable(
                WorkManagerImpl workManagerImpl,
                String workSpecId,
                WorkerParameters.RuntimeExtras runtimeExtras) {
            mWorkManagerImpl = workManagerImpl;
            mWorkSpecId = workSpecId;
            mRuntimeExtras = runtimeExtras;
        }
    
        @Override
        public void run() {
            mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);
        }
    
     public boolean startWork(String id, WorkerParameters.RuntimeExtras runtimeExtras) {
            WorkerWrapper workWrapper;
            synchronized (mLock) {
                // Work may get triggered multiple times if they have passing constraints
                // and new work with those constraints are added.
                if (mEnqueuedWorkMap.containsKey(id)) {
                    Logger.get().debug(
                            TAG,
                            String.format("Work %s is already enqueued for processing", id));
                    return false;
                }
    
                workWrapper =
                        new WorkerWrapper.Builder(
                                mAppContext,
                                mConfiguration,
                                mWorkTaskExecutor,
                                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);
            }
            mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
            Logger.get().debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
            return true;
        }
    最后通过processor开始工作
    
    然后看workwrapper里面的run方法
            final SettableFuture<ListenableWorker.Result> future = SettableFuture.create();
                // Call mWorker.startWork() on the main thread.
                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);
                                }
    
                            }
                        });
    
    可以看到mworker.startwork方法
      @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;
        }
    }
    最后执行我们自己的dowork方法了
    

    无约束任务简单总结:
    1.workmanager执行enqueue()后会创建workcontinuationImpl对象执行enqueue()方法

    1. WorkContinuationImpl持有的EnqueueRunnable对象将任务添加到db,
      并交给Schedulers调度器去调度。
      3.调度器经过一系列判断会调用workmanager的startwork方法
      4.workmanager持有的startworkrunnable对象会交给process处理执行startwork
      5.processor会创建一个workwrapper对象,通过它调用worker的startwork方法,然后调用自己写的startwork方法

    有约束条件的任务执行

    在apk里面的androidmanifest里面注册了一系列广播

            <service
                android:name="androidx.work.impl.background.systemalarm.SystemAlarmService"
                android:enabled="@ref/0x7f040004"
                android:exported="false"
                android:directBootAware="false" />
    
            <service
                android:name="androidx.work.impl.background.systemjob.SystemJobService"
                android:permission="android.permission.BIND_JOB_SERVICE"
                android:enabled="@ref/0x7f040005"
                android:exported="true"
                android:directBootAware="false" />
    
            <receiver
                android:name="androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver"
                android:enabled="true"
                android:exported="false"
                android:directBootAware="false" />
    
            <receiver
                android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy"
                android:enabled="false"
                android:exported="false"
                android:directBootAware="false">
    
                <intent-filter>
    
                    <action
                        android:name="android.intent.action.ACTION_POWER_CONNECTED" />
    
                    <action
                        android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
                </intent-filter>
            </receiver>
    
    点击进去可以看到
    
    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);
        }
    
        /**
         * Proxy for Battery Not Low constraint
         */
        public static class BatteryNotLowProxy extends ConstraintProxy {
        }
    
        /**
         * Proxy for Battery Charging constraint
         */
        public static class BatteryChargingProxy extends ConstraintProxy {
        }
    统一继承了constraintproxy下面看
     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;
        }
    
      @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            super.onStartCommand(intent, flags, startId);
            if (intent != null) {
                mDispatcher.add(intent, startId);
            }
            // If the service were to crash, we want all unacknowledged Intents to get redelivered.
            return Service.START_REDELIVER_INTENT;
        }
    
    注意这个ACTION_CONSTRAINTS_CHANGED
    
    @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            super.onStartCommand(intent, flags, startId);
            if (intent != null) {
                mDispatcher.add(intent, startId);
            }
            // If the service were to crash, we want all unacknowledged Intents to get redelivered.
            return Service.START_REDELIVER_INTENT;
        }
    
    点击 mDispatcher.add(intent, startId);
    
    
     @MainThread
        public boolean add(@NonNull final Intent intent, final int startId) {
            Logger.get().debug(TAG, String.format("Adding command %s (%s)", intent, startId));
            assertMainThread();
            String action = intent.getAction();
            if (TextUtils.isEmpty(action)) {
                Logger.get().warning(TAG, "Unknown command. Ignoring");
                return false;
            }
    
            // If we have a constraints changed intent in the queue don't add a second one. We are
            // treating this intent as special because every time a worker with constraints is complete
            // it kicks off an update for constraint proxies.
            if (CommandHandler.ACTION_CONSTRAINTS_CHANGED.equals(action)
                    && hasIntentWithAction(CommandHandler.ACTION_CONSTRAINTS_CHANGED)) {
                return false;
            }
    
            intent.putExtra(KEY_START_ID, startId);
            synchronized (mIntents) {
                boolean hasCommands = !mIntents.isEmpty();
                mIntents.add(intent);
                if (!hasCommands) {
                    // Only call processCommand if this is the first command.
                    // The call to dequeueAndCheckForCompletion will process the remaining commands
                    // in the order that they were added.
                    processCommand();
                }
            }
            return true;
        }
    主要看processCommand();
    点击进去主要看
    mCommandHandler.onHandleIntent(mCurrentIntent, startId, SystemAlarmDispatcher.this);
    
    @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, startId, dispatcher);
                    } else if (ACTION_EXECUTION_COMPLETED.equals(action)) {
                        handleExecutionCompleted(intent, startId, dispatcher);
                    } else {
                        Logger.get().warning(TAG, String.format("Ignoring intent %s", intent));
                    }
                }
            }
        }
    因为之前我们看到的是ACTION_CONSTRAINTS_CHANGED
    所以主要看  handleConstraintsChanged(intent, startId, dispatcher);
    点击进去主要看
     ConstraintsCommandHandler changedCommandHandler =
                    new ConstraintsCommandHandler(mContext, startId, dispatcher);
            changedCommandHandler.handleConstraintsChanged();
    
    接下来看
     @WorkerThread
        void handleConstraintsChanged() {
            List<WorkSpec> candidates = mDispatcher.getWorkManager().getWorkDatabase()
                    .workSpecDao()
                    .getScheduledWork();
    
            // Update constraint proxy to potentially disable proxies for previously
            // completed WorkSpecs.
            ConstraintProxy.updateAll(mContext, candidates);
    
            // This needs to be done to populate matching WorkSpec ids in every constraint controller.
            mWorkConstraintsTracker.replace(candidates);
    
            List<WorkSpec> eligibleWorkSpecs = new ArrayList<>(candidates.size());
            // Filter candidates should have already been scheduled.
            long now = System.currentTimeMillis();
            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);
                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));
            }
    
            mWorkConstraintsTracker.reset();
        }
    
    最主要是
     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));
    
    
    创建了一个action为ACTION_DELAY_MET
    然后放进addrunnable中
     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);
            }
        }
    又执行了add方法点击进去执行onAllConstraintsMet(Collections.singletonList(mWorkSpecId));可以看到执行了 
    @Override
        public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
            // WorkConstraintsTracker will call onAllConstraintsMet with list of workSpecs whose
            // constraints are met. Ensure the workSpecId we are interested is part of the list
            // before we call Processor#startWork().
            if (!workSpecIds.contains(mWorkSpecId)) {
                return;
            }
    
            Logger.get().debug(TAG, String.format("onAllConstraintsMet for %s", mWorkSpecId));
            // Constraints met, schedule execution
    
            // Not using WorkManagerImpl#startWork() here because we need to know if the processor
            // actually enqueued the work here.
            // TODO(rahulrav@) Once WorkManagerImpl provides a callback for acknowledging if
            // work was enqueued, call WorkManagerImpl#startWork().
            boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);
    
            if (isEnqueued) {
                // setup timers to enforce quotas on workers that have
                // been enqueued
                mDispatcher.getWorkTimer()
                        .startTimer(mWorkSpecId, CommandHandler.WORK_PROCESSING_TIME_IN_MS, this);
            } else {
                // if we did not actually enqueue the work, it was enqueued before
                // cleanUp and pretend this never happened.
                cleanUp();
            }
        }
    可以看出执行了startwork方法
    

    总结起来就是一个大广播里面包含一系列低电量空闲什么的广播,当促发的时候就会促发ACTION_CONSTRAINTS_CHANGED,然后查询room数据库然后去执行startwork

    相关文章

      网友评论

          本文标题:jetpack的workmanage简单原理分析

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