美文网首页
任务调度JobScheduler源码剖析

任务调度JobScheduler源码剖析

作者: 码上就说 | 来源:发表于2018-08-20 11:02 被阅读376次

    一、JobScheduler简介

    JobScheduler是api-21版本上新增加的一个api类,用于根据应用程序自身进程中安排的任务情况来安排各种类型的job。
    使用JobScheduler,需要构建JobInfo,然后自定义一个JobService,在使用构造JobInfo时,可以标识实现作业逻辑的服务组件JobInfo.Builder(int, android.content.ComponetName).
    这个作业调度的执行框架主要目的就是将执行的作业智能化,尽可能批量地处理这些定义好的job,如果没有定义截止日期,可以随时运行作业,具体取决于JobScheduler内部队列的当前状态。
    当作业运行的时候,系统表示你的应用程序保留唤醒锁。因此你无需任何措施来保证设备在工作期间保持清醒状态,之前采用的广播什么的可以忽略了。
    你不需要实例化这个JobScheduler,直接使用Context.getSystemService(Context.JOB_SCHEDULER_SERVICE),这是系统启动的时候加到Context中静态块中的系统服务,但是要记住,这个和注册到ServiceManager中的服务有本质的区别。

    二、作业调度核心类与接口

    2.1 作业调度Client端

    frameworks/base/core/java/android/app/job/
    
    IJobCallback.aidl
    IJobScheduler.aidl
    IJobService.aidl
    JobInfo.java
    JobInfo.aidl
    JobParameters.java
    JobParameters.aidl
    JobScheduler.java
    JobService.java
    JobServiceEngine.java
    JobWorkItem.java
    JobWorkItem.aidl
    

    我们关注的几个核心类是JobInfo.java、JobScheduler.java、JobService.java,它们之间简单的关系如下:


    作业调度结构图.jpg

    所谓client端,就是开发者在使用作业调度的时候,直接调用的就是clien端的api,所以这些api接口开发者会很熟悉,开发者基本的调度步骤是:

    • 通过Context本地获取JOB_SCHEDULER_SERVICE代表的JobScheduler对象。
      一般的调度步骤是:
      JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
      具体的解释看下面。
    • 创建JobInfo对象,JobInfo对象内部是使用Builder设计模式来获取的,Builder内部类中有很多JobInfo相关的属性,这是控制Job属性的重要标识,后续的作业调度也是根据设置的job属性来决定的。下面透露一些简单的属性:
      private final int mJobId;
      private final ComponentName mJobService; //这个JobService就是下面要设置的JobService对象
      private int mConstraintFlags; //这个限制条件里面的信息很多,例如是否正在充电、是否在wifi条件等等。
    • 自定义一个类,继承JobService,继承其中的函数,尤其是:
      onStartJob(JobParameters);
      onStopJob(JobParameters);
    • 调用第一步中获取的jobScheduler对象,执行schedule(JobInfo)函数,当前定义的Job已经在运行中,会根据设置的Job条件来执行。

    这儿谈到的JobSchedulerImpl对象,是在系统启动的时候加入到SystemServiceRegister中的。

    SystemServiceRegistry.java
    
           registerService(Context.JOB_SCHEDULER_SERVICE, JobScheduler.class,
                    new StaticServiceFetcher<JobScheduler>() {
                @Override
                public JobScheduler createService() throws ServiceNotFoundException {
                    IBinder b = ServiceManager.getServiceOrThrow(Context.JOB_SCHEDULER_SERVICE);
                    return new JobSchedulerImpl(IJobScheduler.Stub.asInterface(b));
                }});
    

    这儿可以看出来,全局注册的Service中使用的是JobSchedulerImpl对象,所以才不需要开发者手动new一个JobScheduler对象出来了。

    2.2 作业调度server端

    这儿直接截图吧,代码的位置在frameworks/base/services/core/java/com/android/server/job


    作业调度server端.png

    这儿的代码是完全对开发者隐藏的,但是这儿才是实现作业调度的核心代码。注意到一个关键的类:JobSchedulerService.java,这是SystemService的子类。

    2.2.1 JobSchedulerService启动
    SystemServer.java
    
    private void startOtherServices() {
    //......
          traceBeginAndSlog("StartJobScheduler");
          mSystemServiceManager.startService(JobSchedulerService.class);
          traceEnd();
    //......
    }
    

    这儿执行了JobSchedulerService的构造函数,并且将获取的对象存放在SystemServiceManager.java中的链表中。
    构造函数中做了什么?

    JobSchedulerService.java
    
    public JobSchedulerService(Context context) {
            super(context);
            mHandler = new JobHandler(context.getMainLooper());
            mConstants = new Constants(mHandler);
            mJobSchedulerStub = new JobSchedulerStub();
            mJobs = JobStore.initAndGet(this);
    
            // Create the controllers.
            mControllers = new ArrayList<StateController>();
            mControllers.add(ConnectivityController.get(this));
            mControllers.add(TimeController.get(this));
            mControllers.add(IdleController.get(this));
            mBatteryController = BatteryController.get(this);
            mControllers.add(mBatteryController);
            mStorageController = StorageController.get(this);
            mControllers.add(mStorageController);
            mControllers.add(AppIdleController.get(this));
            mControllers.add(ContentObserverController.get(this));
            mControllers.add(DeviceIdleJobsController.get(this));
    
            // If the job store determined that it can't yet reschedule persisted jobs,
            // we need to start watching the clock.
            if (!mJobs.jobTimesInflatedValid()) {
                Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling");
                context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED));
            }
        }
    
    2.2.1.1 创建一个JobHandler

    JobHandler中使用的Looper是MainLooper,说明执行过程运行在主线程中。

    2.2.1.2 创建JobSchedulerStub对象

    这个对象是Binder类型,是JobScheduler的binder的server端,JobScheduler中执行的函数都在这里面接收。

    final class JobSchedulerStub extends IJobScheduler.Stub {
    }
    
    2.2.1.3 获取JobStore对象

    这个JobStore是维护作业调度程序正在跟踪的作业的主列表,除此之外,还处理持久作业的读写。
    mJobs = JobStore.initAndGet(this);

    JobStore.java
    
    static JobStore initAndGet(JobSchedulerService jobManagerService) {
            synchronized (sSingletonLock) {
                if (sSingleton == null) {
                    sSingleton = new JobStore(jobManagerService.getContext(),
                            jobManagerService.getLock(), Environment.getDataDirectory());
                }
                return sSingleton;
            }
        }
    
    private JobStore(Context context, Object lock, File dataDir) {
            mLock = lock;
            mContext = context;
            mDirtyOperations = 0;
    
            File systemDir = new File(dataDir, "system");
            File jobDir = new File(systemDir, "job");
            jobDir.mkdirs();
            mJobsFile = new AtomicFile(new File(jobDir, "jobs.xml"));
    
            mJobSet = new JobSet();
            mXmlTimestamp = mJobsFile.getLastModifiedTime();
            mRtcGood = (System.currentTimeMillis() > mXmlTimestamp);
    
            readJobMapFromDisk(mJobSet, mRtcGood);
        }
    

    主要的工作是读取/data/system/job/jobs.xml中的内容,并将内容更新到本地来。这些Job相关的信息都会在后期管理job的时候用到。


    jobs信息.png

    下面是jobs.xml中的部分内容,这些和job相关的属性,会被更新到本地。

    <?xml version='1.0' encoding='utf-8' standalone='yes' ?>
    <job-info version="0">
        <job jobid="2" package="com.xdandroid.sample" class="com.xdandroid.hellodaemon.JobSchedulerService" sourcePackageName="com.xdandroid.sample" sourceUserId="0" uid="10167" priority="0" flags="0" lastSuccessfulRunTime="1534682553544" lastFailedRunTime="0">
            <constraints />
            <periodic period="900000" flex="300000" deadline="1534683741578" delay="1534683441578" />
            <extras />
        </job>
        <job jobid="1000" package="com.qualcomm.qti.qms.service.connectionsecurity" class="com.qualcomm.qti.qms.service.connectionsecurity.cloud.ReportJobService" sourcePackageName="com.qualcomm.qti.qms.service.connectionsecurity" sourceUserId="0" uid="10123" priority="0" flags="0" lastSuccessfulRunTime="0" lastFailedRunTime="0">
            <constraints net-capabilities="94208" net-unwanted-capabilities="0" net-transport-types="0" />
            <one-off delay="1535250620603" backoff-policy="1" initial-backoff="3600000" />
            <extras />
        </job>
    </job-info>
    
    2.2.1.4 创建了一个StateController列表

    放入了8个StateController对象。
    StateController就是状态控制器,它是一个抽象类,链表中的8个对象都是继承它的。
    StateController就是在作业管理的各种控制器之间包含共享控制器逻辑,它们全权负责跟踪作业列表,并在这些作业准备好时通知作业管理器运行,或者停止运行等等。

    Controller名称 作用
    ConnectivityController 网络状态变化控制器
    TimeController 为下一个即将到期的作业设置警报
    IdleController 注册屏幕亮与熄灭的监听,以及Dream模式开始与停止的监听,还有Debug模式的监听
    BatteryController 注册电池健康状态的监听和电池是否处于充电状态的监听
    StorageController 注册存储空间的监听
    AppIdleController 监听app是否空闲,尚未从前台应用程序主动启动或者访问的应用程序,在一段时间后被视为闲置,当应用程序进入闲置状态时,它可能被运行一些预定的作业
    ContentObserverController 用于通过ContentObserver监视对内容URI的更改的控制器
    DeviceIdleJobsController 当设备处于瞌睡的状态时,为除白名单之外的所有作业设置约束;当设备没有瞌睡时,将所有作业的约束设置为满足
    public abstract class StateController {
        protected static final boolean DEBUG = JobSchedulerService.DEBUG;
        protected final Context mContext;
        protected final Object mLock;
        protected final StateChangedListener mStateChangedListener;
    
        public StateController(StateChangedListener stateChangedListener, Context context,
                Object lock) {
            mStateChangedListener = stateChangedListener;
            mContext = context;
            mLock = lock;
        }
        public abstract void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob);
        public void prepareForExecutionLocked(JobStatus jobStatus) {
        }
        public abstract void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
                boolean forUpdate);
        public void rescheduleForFailureLocked(JobStatus newJob, JobStatus failureToReschedule) {
        }
        public abstract void dumpControllerStateLocked(PrintWriter pw, int filterUid);
    }
    
    • maybeStartTrackingJobLocked:
      这个函数的实现逻辑以确定是否应该由此控制器跟踪作业。在更新任务的时候也会调用,因此实现控制器必须注意预先存在的任务。
    • prepareForExecutionLocked:
      job即将执行的时候执行的逻辑。
    • maybeStopTrackingJobLocked:
      这儿实现的逻辑是当任务被取消的时候执行的。
    • rescheduleForFailureLocked:
      当一个新的job被创建在一个老的失败的job上重新调度的时候实现的逻辑。
    2.2.1.5 重新调度持久化job

    如果上面的JobStore确定无法安排持久性的job,我们需要注册一个Time Clock的监听来保证持久性job完成。

    if (!mJobs.jobTimesInflatedValid()) {
                Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling");
                context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED));
            }
    
    2.2.2 解析JobInfo

    上面《2.2.1.3 获取JobStore对象》谈到了从本地的jobs.xml中解析Job信息,解析的具体过程是怎样的,下面讲解一下。
    从本地读取Job信息是在放在一个实现Runable接口的类中去做的。在run()中执行的核心方法是:

                    List<JobStatus> jobs;
                    FileInputStream fis = mJobsFile.openRead();
                    synchronized (mLock) {
                        jobs = readJobMapImpl(fis, rtcGood);
                        if (jobs != null) {
                            long now = SystemClock.elapsedRealtime();
                            IActivityManager am = ActivityManager.getService();
                            for (int i=0; i<jobs.size(); i++) {
                                JobStatus js = jobs.get(i);
                                js.prepareLocked(am);
                                js.enqueueTime = now;
                                this.jobSet.add(js);
    
                                numJobs++;
                                if (js.getUid() == Process.SYSTEM_UID) {
                                    numSystemJobs++;
                                    if (isSyncJob(js)) {
                                        numSyncJobs++;
                                    }
                                }
                            }
                        }
    

    其中核心函数readJobMapImpl(...)将本地文件读取起来转化为JobStatus,然后放在一个List中。
    解析XML中的分支元素,并将其中节点信息放在JobInfo.Builder中,然后再利用JobInfo构建一个JobStatus对象。这就是执行的核心要点。

    2.2.3 JobSchedulerService启动阶段

    执行的核心是继承SystemService.java中的

    SystemService.java
    
    public void onBootPhase(int phase) {}
    

    在函数执行内部,也细分为两个阶段:

    public void onBootPhase(int phase) {
            if (PHASE_SYSTEM_SERVICES_READY == phase) {
                mConstants.start(getContext().getContentResolver());
                // Register br for package removals and user removals.
                final IntentFilter filter = new IntentFilter();
                filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
                filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
                filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
                filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
                filter.addDataScheme("package");
                getContext().registerReceiverAsUser(
                        mBroadcastReceiver, UserHandle.ALL, filter, null, null);
                final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
                getContext().registerReceiverAsUser(
                        mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
                mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
                try {
                    ActivityManager.getService().registerUidObserver(mUidObserver,
                            ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
                            | ActivityManager.UID_OBSERVER_IDLE, ActivityManager.PROCESS_STATE_UNKNOWN,
                            null);
                } catch (RemoteException e) {
                    // ignored; both services live in system_server
                }
                // Remove any jobs that are not associated with any of the current users.
                cancelJobsForNonExistentUsers();
            } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
                synchronized (mLock) {
                    // Let's go!
                    mReadyToRock = true;
                    mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
                            BatteryStats.SERVICE_NAME));
                    mLocalDeviceIdleController
                            = LocalServices.getService(DeviceIdleController.LocalService.class);
                    // Create the "runners".
                    for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
                        mActiveServices.add(
                                new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
                                        getContext().getMainLooper()));
                    }
                    // Attach jobs to their controllers.
                    mJobs.forEachJob(new JobStatusFunctor() {
                        @Override
                        public void process(JobStatus job) {
                            for (int controller = 0; controller < mControllers.size(); controller++) {
                                final StateController sc = mControllers.get(controller);
                                sc.maybeStartTrackingJobLocked(job, null);
                            }
                        }
                    });
                    // GO GO GO!
                    mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
                }
            }
        }
    
    2.2.3.1 系统服务启动阶段

    这儿执行的东西总结起来就是

    • 注册启动ContentResolver的监听
    • 注册多个Receiver,来监听Package事件——ACTION_PACKAGE_REMOVED、ACTION_PACKAGE_CHANGED、ACTION_PACKAGE_RESTARTED、ACTION_QUERY_PACKAGE_RESTART等等
    2.2.3.2 三方应用启动阶段

    创建了JobServiceContext对象,这个类是处理客户端绑定和作业的生命周期,作业在一个实例上一次执行一个。

    public final class JobServiceContext implements ServiceConnection {
          public void onServiceConnected(ComponentName name, IBinder service) {
    //......
          }
          public void onServiceDisconnected(ComponentName name) {
            synchronized (mLock) {
                closeAndCleanupJobLocked(true /* needsReschedule */, "unexpectedly disconnected");
            }
        }
    }
    

    这两个重要的方法分别标识当前JobServiceContext包含着两个重要的交互操作,执行一个job和取消一个job

    三、作业操作

    3.1 自定义JobService

    JobService本质上是一个Service,但是又多了3个重要的方法,这个方法是关系到Job执行状态的。

    public abstract class JobService extends Service {
        private static final String TAG = "JobService";
        public static final String PERMISSION_BIND =
                "android.permission.BIND_JOB_SERVICE";
    
        private JobServiceEngine mEngine;
        public final IBinder onBind(Intent intent) {
            if (mEngine == null) {
                mEngine = new JobServiceEngine(this) {
                    @Override
                    public boolean onStartJob(JobParameters params) {
                        return JobService.this.onStartJob(params);
                    }
    
                    @Override
                    public boolean onStopJob(JobParameters params) {
                        return JobService.this.onStopJob(params);
                    }
                };
            }
            return mEngine.getBinder();
        }
        public abstract boolean onStartJob(JobParameters params);
        public abstract boolean onStopJob(JobParameters params);
        public final void jobFinished(JobParameters params, boolean needsReschedule) {
            mEngine.jobFinished(params, needsReschedule);
        }
    }
    
    3.1.1 绑定service

    既然是service,那么还是要和普通的service一样的,自定义的JobService也是需要绑定的,bindService会回调到onBind(...)

    3.1.2 onStartJob

    很显然这个方法是启动当前job执行的核心方法,值得注意的是:这个方法是执行在应用程序的主线程的,开发者写核心逻辑的时候,还是应该另起一个线程来执行主要的逻辑。

    3.1.3 onStopJob

    如果当前决定需要停止job的执行,调用此方法。

    3.1.4 jobFinished

    调用此方法通知JobManager已知完成执行,可以在任何线程中调用,但是最终会调度到程序的主线程中执行,当系统收到此消息时,它会释放当前的唤醒锁。

    onStartJob与onStopJob都是需要开发者继承的方法。

    3.2 作业调度

    最终需要调度当前的job,执行JobScheduler中的schedule方法,从执行的路径来看:

    JobSchedulerImpl.java
    public int schedule(JobInfo job) {
            try {
                return mBinder.schedule(job);
            } catch (RemoteException e) {
                return JobScheduler.RESULT_FAILURE;
            }
        }
    

    这时候调用到JobSchedulerService中的JobSchedulerStub内部类中:

    public int schedule(JobInfo job) throws RemoteException {
                if (DEBUG) {
                    Slog.d(TAG, "Scheduling job: " + job.toString());
                }
                final int pid = Binder.getCallingPid();
                final int uid = Binder.getCallingUid();
    
                enforceValidJobRequest(uid, job);
                if (job.isPersisted()) {
                    if (!canPersistJobs(pid, uid)) {
                        throw new IllegalArgumentException("Error: requested job be persisted without"
                                + " holding RECEIVE_BOOT_COMPLETED permission.");
                    }
                }
    
                if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
                    getContext().enforceCallingOrSelfPermission(
                            android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
                }
    
                long ident = Binder.clearCallingIdentity();
                try {
                    return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, -1, null);
                } finally {
                    Binder.restoreCallingIdentity(ident);
                }
            }
    

    最终JobInfo会被转换成JobStatus,直接调用到:

    private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
            if (!jobStatus.isPreparedLocked()) {
                Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
            }
            jobStatus.enqueueTime = SystemClock.elapsedRealtime();
            final boolean update = mJobs.add(jobStatus);
            if (mReadyToRock) {
                for (int i = 0; i < mControllers.size(); i++) {
                    StateController controller = mControllers.get(i);
                    if (update) {
                        controller.maybeStopTrackingJobLocked(jobStatus, null, true);
                    }
                    controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
                }
            }
        }
    

    这儿会将我们之前加入StateController遍历一遍,看看是否满足条件,如果满足条件,直接启动状态控制器追踪一下。

    如果当前绑定成功的话,会进入到JobServiceContext中的onServiceConnected(...)方法。

    JobServiceContext.java
    
    public void onServiceConnected(ComponentName name, IBinder service) {
    //.....
          doServiceBoundLocked();
    }
    
    void doServiceBoundLocked() {
            removeOpTimeOutLocked();
            handleServiceBoundLocked();
        }
    
     private void handleServiceBoundLocked() {
    //......
            try {
                service.startJob(mParams);
            } catch (Exception e) {
            }
        }
    

    这是最后一部,也是最关键的一部:

    JobServiceEngine.java
    
    static final class JobInterface extends IJobService.Stub {
            final WeakReference<JobServiceEngine> mService;
    
            JobInterface(JobServiceEngine service) {
                mService = new WeakReference<>(service);
            }
    
            @Override
            public void startJob(JobParameters jobParams) throws RemoteException {
                JobServiceEngine service = mService.get();
                if (service != null) {
                    Message m = Message.obtain(service.mHandler, MSG_EXECUTE_JOB, jobParams);
                    m.sendToTarget();
                }
            }
    
            @Override
            public void stopJob(JobParameters jobParams) throws RemoteException {
                JobServiceEngine service = mService.get();
                if (service != null) {
                    Message m = Message.obtain(service.mHandler, MSG_STOP_JOB, jobParams);
                    m.sendToTarget();
                }
            }
        }
    
    class JobHandler extends Handler {
            JobHandler(Looper looper) {
                super(looper);
            }
    
            @Override
            public void handleMessage(Message msg) {
                final JobParameters params = (JobParameters) msg.obj;
                switch (msg.what) {
                    case MSG_EXECUTE_JOB:
                        try {
                            boolean workOngoing = JobServiceEngine.this.onStartJob(params);
                            ackStartMessage(params, workOngoing);
                        } catch (Exception e) {
                            Log.e(TAG, "Error while executing job: " + params.getJobId());
                            throw new RuntimeException(e);
                        }
                        break;
                    case MSG_STOP_JOB:
                        try {
                            boolean ret = JobServiceEngine.this.onStopJob(params);
                            ackStopMessage(params, ret);
                        } catch (Exception e) {
                            Log.e(TAG, "Application unable to handle onStopJob.", e);
                            throw new RuntimeException(e);
                        }
                        break;
                    case MSG_JOB_FINISHED:
                        final boolean needsReschedule = (msg.arg2 == 1);
                        IJobCallback callback = params.getCallback();
                        if (callback != null) {
                            try {
                                callback.jobFinished(params.getJobId(), needsReschedule);
                            } catch (RemoteException e) {
                                Log.e(TAG, "Error reporting job finish to system: binder has gone" +
                                        "away.");
                            }
                        } else {
                            Log.e(TAG, "finishJob() called for a nonexistent job id.");
                        }
                        break;
                    default:
                        Log.e(TAG, "Unrecognised message received.");
                        break;
                }
            }
    

    boolean workOngoing = JobServiceEngine.this.onStartJob(params);这儿才是重中之重,真正回调到我们自定义的JobService中了。
    现在才真正执行到我们需要执行的onStartJob,这儿的逻辑有点混乱,为了便于理解,画一张时序图方便理解一下。


    JobScheduler调度流程.jpg

    最终会调度到JobServiceEngine中的onStartJob(...)方法,这个方法是一个抽象方法:

    JobServiceEngine.java
    
    public abstract boolean onStartJob(JobParameters params);
    public abstract boolean onStopJob(JobParameters params);
    

    实现这些抽象方法的地方就在JobService中:

    JobService.java
    
    public final IBinder onBind(Intent intent) {
            if (mEngine == null) {
                mEngine = new JobServiceEngine(this) {
                    @Override
                    public boolean onStartJob(JobParameters params) {
                        return JobService.this.onStartJob(params);
                    }
    
                    @Override
                    public boolean onStopJob(JobParameters params) {
                        return JobService.this.onStopJob(params);
                    }
                };
            }
            return mEngine.getBinder();
        }
    

    现在是不是一切都清楚了,Service绑定之后触发的回调就是onBind(...),然后我们在onBind(...)中实现JobServiceEngine抽象方法,这里直接调用到JobService中的抽象方法,而实现JobService抽象方法的地方又在我们自定义的JobService中,一切都连贯起来了。

    3.3 作业取消

    关于取消作业,有两种取消的函数,一个是根据jobId取消特定Job,还有是取消所有的Job。

    JobScheduler.java
        public abstract void cancel(int jobId);
        public abstract void cancelAll();
    

    下面描述一下cancel(int jobId)的执行流程:


    JobScheduler取消流程.jpg

    其实这两个函数的区别从函数的名称上就可以看出来,cancel(int jobId)是取消特定的JobId的作业,cancelAll()是取消当前的所有的job的。
    既然传入的JobId,那么肯定有根据JobId来查找对应的Job的流程。
    其实两个函数后面执行的流程是一样的,但是cancelAll()在前面多了一个流程:

    JobSchedulerService.java
    
    public void cancelJobsForUid(int uid, String reason) {
            if (uid == Process.SYSTEM_UID) {
                Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
                return;
            }
            synchronized (mLock) {
                final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
                for (int i=0; i<jobsForUid.size(); i++) {
                    JobStatus toRemove = jobsForUid.get(i);
                    cancelJobImplLocked(toRemove, null, reason);
                }
            }
        }
    

    cancelAll()是将当前uid的所有jobStatus取出来,然后分别执行cancel(int jobId)同样的操作。

    四、总结

    JobScheduler是Google提供的一个可以设置一些前置条件,来规定在特定条件下处理的任务,这样的好处可以简化代码的逻辑,使代码的设计思路变得简单。同时保证了批量处理任务,可以在一定程度上省电,用过JobScheduler的都知道,用它来进程保活非常高效。

    相关文章

      网友评论

          本文标题:任务调度JobScheduler源码剖析

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