JobSchedule之源码分析-第一讲
2015-10-15 14:02
288 查看
在上一篇介绍了JobSchedule的使用,但是毕竟这个只能在5.0以上的版本,才能使用,所以在我们这些做app开发的,实在是不那么好用,
所以想自己开发一下在Andriod 5.0一下版本使用的库,就想照着源码来写一下,但必须要先了解源码,才能着手工作,所以我们先来看看源码是如何工作的吧,
第一部分:
来看JobSchedule所监听的四个事件都是根据什么原理来进行的
1.空闲状态:IdleController.java
系统判断手机是否处于空闲状态的判断依据其实就是屏幕点亮和关闭,还有是否处于Dreaming状态,(就是类似于window休眠动画的那个)
所以注册的就是4个广播:
Step 1:
Step 2:
下面就是真正干活的地方了
Step 4
发送一个message,看看接受的位置
Step 7
13:JobServiceContext.java
Step 18
Step 21 :JobServiceContext.java
Step 22 :JobServiceContext.java
所以想自己开发一下在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了 }
相关文章推荐
- linux文件类型
- 为iPhone6设计自适应布局(二)
- 购物车动画
- MySQL 系列--安装配置
- URI参数签名算法【转载】
- Android中Textview显示带html文本三-------【Textview显示网络图片】
- 雷达扫描动画
- UINavigationController + UIScrollView组合,视图尺寸的设置探秘(二)
- jvm垃圾收集器与内存分配策略
- Echarts Map 值域为小数的原因
- 大写标识符
- Linux 中 CURL常用命令详解
- netfilter之NAT代码解读
- 你不知道的故事——被藏起来的婚鞋
- 你不知道的故事——被藏起来的婚鞋
- SOE传入参数过长出错,
- 为iPhone6设计自适应布局(一)
- 文章标题
- CSS3-2D转换
- linux下面的挂载点讲解