美文网首页Android 源码分析
JobSchedulerService 源码分析 -- JobS

JobSchedulerService 源码分析 -- JobS

作者: _夜 | 来源:发表于2017-12-06 17:00 被阅读0次

    一、JobServiceContext 通过 bindService 的方式启动 JobService

    1. JobServiceContext 中的 executeRunnableJob 执行 bindService

    boolean binding = mContext.bindServiceAsUser(intent, this, 
            Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
            new UserHandle(job.getUserId()));
    
    

    2. ActivityThread 调用 handleBindService() 回传 JobService 中的 IJobService mBinder

    public final class ActivityThread {
    
        private final void handleBindService(BindServiceData data) {
            ...
            try {
                if (!data.rebind) {
                    IBinder binder = s.onBind(data.intent);
                    ActivityManagerNative.getDefault().publishService(data.token, data.intent, binder);
                } else {
                }
            } catch (RemoteException ex) {
            }
        }
    }
    
    
    public final IBinder onBind(Intent intent) {
        return mBinder.asBinder();
    }
    
    

    3. JobService 中的 IJobService mBinder 代码

    /**
     * JobServiceContext 通过 bindService 的方式与 JobService 建立连接,
     * mBinder 会被传递到 JobServiceContext 的 onServiceConnected(ComponentName name, IBinder service) 中
     * JobServiceContext 通过 IBinder service 调用 IJobService 中的 startJob(), stopJob()
     */
    /** Binder for this service. */
    IJobService mBinder = new IJobService.Stub() {
        @Override
        public void startJob(JobParameters jobParams) {
            ensureHandler();
            Message m = Message.obtain(mHandler, MSG_EXECUTE_JOB, jobParams);
            m.sendToTarget();
        }
        @Override
        public void stopJob(JobParameters jobParams) {
            ensureHandler();
            Message m = Message.obtain(mHandler, MSG_STOP_JOB, jobParams);
            m.sendToTarget();
        }
    };
    
    

    4. JobServiceContext 在 onServiceConnected(ComponentName name, IBinder service) 中接收 IJobService mBinder

    /**
     * 建立与 JobService 的连接是通过 bindService 的方式
     * IBinder service 来自 JobService 中的 IJobService mBinder
     *
     * We acquire/release a wakelock on onServiceConnected/unbindService. This mirrors the work
     * we intend to send to the client - we stop sending work when the service is unbound so until
     * then we keep the wakelock.
     * @param name The concrete component name of the service that has been connected.
     * @param service The IBinder of the Service's communication channel,
     */
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        if (!name.equals(mRunningJob.getServiceComponent())) {
            mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
            return;
        }
        this.service = IJobService.Stub.asInterface(service);
        final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mRunningJob.getTag());
        mWakeLock.setWorkSource(new WorkSource(mRunningJob.getUid()));
        mWakeLock.setReferenceCounted(false);
        mWakeLock.acquire();
        // 发出与 JobService 成功建立连接的消息
        mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
    }
    
    

    二、JobServiceContext 处理 MSG_SERVICE_BOUND 消息

    1. 接收 MSG_SERVICE_BOUND 消息

    private class JobServiceHandler extends Handler {
        JobServiceHandler(Looper looper) {
            super(looper);
        }
    
        @Override
        public void handleMessage(Message message) {
            switch (message.what) {
                case MSG_SERVICE_BOUND:
                    removeOpTimeOut();
                    handleServiceBoundH();
                    break;
            }
        }
    }
    
    

    2. 跨进程调起 JobService 中的 IJobService 的 startJob(JobParameters jobParams)

    /** Start the job on the service. */
    private void handleServiceBoundH() {
        if (DEBUG) {
            Slog.d(TAG, "MSG_SERVICE_BOUND for " + mRunningJob.toShortString());
        }
        if (mVerb != VERB_BINDING) {// 刚完成 bingService 时,mVerb = VERB_BINDING;
            Slog.e(TAG, "Sending onStartJob for a job that isn't pending. " + VERB_STRINGS[mVerb]);
            closeAndCleanupJobH(false /* reschedule */);
            return;
        }
        if (mCancelled.get()) {
            if (DEBUG) {
                Slog.d(TAG, "Job cancelled while waiting for bind to complete. " + mRunningJob);
            }
            closeAndCleanupJobH(true /* reschedule */);
            return;
        }
        try {
            // 修改 mVerb 为 VERB_STARTING
            mVerb = VERB_STARTING;
            scheduleOpTimeOut();
            // 注意此处,调起 JobService 中的 startJob(JobParameters jobParams)
            service.startJob(mParams);
        } catch (RemoteException e) {
            Slog.e(TAG, "Error sending onStart message to '" + mRunningJob.getServiceComponent().getShortClassName() + "' ", e);
        }
    }
    
    

    三、跨进程调起 JobService 中的 IJobService 的 startJob(JobParameters jobParams)

    1. 发出 MSG_EXECUTE_JOB 消息

    /**
     * JobServiceContext 通过 bindService 的方式与 JobService 建立连接,
     * mBinder 会被传递到 JobServiceContext 的 onServiceConnected(ComponentName name, IBinder service) 中
     * JobServiceContext 通过 IBinder service 调用 IJobService 中的 startJob(), stopJob()
     */
    /** Binder for this service. */
    IJobService mBinder = new IJobService.Stub() {
        @Override
        public void startJob(JobParameters jobParams) {
            ensureHandler();
            Message m = Message.obtain(mHandler, MSG_EXECUTE_JOB, jobParams);
            m.sendToTarget();
        }
        @Override
        public void stopJob(JobParameters jobParams) {
            ensureHandler();
            Message m = Message.obtain(mHandler, MSG_STOP_JOB, jobParams);
            m.sendToTarget();
        }
    };
    
    

    2. 接收 MSG_EXECUTE_JOB 消息,调起 onStartJob(params)

    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 {
                        // 注意此处接收 onStartJob() 的返回值
                        boolean workOngoing = JobService.this.onStartJob(params);
                        ackStartMessage(params, workOngoing);
                    } catch (Exception e) {
                        Log.e(TAG, "Error while executing job: " + params.getJobId());
                        throw new RuntimeException(e);
                    }
                    break;
            }
        }
    }
    
    

    3. 跨进程回调至 JobServiceContext 中的 acknowledgeStartMessage()

    private void ackStartMessage(JobParameters params, boolean workOngoing) {
        final IJobCallback callback = params.getCallback();
        final int jobId = params.getJobId();
        if (callback != null) {
            try {
                // 跨进程调用,IJobCallback 对应于 JobServiceContext
                callback.acknowledgeStartMessage(jobId, workOngoing);
            } catch(RemoteException e) {
                Log.e(TAG, "System unreachable for starting job.");
            }
        } else {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "Attempting to ack a job that has already been processed.");
            }
        }
    }
    
    

    四、JobServiceContext 调用 acknowledgeStartMessage(int jobId, boolean ongoing)

    1. 发出消息 MSG_CALLBACK

    public void acknowledgeStartMessage(int jobId, boolean ongoing) {
        if (!verifyCallingUid()) {
            return;
        }
        mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, ongoing ? 1 : 0).sendToTarget();
    }
    
    

    2. 接收消息 MSG_CALLBACK

    private class JobServiceHandler extends Handler {
        JobServiceHandler(Looper looper) {
            super(looper);
        }
    
        @Override
        public void handleMessage(Message message) {
            switch (message.what) {
                case MSG_CALLBACK:
                    removeOpTimeOut();
    
                    if (mVerb == VERB_STARTING) {
                        // JobService.onStartJob(params) 后回调至这里
                        final boolean workOngoing = message.arg2 == 1;
                        handleStartedH(workOngoing);
                    }
                    break;
            }
        }
    }
    
    

    3. 根据 JobService 中的 onStartJob() 返回值做相应处理

    private void handleStartedH(boolean workOngoing) {
        switch (mVerb) {
            case VERB_STARTING:
                // 此处修改 mVerb 为 VERB_EXECUTING
                mVerb = VERB_EXECUTING;
                if (!workOngoing) {
                    // Job is finished already so fast-forward to handleFinished.
                    // unbindService(),把该 job 从 mJobs 中移除,同时会处理需要重新 schedule 或周期性的 Job
                    handleFinishedH(false);
                    return;
                }
    
                // JobService 中的 onStartJob() 返回true 时才会走下面逻辑
                if (mCancelled.get()) {
                    if (DEBUG) {
                        Slog.d(TAG, "Job cancelled while waiting for onStartJob to complete.");
                    }
                    // Cancelled *while* waiting for acknowledgeStartMessage from client.
                    // 这里会层层调用到  service.stopJob(mParams);
                    handleCancelH();
                    return;
                }
                // 注意这里,触发超时机制,1min 后若 APP 无反馈,则 job 状态改为 VERB_STOPPING
                // 并再次触发超时机制,若 1min 后仍然没反应,则走 job 处理完成流程
                // 对应 API24 来说,超时时间是 10min
                scheduleOpTimeOut();
                break;
            default:
                Slog.e(TAG, "Handling started job but job wasn't starting! Was "
                        + VERB_STRINGS[mVerb] + ".");
                return;
        }
    }
    
    

    五、JobService 在 onStartJob(JobParameters params) 返回 true 时需要调用 jobFinished(),(不调用也没关系,有超时机制)

    1. 发出 MSG_JOB_FINISHED 消息

    public final void jobFinished(JobParameters params, boolean needsReschedule) {
        ensureHandler();
        Message m = Message.obtain(mHandler, MSG_JOB_FINISHED, params);
        m.arg2 = needsReschedule ? 1 : 0;
        m.sendToTarget();
    }
    
    

    2. 处理 MSG_JOB_FINISHED 消息

    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_JOB_FINISHED:
                    final boolean needsReschedule = (msg.arg2 == 1);
                    IJobCallback callback = params.getCallback();
                    if (callback != null) {
                        try {
                            // 跨进程调用,IJobCallback 对应于 JobServiceContext
                            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;
            }
        }
    }
    
    

    3. 跨进程回调至 JobServiceContext

    public void jobFinished(int jobId, boolean reschedule) {
        if (!verifyCallingUid()) {
            return;
        }
        mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0).sendToTarget();
    }
    
    
    private class JobServiceHandler extends Handler {
        JobServiceHandler(Looper looper) {
            super(looper);
        }
    
        @Override
        public void handleMessage(Message message) {
            switch (message.what) {
                case MSG_CALLBACK:
                    if (DEBUG) {
                        Slog.d(TAG, "MSG_CALLBACK of : " + mRunningJob + " v:" + (mVerb >= 0 ? VERB_STRINGS[mVerb] : "[invalid]"));
                    }
                    removeOpTimeOut();
    
                    if (mVerb == VERB_STARTING) {
                        // JobService.onStartJob(params) 后回调至这里
                        final boolean workOngoing = message.arg2 == 1;
                        handleStartedH(workOngoing);
                    } else if (mVerb == VERB_EXECUTING || mVerb == VERB_STOPPING) {
                        // JobService.jobFinished() 会回调至这里
                        final boolean reschedule = message.arg2 == 1;
                        // unbindService(),把该 job 从 mJobs 中移除,同时会处理需要重新 schedule 或周期性的 Job
                        handleFinishedH(reschedule);
                    } else {
                        if (DEBUG) {
                            Slog.d(TAG, "Unrecognised callback: " + mRunningJob);
                        }
                    }
                    break;
            }
        }
    }
    
    
    private void handleFinishedH(boolean reschedule) {
        switch (mVerb) {
            case VERB_EXECUTING:
            case VERB_STOPPING:
                // unbindService(),把该 job 从 mJobs 中移除,同时会处理需要重新 schedule 或周期性的 Job
                closeAndCleanupJobH(reschedule);
                break;
            default:
                Slog.e(TAG, "Got an execution complete message for a job that wasn't being" + "executed. Was " + VERB_STRINGS[mVerb] + ".");
        }
    }
    
    
    /**
     * unbindService(),把该 job 从 mJobs 中移除,同时会处理需要重新 schedule 或周期性的 Job
     * The provided job has finished, either by calling
     * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
     * or from acknowledging the stop message we sent. Either way, we're done tracking it and
     * we want to clean up internally.
     */
    private void closeAndCleanupJobH(boolean reschedule) {
        final JobStatus completedJob = mRunningJob;
        synchronized (mLock) {
            try {
                mBatteryStats.noteJobFinish(mRunningJob.getName(), mRunningJob.getUid());
            } catch (RemoteException e) {
                // Whatever.
            }
            if (mWakeLock != null) {
                mWakeLock.release();
            }
            // 注意此处,调用 unbindService
            mContext.unbindService(JobServiceContext.this);
            mWakeLock = null;
            mRunningJob = null;
            mParams = null;
            mVerb = -1;
            mCancelled.set(false);
            service = null;
            mAvailable = true;
        }
        removeOpTimeOut();
        removeMessages(MSG_CALLBACK);
        removeMessages(MSG_SERVICE_BOUND);
        removeMessages(MSG_CANCEL);
        removeMessages(MSG_SHUTDOWN_EXECUTION);
        // onJobCompleted 中会把该 job 从 mJobs 中移除,同时会处理需要重新 schedule 或周期性的 Job
        mCompletedListener.onJobCompleted(completedJob, reschedule);
    }
    
    

    4. 调用 JobSchedulerService 中的 onJobCompleted()

    public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) {
        if (DEBUG) {
            Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
        }
        // 从 mJobs 及各 controller 中移除 job
        if (!stopTrackingJob(jobStatus)) {
            if (DEBUG) {
                Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
            }
            return;
        }
        if (needsReschedule) {
            // 处理设置了失败处理策略的 job
            JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
            startTrackingJob(rescheduled);
        } else if (jobStatus.getJob().isPeriodic()) {
            // 重新 tracking 周期性的 job
            JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
            startTrackingJob(rescheduledPeriodic);
        }
        // 检查所有满足执行条件的 Job,根据策略决定是否放入 mPendingJobs,随后执行 mPendingJobs 中的 Job
        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    }
    
    

    相关文章

      网友评论

        本文标题:JobSchedulerService 源码分析 -- JobS

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