任务调度JobScheduler源码剖析

一、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,它们之间简单的关系如下:


任务调度JobScheduler源码剖析_第1张图片
作业调度结构图.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() {
            @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


任务调度JobScheduler源码剖析_第2张图片
作业调度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();
        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相关的属性,会被更新到本地。



    
        
        
        
    
    
        
        
        
    

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

其中核心函数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 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源码剖析_第3张图片
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源码剖析_第4张图片
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 jobsForUid = mJobs.getJobsByUid(uid);
            for (int i=0; i

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

四、总结

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

你可能感兴趣的:(任务调度JobScheduler源码剖析)