JobScheduler与JobService的使用

零. JobSchedule机制
请参考:http://gityuan.com/2017/03/10/job_scheduler_service/

在这里插入图片描述

简单理解:
JobScheduleService作为系统服务,负责job的调度.
JobScheduler作为JobScheduleService系统服务的本地应用代理
JobService作为本地应用的回调服务供JobScheduleService回调
JobInfo 作为job的信息描述实体. 为上述job的传递,存储,执行提供信息支持.

一.JobService是什么?
Entry point for the callback from the {@link android.app.job.JobScheduler}.

This is the base class that handles asynchronous requests that were previously scheduled. You are responsible for overriding {@link JobService#onStartJob(JobParameters)}, which is where you will implement your job logic.

This service executes each incoming job on a {@link android.os.Handler} running on your application's main thread. This means that you must offload your execution logic to another thread/handler/{@link android.os.AsyncTask} of your choosing. Not doing so will result in blocking any future callbacks from the JobManager - specifically {@link #onStopJob(android.app.job.JobParameters)}, which is meant to inform you that the scheduling requirements are no longer being met.

public abstract class JobService extends Service {
    private static final String TAG = "JobService";

    /**
     * Job services must be protected with this permission:
     *
     * <pre class="prettyprint">
     *     &#60;service android:name="MyJobService"
     *              android:permission="android.permission.BIND_JOB_SERVICE" &#62;
     *         ...
     *     &#60;/service&#62;
     * </pre>
     *
     * <p>If a job service is declared in the manifest but not protected with this
     * permission, that service will be ignored by the system.
     */
    public static final String PERMISSION_BIND =
            "android.permission.BIND_JOB_SERVICE";

    private JobServiceEngine mEngine;

    /** @hide */
    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();
    }

    /**
     * Call this to inform the JobScheduler that the job has finished its work.  When the
     * system receives this message, it releases the wakelock being held for the job.
     * <p>
     * You can request that the job be scheduled again by passing {@code true} as
     * the <code>wantsReschedule</code> parameter. This will apply back-off policy
     * for the job; this policy can be adjusted through the
     * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)} method
     * when the job is originally scheduled.  The job's initial
     * requirements are preserved when jobs are rescheduled, regardless of backed-off
     * policy.
     * <p class="note">
     * A job running while the device is dozing will not be rescheduled with the normal back-off
     * policy.  Instead, the job will be re-added to the queue and executed again during
     * a future idle maintenance window.
     * </p>
     *
     * @param params The parameters identifying this job, as supplied to
     *               the job in the {@link #onStartJob(JobParameters)} callback.
     * @param wantsReschedule {@code true} if this job should be rescheduled according
     *     to the back-off criteria specified when it was first scheduled; {@code false}
     *     otherwise.
     */
    public final void jobFinished(JobParameters params, boolean wantsReschedule) {
        mEngine.jobFinished(params, wantsReschedule);
    }

    /**
     * Called to indicate that the job has begun executing.  Override this method with the
     * logic for your job.  Like all other component lifecycle callbacks, this method executes
     * on your application's main thread.
     * <p>
     * Return {@code true} from this method if your job needs to continue running.  If you
     * do this, the job remains active until you call
     * {@link #jobFinished(JobParameters, boolean)} to tell the system that it has completed
     * its work, or until the job's recriteriaquired constraints are no longer satisfied.  For
     * example, if the job was scheduled using
     * {@link JobInfo.Builder#setRequiresCharging(boolean) setRequiresCharging(true)},
     * it will be immediately halted by the system if the user unplugs the device from power,
     * the job's {@link #onStopJob(JobParameters)} callback will be invoked, and the app
     * will be expected to shut down all ongoing work connected with that job.
     * <p>
     * The system holds a wakelock on behalf of your app as long as your job is executing.
     * This wakelock is acquired before this method is invoked, and is not released until either
     * you call {@link #jobFinished(JobParameters, boolean)}, or after the system invokes
     * {@link #onStopJob(JobParameters)} to notify your job that it is being shut down
     * prematurely.
     * <p>
     * Returning {@code false} from this method means your job is already finished.  The
     * system's wakelock for the job will be released, and {@link #onStopJob(JobParameters)}
     * will not be invoked.
     *
     * @param params Parameters specifying info about this job, including the optional
     *     extras configured with {@link JobInfo.Builder#setExtras(android.os.PersistableBundle).
     *     This object serves to identify this specific running job instance when calling
     *     {@link #jobFinished(JobParameters, boolean)}.
     * @return {@code true} if your service will continue running, using a separate thread
     *     when appropriate.  {@code false} means that this job has completed its work.
     */
    public abstract boolean onStartJob(JobParameters params);

    /**
     * This method is called if the system has determined that you must stop execution of your job
     * even before you've had a chance to call {@link #jobFinished(JobParameters, boolean)}.
     *
     * <p>This will happen if the requirements specified at schedule time are no longer met. For
     * example you may have requested WiFi with
     * {@link android.app.job.JobInfo.Builder#setRequiredNetworkType(int)}, yet while your
     * job was executing the user toggled WiFi. Another example is if you had specified
     * {@link android.app.job.JobInfo.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
     * idle maintenance window. You are solely responsible for the behavior of your application
     * upon receipt of this message; your app will likely start to misbehave if you ignore it.
     * <p>
     * Once this method returns, the system releases the wakelock that it is holding on
     * behalf of the job.</p>
     *
     * @param params The parameters identifying this job, as supplied to
     *               the job in the {@link #onStartJob(JobParameters)} callback.
     * @return {@code true} to indicate to the JobManager whether you'd like to reschedule
     * this job based on the retry criteria provided at job creation-time; or {@code false}
     * to end the job entirely.  Regardless of the value returned, your job must stop executing.
     */
    public abstract boolean onStopJob(JobParameters params);
}

当手机灭屏状态下保持一段时间后,系统会进入休眠,一些后台运行的任务就可能得不到正常执行,比如网络下载中断,后台播放音乐暂停等。WakeLock正是为了解决这类问题,应用只要申请了WakeLock,那么在释放WakeLock之前,系统不会进入休眠,即使在灭屏的状态下,应用要执行的任务依旧不会被系统打断。

二, JobScheduler是什么?
This is an API for scheduling various types of jobs against the framework that will be executed in your application’s own process.

See {@link android.app.job.JobInfo} for more description of the types of jobs that can be run
and how to construct them. You will construct these JobInfo objects and pass them to the
JobScheduler with {@link #schedule(JobInfo)}.
When the criteria declared are met, the
system will execute this job on your application’s {@link android.app.job.JobService}.
You identify the service component that implements the logic for your job when you
construct the JobInfo using
{@link android.app.job.JobInfo.Builder#JobInfo.Builder(int,android.content.ComponentName)}.

  • The framework will be intelligent about when it executes jobs, and attempt to batch
  • and defer them as much as possible. Typically if you don’t specify a deadline on a job, it
  • can be run at any moment depending on the current state of the JobScheduler’s internal queue.

* While a job is running, the system holds a wakelock on behalf of your app. For this reason, you do not need to take any action to guarantee that the device stays awake for the *duration of the job. *

You do not instantiate this class directly; instead, retrieve it through {@link android.content.Context#getSystemService Context.getSystemService(Context.JOB_SCHEDULER_SERVICE)}.

三, JobSchedulerService

JobScheduleService是系统处理Job的核心系统服务类,位于system_server进程;其在app进程常用的代理类为JobSchedule,(其中JobScheduleImpl实现JobSchedule).

1.下面是schedule的实际执行方法.

995    public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
996            int userId, String tag) {
997        try {
998            if (ActivityManager.getService().isAppStartModeDisabled(uId,
999                    job.getService().getPackageName())) {
1000                Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
1001                        + " -- package not allowed to start");
1002                return JobScheduler.RESULT_FAILURE;
1003            }
1004        } catch (RemoteException e) {
1005        }
1006
1007        synchronized (mLock) {
1008            final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
1009
1010            if (work != null && toCancel != null) {
1011                // Fast path: we are adding work to an existing job, and the JobInfo is not
1012                // changing.  We can just directly enqueue this work in to the job.
1013                if (toCancel.getJob().equals(job)) {
1014
1015                    toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
1016
1017                    // If any of work item is enqueued when the source is in the foreground,
1018                    // exempt the entire job.
1019                    toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
1020
1021                    return JobScheduler.RESULT_SUCCESS;
1022                }
1023            }
1024
1025            JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
1026
1027            // Give exemption if the source is in the foreground just now.
1028            // Note if it's a sync job, this method is called on the handler so it's not exactly
1029            // the state when requestSync() was called, but that should be fine because of the
1030            // 1 minute foreground grace period.
1031            jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);
1032
1033            if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
1034            // Jobs on behalf of others don't apply to the per-app job cap
1035            if (ENFORCE_MAX_JOBS && packageName == null) {
1036                if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
1037                    Slog.w(TAG, "Too many jobs for uid " + uId);
1038                    throw new IllegalStateException("Apps may not schedule more than "
1039                                + MAX_JOBS_PER_APP + " distinct jobs");
1040                }
1041            }
1042
1043            // This may throw a SecurityException.
1044            jobStatus.prepareLocked(ActivityManager.getService());
1045
1046            if (work != null) {
1047                // If work has been supplied, enqueue it into the new job.
1048                jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
1049            }
1050
1051            if (toCancel != null) {
1052                // Implicitly replaces the existing job record with the new instance
1053                cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
1054            } else {
1055                startTrackingJobLocked(jobStatus, null);
1056            }
1057            StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
1058                    uId, null, jobStatus.getBatteryName(),
1059                    StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
1060                    JobProtoEnums.STOP_REASON_CANCELLED, jobStatus.getStandbyBucket(),
1061                    jobStatus.getJobId());
1062
1063            // If the job is immediately ready to run, then we can just immediately
1064            // put it in the pending list and try to schedule it.  This is especially
1065            // important for jobs with a 0 deadline constraint, since they will happen a fair
1066            // amount, we want to handle them as quickly as possible, and semantically we want to
1067            // make sure we have started holding the wake lock for the job before returning to
1068            // the caller.
1069            // If the job is not yet ready to run, there is nothing more to do -- we are
1070            // now just waiting for one of its controllers to change state and schedule
1071            // the job appropriately.
1072            if (isReadyToBeExecutedLocked(jobStatus)) {
1073                // This is a new job, we can just immediately put it on the pending
1074                // list and try to run it.
1075                mJobPackageTracker.notePending(jobStatus);
1076                addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
1077                maybeRunPendingJobsLocked();
1078            } else {
1079                evaluateControllerStatesLocked(jobStatus);
1080            }
1081        }
1082        return JobScheduler.RESULT_SUCCESS;
1083    }

三.参考
1.https://developer.android.com/reference/android/app/job/JobService
1+.Android 功耗分析之wakelock
https://www.jianshu.com/p/67ccdac38271
1++.Android电源管理基础知识整理
https://www.cnblogs.com/linhaostudy/p/12119487.html
2.理解JobScheduler机制
http://gityuan.com/2017/03/10/job_scheduler_service/

3.深入理解JobScheduler与JobService的使用
https://www.jianshu.com/p/1f2103d3d2a2

4.后台任务 - 保持设备唤醒状态
https://www.jianshu.com/p/5db15ce7de1e

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值