(八)JobSchedule 的使用以及源码分析

版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

一、概述

JobScheduler 是在 Android 21 新增加的特性,他可以把一些不是很紧急的任务,放到合适的时候进行批量处理,在电量优化中有很大的作用。

1.把任务整合起来,等到充电状态或者 Wifi 连接情况下进行执行,充电时候就不会在乎耗电量, Wifi 连接情况下比蜂窝网络省电。

2.在系统待机的时候,把任务整合到一起,一定时间批量处理,可以避免多次的唤醒 CPU,使 CPU 得不到休息,造成耗电。

二、JobSchedule 的使用

1.JobService

JobSchedule 使用起来相对比较简单,首先需要自己实现一个 JobService。该 Service 运行在主线程中,考虑到这个情况,简单的在代码中添加一个 AsyncTask 进行配合。

MyJobService :

public class MyJobService extends JobService {

    public static final String TAG = "MyJobService";

    @Override
    public boolean onStartJob(JobParameters params) {
        //如果返回值是 false, 这个方法返回时任务已经执行完毕。
        //如果返回值是 true, 那么这个任务正要被执行,我们就需要开始执行任务。
        //当任务执行完毕时你需要调用 jobFinished(JobParameters params, boolean needsRescheduled)来通知系统
        new MyAsyncTask().execute(params);
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        //这段注释转自 https://www.jianshu.com/p/1d4ebae39263,感觉作者写的很详细。
        //当系统接收到一个取消请求时,系统会调用 onStopJob 方法取消正在等待执行的任务。
        //其实 onStopJob 在 jobFinished 正常调用结束一个 job 时,也是不会调用的,
        //只有在该 job 没有被执行完,就被 cancel 掉的时候回调到,
        //比如某个 job 还没有执行就被 JobScheduler 给 Cancel 掉时,或者在某个运行条件不满足时。
        //比如原来在 Wifi 环境允许的某个任务,执行过程中切换到了非Wifi场景,那也会调用该方法。
        //该方法也返回一个 boolean 值,返回 true 表示会重新放到 JobScheduler 里 reScheduler,false 表示直接忽略。
        return false;
    }

    class MyAsyncTask extends AsyncTask<JobParameters, Void, Void> {
        JobParameters jobParameters;

        @Override
        protected Void doInBackground(JobParameters[] objects) {
            //做真正的处理
            jobParameters = objects[0];
            Log.i(TAG, jobParameters.getJobId() + " 任务开始执行......");
            PersistableBundle extras = jobParameters.getExtras();
            String location = extras.getString("data");
            Log.i(TAG, jobParameters.getJobId() + " 上传:"+location);

            return null;
        }

        @Override
        protected void onPostExecute(Void s) {
            //当任务执行完毕之后,需要调用 jobFinished 来让系统知道这个任务已经结束,
            //系统可以将下一个任务添加到队列中
            //true 表示需要重复执行
            //false 反之
            jobFinished(jobParameters, false);
            Log.i(TAG, jobParameters.getJobId() + "任务执行完成......");
        }
    }
}

2.JobInfo

JobInfo 的使用

       JobInfo jobInfo = new
                JobInfo.Builder(0,
                new ComponentName(context, MyJobService.class))
                //只在充电的时候
                .setRequiresCharging(true)
                //不是蜂窝网络
                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
                .setExtras(extras).build();

使用 JobInfo.Builder 来创建一个 JobInfo,这边主要介绍一下 JobInfo.Builder 的几个方法。

setExtras(@NonNull PersistableBundle extras)
数据传递,设置要传递的数据,可以在 Sercice 中获取到。

setRequiredNetworkType(@NetworkType int networkType)
设置 JobInfo 启动时的网络环境。

/** Default.
    默认,有没有网络都可以执行 */
public static final int NETWORK_TYPE_NONE = 0;

/** This job requires network connectivity.
    任何网络情况下都可以 */
public static final int NETWORK_TYPE_ANY = 1;

/** This job requires network connectivity that is unmetered.
    要求网络是非蜂窝网络 */
public static final int NETWORK_TYPE_UNMETERED = 2;

/** This job requires network connectivity that is not roaming. 
    要求网络是非漫游网络*/
public static final int NETWORK_TYPE_NOT_ROAMING = 3;

/** This job requires metered connectivity such as most cellular data networks.
    要求网络是蜂窝网络 */
public static final int NETWORK_TYPE_METERED = 4;

setRequiresCharging(boolean requiresCharging)
设置 JobInfo 启动是否需要在充电情况下,true 表示需要。

扫描二维码关注公众号,回复: 2670002 查看本文章

setMinimumLatency(long minLatencyMillis)
设置 JobInfo 任务延迟执行时间。

setOverrideDeadline(long maxExecutionDelayMillis)
设置 JobInfo 任务最长延迟时间。

setPeriodic(long intervalMillis, long flexMillis)
设置任务时间间隔,不能与 setMinimumLatency 和 setOverrideDeadline 一起使用。

setRequiresDeviceIdle(boolean requiresDeviceIdle)
设置任务是否需要机器空闲,true 表示需要

setPersisted(boolean isPersisted)
设置机器重启后是否继续执行, true 表示继续。

setBackoffCriteria(long initialBackoffMillis, @BackoffPolicy int backoffPolicy)
设置 JobInfo 的重试机制和时间策略。

initialBackoffMillis 默认为 30s,最长可以设置为 5 小时。

backoffPolicy 有两个值,

//线性增长
public static final int BACKOFF_POLICY_LINEAR = 0;
//指数增长
public static final int BACKOFF_POLICY_EXPONENTIAL = 1;

3.demo

public class JobManager {

    private static volatile JobManager sigleton = null;

    public static JobManager getInstance(){

        if (sigleton == null) {
            synchronized (JobManager.class) {
                if (sigleton == null) {
                    sigleton = new JobManager();
                }
            }
        }
        return sigleton;
    }

    private JobScheduler scheduler;
    private Context context;

    public void init(Context context) {
        this.context = context.getApplicationContext();
        scheduler = (JobScheduler)context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
    }

    public void addJob(String msg){

        if (scheduler == null) {
            return;
        }

        List<JobInfo> jobInfos = scheduler.getAllPendingJobs();

        JobInfo jobInfo = null;
        for (JobInfo info : jobInfos ) {
            if (info.getId() == 0) {
                jobInfo = info;
                break;
            }
        }
        //数据与 Intent 一样
        PersistableBundle extras = jobInfo.getExtras();
        String data = extras.getString("data");
        data += "," + msg;

        scheduler.cancel(0);

        extras.putString("data", data);
        jobInfo = new JobInfo.Builder(0, new ComponentName(context, MyJobService.class))
                .setRequiresCharging(true)      //只在充电状态下
                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) //  非蜂窝网络
                .setExtras(extras)
                .build();

        //提交任务
        scheduler.schedule(jobInfo);

    }

}

模拟定位信息收集,会把每次的定位信息进行拼接,直到符合执行 jobInfo 任务条件时,进行调用 MyJobService,在 MyJobService 中进行添加上送代码,实现上送。

三、JobSchedule 源码分析

1.JobSchedulerService

JobSchedule 依赖于 JobSchedulerService 的系统服务。JobScheduler 在提交任务时候,调用的是 schedule 这个方法,通过 bundle 机制,最终会调用到 JobSchedulerService 的 schedule 方法。

JobSchedulerService 的 schedule:

    public int schedule(JobInfo job, int uId) {
        return scheduleAsPackage(job, uId, null, -1, null);
    }

schedule 方法调用到了 scheduleAsPackage 方法,

JobSchedulerService 的 scheduleAsPackage :

    public int scheduleAsPackage(JobInfo job, int uId, String packageName, int userId,
            String tag) {
        JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
        try {
            if (ActivityManagerNative.getDefault().getAppStartMode(uId,
                    job.getService().getPackageName()) == ActivityManager.APP_START_MODE_DISABLED) {
                Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
                        + " -- package not allowed to start");
                return JobScheduler.RESULT_FAILURE;
            }
        } catch (RemoteException e) {
        }
        if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
        JobStatus toCancel;
        synchronized (mLock) {
            // Jobs on behalf of others don't apply to the per-app job cap
            if (ENFORCE_MAX_JOBS && packageName == null) {
                if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
                    Slog.w(TAG, "Too many jobs for uid " + uId);
                    throw new IllegalStateException("Apps may not schedule more than "
                                + MAX_JOBS_PER_APP + " distinct jobs");
                }
            }

            toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
            if (toCancel != null) {
                cancelJobImpl(toCancel, jobStatus);
            }
            startTrackingJob(jobStatus, toCancel);
        }
        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
        return JobScheduler.RESULT_SUCCESS;
    }

JobSchedulerService 的 scheduleAsPackage 创建了一个 JobStatus ,然后调用 startTrackingJob 这个方法。

JobSchedulerService 的 startTrackingJob:

    private void startTrackingJob(JobStatus jobStatus, JobStatus lastJob) {
        synchronized (mLock) {
            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);
                }
            }
        }
    }

startTrackingJob 中对一个全局变量 mControllers 进行遍历,我们先进行查看一下 mControllers 是什么东西。mControllers 的初始化,是在 JobSchedulerService 的构造函数中,可以很明显的发现,mControllers 里面存储的是网络、电池等控制器。

JobSchedulerService 的构造函数:

    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));
        mControllers.add(BatteryController.get(this));
        mControllers.add(AppIdleController.get(this));
        mControllers.add(ContentObserverController.get(this));
        mControllers.add(DeviceIdleJobsController.get(this));
    }

startTrackingJob 中遍历各个控制器,然后调用各个控制器的 maybeStartTrackingJobLocked 这个方法。

2.ConnectivityController

这边以 ConnectivityController 为例进行分析。我们查看 ConnectivityController 的 maybeStartTrackingJobLocked 方法。

ConnectivityController 的 maybeStartTrackingJobLocked:

    public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
        if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()
                || jobStatus.hasNotRoamingConstraint()) {
            updateConstraintsSatisfied(jobStatus);
            mTrackedJobs.add(jobStatus);
        }
    }

在 maybeStartTrackingJobLocked 方法里面就只有进行一个简单的吧 jobStatus 的状态添加到 mTrackedJobs集合中。

我们查看 ConnectivityController 的构造函数。

ConnectivityController 的构造函数:

    private ConnectivityController(StateChangedListener stateChangedListener, Context context,
            Object lock) {
        super(stateChangedListener, context, lock);

        mConnManager = mContext.getSystemService(ConnectivityManager.class);
        mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);

        final IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        mContext.registerReceiverAsUser(
                mConnectivityReceiver, UserHandle.SYSTEM, intentFilter, null, null);

        mNetPolicyManager.registerListener(mNetPolicyListener);
    }

ConnectivityController 在构造函数中进行了一个网络改变的广播接收者 mConnectivityReceiver 的注册。

    private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            updateTrackedJobs(-1);
        }
    };

当网络状态发生改变的时候,调用 updateTrackedJobs 方法。

ConnectivityController 的 updateTrackedJobs :

    private void updateTrackedJobs(int uid) {
        synchronized (mLock) {
            boolean changed = false;
            for (int i = 0; i < mTrackedJobs.size(); i++) {
                final JobStatus js = mTrackedJobs.get(i);
                if (uid == -1 || uid == js.getSourceUid()) {
                    changed |= updateConstraintsSatisfied(js);
                }
            }
            if (changed) {
                mStateChangedListener.onControllerStateChanged();
            }
        }
    }

updateTrackedJobs 的方法会调用的 mStateChangedListener 回调方法 onControllerStateChanged 进行回调。mStateChangedListener 是在构造函数中进行初始化,其实就是外部传递进来的 JobSchedulerService。

3.StateChangedListener

在 JobSchedulerService 的构造函数中我们可以发现, ConnectivityController 的创建是通过 ConnectivityController 的 get 方法进行获取,查看 get 方法。

* ConnectivityController 的 get :*

    public static ConnectivityController get(JobSchedulerService jms) {
        synchronized (sCreationLock) {
            if (mSingleton == null) {
                mSingleton = new ConnectivityController(jms, jms.getContext(), jms.getLock());
            }
            return mSingleton;
        }
    }

在 get 中直接把参数 jms 作为 ConnectivityController 构造函数中的 stateChangedListener。

**注:**JobSchedulerService 实现了 StateChangedListener 接口, 所以,网络改变最终会调用到 JobSchedulerService 实现的 StateChangedListener 下的 onControllerStateChanged 方法。

JobSchedulerService 的 onControllerStateChanged:

    public void onControllerStateChanged() {
        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    }

onControllerStateChanged 回调中就直接发送了一个 handle 的消息。mHandler 是一个 JobHandler,这个是 JobSchedulerService 的内部类。

JobHandler :

   private class JobHandler extends Handler {

        public JobHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message message) {
            synchronized (mLock) {
                if (!mReadyToRock) {
                    return;
                }
            }
            switch (message.what) {
                case MSG_JOB_EXPIRED:
                    synchronized (mLock) {
                        JobStatus runNow = (JobStatus) message.obj;
                        // runNow can be null, which is a controller's way of indicating that its
                        // state is such that all ready jobs should be run immediately.
                        if (runNow != null && !mPendingJobs.contains(runNow)
                                && mJobs.containsJob(runNow)) {
                            mJobPackageTracker.notePending(runNow);
                            mPendingJobs.add(runNow);
                        }
                        queueReadyJobsForExecutionLockedH();
                    }
                    break;
                case MSG_CHECK_JOB:
                    synchronized (mLock) {
                        if (mReportedActive) {
                            // if jobs are currently being run, queue all ready jobs for execution.
                            queueReadyJobsForExecutionLockedH();
                        } else {
                            // Check the list of jobs and run some of them if we feel inclined.
                            maybeQueueReadyJobsForExecutionLockedH();
                        }
                    }
                    break;
                case MSG_CHECK_JOB_GREEDY:
                    synchronized (mLock) {
                        queueReadyJobsForExecutionLockedH();
                    }
                    break;
                case MSG_STOP_JOB:
                    cancelJobImpl((JobStatus)message.obj, null);
                    break;
            }
            //真正执行任务
            maybeRunPendingJobsH();
            // Don't remove JOB_EXPIRED in case one came along while processing the queue.
            removeMessages(MSG_CHECK_JOB);
        }
        ...
    }

这个用法跟我们自己定义的 handle 一样,主要代码是在 case MSG_CHECK_JOB 下。

                case MSG_CHECK_JOB:
                    synchronized (mLock) {
                        if (mReportedActive) {
                            // if jobs are currently being run, queue all ready jobs for execution.
                            //如果当前有 job 正在运行,已经准备好的 job 将进行排队,等待执行。
                            queueReadyJobsForExecutionLockedH();
                        } else {
                            // Check the list of jobs and run some of them if we feel inclined.
                            //检查 jobs 的列表,如果满足条件,则执行。
                            maybeQueueReadyJobsForExecutionLockedH();
                        }
                    }
                    break;

没有 job 在执行的时候,走 else,调用 maybeQueueReadyJobsForExecutionLockedH,maybeQueueReadyJobsForExecutionLockedH 主要是进行状态的设置,以及把满足条件的广播添加到 mPendingJobs 中,

        private void maybeQueueReadyJobsForExecutionLockedH() {
            if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");

            noteJobsNonpending(mPendingJobs);
            //先进行清空
            mPendingJobs.clear();
            mJobs.forEachJob(mMaybeQueueFunctor);
            //在这里面添加
            mMaybeQueueFunctor.postProcess();
        }

JobHandler 真正执行任务是在方法末尾的 maybeRunPendingJobsH 这个内部方法中。

JobHandler 的 maybeRunPendingJobsH:

        private void maybeRunPendingJobsH() {
            synchronized (mLock) {
                if (DEBUG) {
                    Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
                }
                assignJobsToContextsLocked();
                reportActive();
            }
        }

主要内容是在 assignJobsToContextsLocked 里面。

assignJobsToContextsLocked:

    private void assignJobsToContextsLocked() {
        if (DEBUG) {
            Slog.d(TAG, printPendingQueue());
        }

        int memLevel;
        try {
            memLevel = ActivityManagerNative.getDefault().getMemoryTrimLevel();
        } catch (RemoteException e) {
            memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
        }
        switch (memLevel) {
            case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
                mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
                break;
            case ProcessStats.ADJ_MEM_FACTOR_LOW:
                mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
                break;
            case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
                mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
                break;
            default:
                mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
                break;
        }

        JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
        boolean[] act = mTmpAssignAct;
        int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
        int numActive = 0;
        int numForeground = 0;
        for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
            final JobServiceContext js = mActiveServices.get(i);
            final JobStatus status = js.getRunningJob();
            if ((contextIdToJobMap[i] = status) != null) {
                numActive++;
                if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
                    numForeground++;
                }
            }
            act[i] = false;
            preferredUidForContext[i] = js.getPreferredUid();
        }
        if (DEBUG) {
            Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
        }

        //这里开始遍历 mPendingJobs,也就是待执行的任务
        for (int i=0; i<mPendingJobs.size(); i++) {
            JobStatus nextPending = mPendingJobs.get(i);

            // If job is already running, go to next job.
            int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
            if (jobRunningContext != -1) {
                continue;
            }

            final int priority = evaluateJobPriorityLocked(nextPending);
            nextPending.lastEvaluatedPriority = priority;

            // Find a context for nextPending. The context should be available OR
            // it should have lowest priority among all running jobs
            // (sharing the same Uid as nextPending)
            int minPriority = Integer.MAX_VALUE;
            int minPriorityContextId = -1;
            for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
                JobStatus job = contextIdToJobMap[j];
                int preferredUid = preferredUidForContext[j];
                if (job == null) {
                    if ((numActive < mMaxActiveJobs ||
                            (priority >= JobInfo.PRIORITY_TOP_APP &&
                                    numForeground < mConstants.FG_JOB_COUNT)) &&
                            (preferredUid == nextPending.getUid() ||
                                    preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
                        // This slot is free, and we haven't yet hit the limit on
                        // concurrent jobs...  we can just throw the job in to here.
                        minPriorityContextId = j;
                        break;
                    }
                    // No job on this context, but nextPending can't run here because
                    // the context has a preferred Uid or we have reached the limit on
                    // concurrent jobs.
                    continue;
                }
                if (job.getUid() != nextPending.getUid()) {
                    continue;
                }
                if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
                    continue;
                }
                if (minPriority > nextPending.lastEvaluatedPriority) {
                    minPriority = nextPending.lastEvaluatedPriority;
                    minPriorityContextId = j;
                }
            }
            if (minPriorityContextId != -1) {
                contextIdToJobMap[minPriorityContextId] = nextPending;
                act[minPriorityContextId] = true;
                numActive++;
                if (priority >= JobInfo.PRIORITY_TOP_APP) {
                    numForeground++;
                }
            }
        }
        if (DEBUG) {
            Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
        }
        mJobPackageTracker.noteConcurrency(numActive, numForeground);
        for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
            boolean preservePreferredUid = false;
            if (act[i]) {
                JobStatus js = mActiveServices.get(i).getRunningJob();
                if (js != null) {
                    if (DEBUG) {
                        Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob());
                    }
                    // preferredUid will be set to uid of currently running job.
                    mActiveServices.get(i).preemptExecutingJob();
                    preservePreferredUid = true;
                } else {
                    final JobStatus pendingJob = contextIdToJobMap[i];
                    if (DEBUG) {
                        Slog.d(TAG, "About to run job on context "
                                + String.valueOf(i) + ", job: " + pendingJob);
                    }
                    for (int ic=0; ic<mControllers.size(); ic++) {
                        mControllers.get(ic).prepareForExecutionLocked(pendingJob);
                    }
                    //真正的执行任务
                    if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
                        Slog.d(TAG, "Error executing " + pendingJob);
                    }
                    if (mPendingJobs.remove(pendingJob)) {
                        mJobPackageTracker.noteNonpending(pendingJob);
                    }
                }
            }
            if (!preservePreferredUid) {
                mActiveServices.get(i).clearPreferredUid();
            }
        }
    }

真正执行任务是在 mActiveServices.get(i).executeRunnableJob(pendingJob) 里面,mActiveServices 是 List < JobServiceContext > ,我们继续查看 JobServiceContext 的代码。

4.JobServiceContext

JobServiceContext 的 executeRunnableJob:

  boolean executeRunnableJob(JobStatus job) {
        synchronized (mLock) {
            if (!mAvailable) {
                Slog.e(TAG, "Starting new runnable but context is unavailable > Error.");
                return false;
            }

            mPreferredUid = NO_PREFERRED_UID;

            mRunningJob = job;
            final boolean isDeadlineExpired =
                    job.hasDeadlineConstraint() &&
                            (job.getLatestRunTimeElapsed() < SystemClock.elapsedRealtime());
            Uri[] triggeredUris = null;
            if (job.changedUris != null) {
                triggeredUris = new Uri[job.changedUris.size()];
                job.changedUris.toArray(triggeredUris);
            }
            String[] triggeredAuthorities = null;
            if (job.changedAuthorities != null) {
                triggeredAuthorities = new String[job.changedAuthorities.size()];
                job.changedAuthorities.toArray(triggeredAuthorities);
            }
            mParams = new JobParameters(this, job.getJobId(), job.getExtras(), isDeadlineExpired,
                    triggeredUris, triggeredAuthorities);
            mExecutionStartTimeElapsed = SystemClock.elapsedRealtime();

            mVerb = VERB_BINDING;
            scheduleOpTimeOut();
            //创建一个 intent ,设置的 Component 是我们在创建 JobInfo 的时候传递的 Component。
            final Intent intent = new Intent().setComponent(job.getServiceComponent());
            //启动服务,就是我们设置的服务
            boolean binding = mContext.bindServiceAsUser(intent, this,
                    Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
                    new UserHandle(job.getUserId()));
            if (!binding) {
                if (DEBUG) {
                    Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable.");
                }
                mRunningJob = null;
                mParams = null;
                mExecutionStartTimeElapsed = 0L;
                mVerb = VERB_FINISHED;
                removeOpTimeOut();
                return false;
            }
            try {
                mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
            } catch (RemoteException e) {
                // Whatever.
            }
            mJobPackageTracker.noteActive(job);
            mAvailable = false;
            return true;
        }
    }

在 executeRunnableJob 里面创建 Intent,设置 Component 是我们在创建 JobInfo 的时候传递的 Component。然后进行绑定我们最初创建的 JobService。

总结:其实没有什么较复杂的内容,就是监听了一个网络改变的广播(或者其他的),当发现网络改变的时候,满足我们的条件,就会启动设置的 JobService。

猜你喜欢

转载自blog.csdn.net/qq_18983205/article/details/81390389