JobSchedule之源码分析-第一讲

在上一篇介绍了JobSchedule的使用,但是毕竟这个只能在5.0以上的版本,才能使用,所以在我们这些做app开发的,实在是不那么好用,

所以想自己开发一下在Andriod 5.0一下版本使用的库,就想照着源码来写一下,但必须要先了解源码,才能着手工作,所以我们先来看看源码是如何工作的吧,

第一部分:

来看JobSchedule所监听的四个事件都是根据什么原理来进行的

1.空闲状态:IdleController.java

系统判断手机是否处于空闲状态的判断依据其实就是屏幕点亮和关闭,还有是否处于Dreaming状态,(就是类似于window休眠动画的那个)

所以注册的就是4个广播:


        public void startTracking() {
            IntentFilter filter = new IntentFilter();

            // Screen state
            filter.addAction(Intent.ACTION_SCREEN_ON);
            filter.addAction(Intent.ACTION_SCREEN_OFF);

            // Dreaming state
            filter.addAction(Intent.ACTION_DREAMING_STARTED);
            filter.addAction(Intent.ACTION_DREAMING_STOPPED);

            // Debugging/instrumentation
            filter.addAction(ACTION_TRIGGER_IDLE);

            mContext.registerReceiver(this, filter);
        }
所要做的就是根据广播来判断了,下面来看看如何操作的。

Step 1:

        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();

            if (action.equals(Intent.ACTION_SCREEN_ON)
                    || action.equals(Intent.ACTION_DREAMING_STOPPED)) {                                  //当屏幕点亮或者dreaming状态停止的时候
                // possible transition to not-idle
                if (mIdle) {<span style="white-space:pre">										//如果要是已经执行过了,则进入,所做的是退出,
                    if (DEBUG) {
                        Slog.v(TAG, "exiting idle : " + action);
                    }
                    mAlarm.cancel(mIdleTriggerIntent);                                                  //这个AlarmManager的作用就是定时发送,后面可以看到,这里做的是关闭屏幕71分钟之后,才作为Idle状态执行的,
                    mIdle = false;
                    reportNewIdleState(mIdle);
                }
            } else if (action.equals(Intent.ACTION_SCREEN_OFF)
                    || action.equals(Intent.ACTION_DREAMING_STARTED)) {
                // when the screen goes off or dreaming starts, we schedule the
                // alarm that will tell us when we have decided the device is
                // truly idle.
                final long nowElapsed = SystemClock.elapsedRealtime();
                final long when = nowElapsed + INACTIVITY_IDLE_THRESHOLD;
                if (DEBUG) {
                    Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when="
                            + when);
                }
                mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                        when, IDLE_WINDOW_SLOP, mIdleTriggerIntent);                                 //这个是根据开机时间来计算的,并且包括睡眠时间,时间到了,唤醒设备。
            } else if (action.equals(ACTION_TRIGGER_IDLE)) {
                // idle time starts now
                if (!mIdle) {                                                                        //当71分钟时间之后,没有执行过的话,开始执行,
                    if (DEBUG) {
                        Slog.v(TAG, "Idle trigger fired @ " + SystemClock.elapsedRealtime());
                    }
                    mIdle = true;
                    reportNewIdleState(mIdle);
                }
            }
        }
    }

Step 2:

下面就是真正干活的地方了

    void reportNewIdleState(boolean isIdle) {
        synchronized (mTrackedTasks) {                              //mTrackedTasks是在我们schedule的时候,注册进去的,有一点需要说明的是,在schedule的时候,我们注册进去的是JobInfo,现在的是JobStatus,这个包含前面的和uid,相当于封装,
            for (JobStatus task : mTrackedTasks) {
                task.idleConstraintSatisfied.set(isIdle);           //依次执行原子变量的设置
            }
        }
        mStateChangedListener.onControllerStateChanged();           //这里回调到Service中执行
    }
Step 3: JobSchedulerService.java

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

Step 4

发送一个message,看看接受的位置

        public void handleMessage(Message message) {
            synchronized (mJobs) {
                if (!mReadyToRock) {
                    return;
                }
            }
            switch (message.what) {
                case MSG_JOB_EXPIRED:
                    synchronized (mJobs) {
                        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)) {
                            mPendingJobs.add(runNow);
                        }
                        queueReadyJobsForExecutionLockedH();
                    }
                    break;
                case MSG_CHECK_JOB:
                    synchronized (mJobs) {
                        // Check the list of jobs and run some of them if we feel inclined.
                        maybeQueueReadyJobsForExecutionLockedH();                         //这个就是检查每个要执行的工作的状态,并且加到mPendingJobs

                    }
                    break;
            }
            maybeRunPendingJobsH();
            // Don't remove JOB_EXPIRED in case one came along while processing the queue.
            removeMessages(MSG_CHECK_JOB);
        }
Step 5
        private void maybeRunPendingJobsH() {
            synchronized (mJobs) {
                Iterator<JobStatus> it = mPendingJobs.iterator();                    //在上一步所有的都加到这个List中
                if (DEBUG) {
                    Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
                }
                while (it.hasNext()) {
                    JobStatus nextPending = it.next();
                    JobServiceContext availableContext = null;
                    for (int i=0; i<mActiveServices.size(); i++) {
                        JobServiceContext jsc = mActiveServices.get(i);
                        final JobStatus running = jsc.getRunningJob();
                        if (running != null && running.matches(nextPending.getUid(),
                                nextPending.getJobId())) {
                            // Already running this job for this uId, skip.
                            availableContext = null;
                            break;
                        }
                        if (jsc.isAvailable()) {
                            availableContext = jsc;
                        }
                    }
                    if (availableContext != null) {
                        if (!availableContext.executeRunnableJob(nextPending)) {                           //执行可运行的任务
                            if (DEBUG) {
                                Slog.d(TAG, "Error executing " + nextPending);
                            }
                            mJobs.remove(nextPending);
                        }
                        it.remove();
                    }
                }
            }
        }
    }
Step 6:JobServiceContext.java
    boolean executeRunnableJob(JobStatus job) {
        synchronized (mLock) {
            if (!mAvailable) {
                Slog.e(TAG, "Starting new runnable but context is unavailable > Error.");
                return false;
            }

            mRunningJob = job;
            mParams = new JobParameters(this, job.getJobId(), job.getExtras(),
                    !job.isConstraintsSatisfied());                              //这个是我们之前传入的,这里可以作为取出使用。
            mExecutionStartTimeElapsed = SystemClock.elapsedRealtime();

            mVerb = VERB_BINDING;
            scheduleOpTimeOut();                                                //这里是bind,规定在8s之内完成,否则取消当前任务,
            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()));                           //由于这里是bind,ServiceConnection是本身,所以如果成功,将会进入下一步
            if (!binding) {                                                     //这里是如果失败的话,
                if (DEBUG) {
                    Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable.");
                }
                mRunningJob = null;
                mParams = null;
                mExecutionStartTimeElapsed = 0L;
                removeOpTimeOut();
                return false;
            }
            try {
                mBatteryStats.noteJobStart(job.getName(), job.getUid());
            } catch (RemoteException e) {
                // Whatever.
            }
            mAvailable = false;
            return true;
        }
    }

Step 7
    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();
        mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
    }
Step 8
        public void handleMessage(Message message) {
            switch (message.what) {
                case MSG_SERVICE_BOUND:
                    removeOpTimeOut();                              //如果8秒之内的话,移除remove的callback
                    handleServiceBoundH();
                    break;
Step 9
        private void handleServiceBoundH() {

            if (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()) {                          //这个是一种容错处理,如果在binding过程中取消了的话,则不执行了
                if (DEBUG) {
                    Slog.d(TAG, "Job cancelled while waiting for bind to complete. "
                            + mRunningJob);
                }
                closeAndCleanupJobH(true /* reschedule */);
                return;
            }
            try {
                mVerb = VERB_STARTING;
                scheduleOpTimeOut();                        //同样startJob中不要做耗时操作。保证在8s之内完成。
                service.startJob(mParams);
            } catch (RemoteException e) {
                Slog.e(TAG, "Error sending onStart message to '" +
                        mRunningJob.getServiceComponent().getShortClassName() + "' ", e);
            }
        }
Step 10:JobService.java
    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();
        }
    };
Step 11
        @Override
        public void handleMessage(Message msg) {
            final JobParameters params = (JobParameters) msg.obj;
            switch (msg.what) {
                case MSG_EXECUTE_JOB:
                    try {
                        boolean workOngoing = JobService.this.onStartJob(params);                     //这里会回调我们的onStartJob方法。做我们的任务,
                        ackStartMessage(params, workOngoing);
                    } catch (Exception e) {
                        Log.e(TAG, "Error while executing job: " + params.getJobId());
                        throw new RuntimeException(e);
                    }
                    break;
Step 12
        private void ackStartMessage(JobParameters params, boolean workOngoing) {
            final IJobCallback callback = params.getCallback();
            final int jobId = params.getJobId();
            if (callback != null) {
                try {
                     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.");
                }
            }
        }
Step 13:JobServiceContext.java
    @Override
    public void acknowledgeStartMessage(int jobId, boolean ongoing) {
        if (!verifyCallingUid()) {
            return;
        }
        mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, ongoing ? 1 : 0).sendToTarget();
    }
Step 14
        public void handleMessage(Message message) {
            switch (message.what) {
                case MSG_CALLBACK:
                    removeOpTimeOut();

                    if (mVerb == VERB_STARTING) {                //状态就是这个
                        final boolean workOngoing = message.arg2 == 1;             //如果回调返回true
                        handleStartedH(workOngoing);
                    } else if (mVerb == VERB_EXECUTING ||
                            mVerb == VERB_STOPPING) {
                        final boolean reschedule = message.arg2 == 1;
                        handleFinishedH(reschedule);
                    } else {
                        if (DEBUG) {
                            Slog.d(TAG, "Unrecognised callback: " + mRunningJob);
                        }
                    }
                    break;
Step 15
        private void handleStartedH(boolean workOngoing) {
            switch (mVerb) {
                case VERB_STARTING:
                    mVerb = VERB_EXECUTING;                                         //状态切换
                    if (!workOngoing) {                                             //这个如果返回true就不会进去,直接进入scheduleOpTimeOut
                        // Job is finished already so fast-forward to handleFinished.
                        handleFinishedH(false);                                    //如果我们的回调onStartJob返回false执行,Step 21
                        return;
                    }
                    if (mCancelled.get()) {
                        if (DEBUG) {
                            Slog.d(TAG, "Job cancelled while waiting for onStartJob to complete.");
                        }
                        // Cancelled *while* waiting for acknowledgeStartMessage from client.
                        handleCancelH();
                        return;
                    }
                    scheduleOpTimeOut();                          //正常流程会走这里
                    break;
                default:
                    Slog.e(TAG, "Handling started job but job wasn't starting! Was "
                            + VERB_STRINGS[mVerb] + ".");
                    return;
            }
        }
Step 16
    private void scheduleOpTimeOut() {
        removeOpTimeOut();

        final long timeoutMillis = (mVerb == VERB_EXECUTING) ?
                EXECUTING_TIMESLICE_MILLIS : OP_TIMEOUT_MILLIS;                   这里60S之后会走MSG_TIMEOUT//所以从这里可以看出来,onStartJob和onStopJob中间间隔 一分钟时间
        if (DEBUG) {
            Slog.d(TAG, "Scheduling time out for '" +
                    mRunningJob.getServiceComponent().getShortClassName() + "' jId: " +
                    mParams.getJobId() + ", in " + (timeoutMillis / 1000) + " s");
        }
        Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT);
        mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
        mTimeoutElapsed = SystemClock.elapsedRealtime() + timeoutMillis;
    }
Step 17

        private void handleOpTimeoutH() {
            switch (mVerb) {
                case VERB_BINDING:
                    Slog.e(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
                            ", dropping.");
                    closeAndCleanupJobH(false /* needsReschedule */);
                    break;
                case VERB_STARTING:
                    // Client unresponsive - wedged or failed to respond in time. We don't really
                    // know what happened so let's log it and notify the JobScheduler
                    // FINISHED/NO-RETRY.
                    Slog.e(TAG, "No response from client for onStartJob '" +
                            mRunningJob.toShortString());
                    closeAndCleanupJobH(false /* needsReschedule */);
                    break;
                case VERB_STOPPING:
                    // At least we got somewhere, so fail but ask the JobScheduler to reschedule.
                    Slog.e(TAG, "No response from client for onStopJob, '" +
                            mRunningJob.toShortString());
                    closeAndCleanupJobH(true /* needsReschedule */);
                    break;
                case VERB_EXECUTING:
                    // Not an error - client ran out of time.
                    Slog.i(TAG, "Client timed out while executing (no jobFinished received)." +
                            " sending onStop. "  + mRunningJob.toShortString());
                    sendStopMessageH();                                            //发送停止消息,
                    break;
                default:
                    Slog.e(TAG, "Handling timeout for an invalid job state: " +
                            mRunningJob.toShortString() + ", dropping.");
                    closeAndCleanupJobH(false /* needsReschedule */);
            }
        }

Step 18

        private void sendStopMessageH() {
            removeOpTimeOut();
            if (mVerb != VERB_EXECUTING) {
                Slog.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob);
                closeAndCleanupJobH(false /* reschedule */);
                return;
            }
            try {
                mVerb = VERB_STOPPING;
                scheduleOpTimeOut();
                service.stopJob(mParams);
            } catch (RemoteException e) {
                Slog.e(TAG, "Error sending onStopJob to client.", e);
                closeAndCleanupJobH(false /* reschedule */);
            }
        }
Step 19:JobService.java
    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();
        }
    };
Step 20
        @Override
        public void handleMessage(Message msg) {
            final JobParameters params = (JobParameters) msg.obj;
            switch (msg.what) {
                case MSG_STOP_JOB:
                    try {
                        boolean ret = JobService.this.onStopJob(params);                  //回调我们的Stop方法
                        ackStopMessage(params, ret);                                      //这里就没做什么了
                    } catch (Exception e) {
                        Log.e(TAG, "Application unable to handle onStopJob.", e);
                        throw new RuntimeException(e);
                    }

Step 21 :JobServiceContext.java

        private void handleFinishedH(boolean reschedule) {
            switch (mVerb) {
                case VERB_EXECUTING:
                case VERB_STOPPING:
                    closeAndCleanupJobH(reschedule);                     //关闭,并且清理工作
                    break;
                default:
                    Slog.e(TAG, "Got an execution complete message for a job that wasn't being" +
                            "executed. Was " + VERB_STRINGS[mVerb] + ".");
            }
        }

Step 22 : JobServiceContext.java

        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();
                }
                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);
            mCompletedListener.onJobCompleted(completedJob, reschedule);
        }
    }
Step 23 : JobSchedulerService.java
    public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) {
        if (!stopTrackingJob(jobStatus)) {
            return;
        }
        if (needsReschedule) {
            JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
            startTrackingJob(rescheduled);
        } else if (jobStatus.getJob().isPeriodic()) {
            JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
            startTrackingJob(rescheduledPeriodic);
        }
        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();                //继续执行job,但不要以为还会走我们刚才走过的job,因为我们都已经remove了
    }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值