Android进阶——更节电的后台任务JobScheduler 机制使用详解

引言

Android 5.0系统以后,Google为了提高使用流畅度以及延长电池续航,引入了在应用后台/锁屏时,系统会回收应用并自动销毁应用拉起的Service的机制。同时为了满足在特定条件下(比如网络、充电状态、电量、时间、周期等)触发执行某些任务的需求,于是乎JobScheduler 机制应运而生。总之,对于一定预定条件而触发的任务,JobScheduler是绝佳选择。

一、JobScheduler 机制概述

JobScheduler 机制中把每个需要后台的业务抽象为一个Job,通过系统管理Job,来提高资源的利用率和减少不必要的唤醒,从而提高性能,节省电源。当系统启动时会通过system_server进程启动**JobSchedulerService**服务,然后当使用该机制时,首先通过JobInfo构造具体的后台任务对象,并通过Jobscheduler 传入到后台任务调度器,当满足配置的条件时系统便会在对应的JobService上执行对应的作业。简而言之,系统提供了一种条件周期性执行的后台任务,无需开发者自己去唤醒,达到配置的条件便会自动执行。

二、JobSchedulerService 服务

The JobSchedulerService knows nothing about constraints, or the state of active jobs. It receives callbacks from the various controllers and completed jobs and operates accordingly.

从通过 (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE)的方式获取JobScheduler实例可以得知JobSchedulerService 也是以系统服务形式运行在后台,JobSchedulerService对Job的状态和约束都不了解,完全是通过各种controller的回调去处理各种Job。

1、触发JobSchedulerService 的启动

在com.android.server.SystemServer#startOtherServices方法里

 mSystemServiceManager.startService(JobSchedulerService.class);

SystemServiceManager启动所有系统核心服务的方式都大同小异,基本上都是首先根据传入的class字节码类型newInstance反射构造相关的对象,注册到系统服务列表后再触发其相应的onStart方法启动对应的服务。

com.android.server.SystemServiceManager#startService(java.lang.Class)

public <T extends SystemService> T startService(Class<T> serviceClass) {
     try {
         final String name = serviceClass.getName(); 
         final T service;
             Constructor<T> constructor = serviceClass.getConstructor(Context.class);
             service = constructor.newInstance(mContext);//传入SystemServiceManager的mContext 反射构造JobSchedulerService 对象
				...
         // {@ link ArrayList<SystemService> mServices}Register it.
         mServices.add(service);
         // Start it. 启动JobSchedulerService 
         service.onStart();
         return service;
     }
 }

2、JobSchedulerService 对象的构造

 public final class JobSchedulerService extends com.android.server.SystemService
        implements StateChangedListener, JobCompletedListener{
        ...
 public JobSchedulerService(Context context) {
        super(context);
        mHandler = new JobHandler(context.getMainLooper());//使用system_server进程中主线程的Looper初始化JobHandler
        mConstants = new Constants(mHandler);
        mJobSchedulerStub = new JobSchedulerStub();//创建对应Binder服务端
        mJobs = JobStore.initAndGet(this);

        // Create the controllers.
        mControllers = new ArrayList<StateController>();
        mControllers.add(ConnectivityController.get(this));//注册监听网络连接状态的广播
        mControllers.add(TimeController.get(this));//注册监听Job时间到期的广播
        mControllers.add(IdleController.get(this));//注册监听屏幕亮/灭,dream进入/退出,状态改变的广播
        mBatteryController = BatteryController.get(this);//注册监听电池是否充电,电量状态的广播
        mControllers.add(mBatteryController);
        mStorageController = StorageController.get(this);
        mControllers.add(mStorageController);
        mControllers.add(AppIdleController.get(this));//监听app是否空闲
        mControllers.add(ContentObserverController.get(this));//监听ContentObserver事件广播
        mControllers.add(DeviceIdleJobsController.get(this));//监听设备空闲广播
    }
     
         @Override
    public void onControllerStateChanged() {
        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    }

    @Override
    public void onRunJobNow(JobStatus jobStatus) {
        mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
    }
    @Override
    public void onStart() {
        publishLocalService(JobSchedulerInternal.class, new LocalService());
        publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
    }

JobSchedulerService 继承自SystemService类并实现了StateChangedListener、JobCompletedListener接口,构造方法执行时主要完成四件事。

2.1、使用system_server进程的主线程Looper初始化了JobHandler

该过程运行在主线程,因此不能做耗时操作。

//com.android.server.job.JobSchedulerService.JobHandler 
final private class JobHandler extends Handler {
        public JobHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message message) {
            synchronized (mLock) {
                if (!mReadyToRock) {//phase == PHASE_THIRD_PARTY_APPS_CAN_START 时mReadyToRock为true运行运行第三方App
                    return;
                }
                switch (message.what) {
                    case MSG_JOB_EXPIRED: {
							...
                    } break;
                    case MSG_CHECK_JOB:
                        if (mReportedActive) {
                            // if jobs are currently being run, queue all ready jobs for execution.
                            queueReadyJobsForExecutionLocked();
                        } else {
                            // Check the list of jobs and run some of them if we feel inclined.
                            maybeQueueReadyJobsForExecutionLocked();
                        }
                        break;
                    case MSG_CHECK_JOB_GREEDY:
                        queueReadyJobsForExecutionLocked();
                        break;
                    case MSG_STOP_JOB:
                        cancelJobImplLocked((JobStatus) message.obj, null,
                                "app no longer allowed to run");
                        break;
                }
                maybeRunPendingJobsLocked();
                // Don't remove JOB_EXPIRED in case one came along while processing the queue.
                removeMessages(MSG_CHECK_JOB);
            }
        }
    }

2.2、创建了JobSchedulerService 的对应Binder服务端

    /**
     * Binder stub trampoline implementation
     */
    final class JobSchedulerStub extends IJobScheduler.Stub {
      
        private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();

        // IJobScheduler implementation
        @Override
        public int schedule(JobInfo job) throws RemoteException {
			。。。
            try {
                return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, -1, null);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

        // IJobScheduler implementation
        @Override
        public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
			...
            long ident = Binder.clearCallingIdentity();
            try {
                return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, -1, null);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

        @Override
        public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
                throws RemoteException {
			...
            try {
                return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
                        packageName, userId, tag);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

        @Override
        public List<JobInfo> getAllPendingJobs() throws RemoteException {

            try {
                return JobSchedulerService.this.getPendingJobs(uid);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

        @Override
        public JobInfo getPendingJob(int jobId) throws RemoteException {
            final int uid = Binder.getCallingUid();
            long ident = Binder.clearCallingIdentity();
            try {
                return JobSchedulerService.this.getPendingJob(uid, jobId);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

        @Override
        public void cancelAll() throws RemoteException {
			...
            try {
                JobSchedulerService.this.cancelJobsForUid(uid, "cancelAll() called by app");
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

        @Override
        public void cancel(int jobId) throws RemoteException {
			...
            try {
                JobSchedulerService.this.cancelJob(uid, jobId);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    };

2.3、创建了持久化相关的JobStore

JobStore对象构造时会在创建/data/system/job/jobs.xml文件,同时可能之前已经存储过,还会解析XML文件创建JobInfo和,并转化为对应的JobStatus,最后把所有的JobStatus并保存到JobSet集合中,也是为什么JobScheduler可以持久化的原因。

JobStatus对象记录着任务的jobId, ComponentName, uid以及标签和失败次数信息。

  private JobStore(Context context, Object lock, File dataDir) {
        File systemDir = new File(dataDir, "system");
        File jobDir = new File(systemDir, "job");
        jobDir.mkdirs();
        mJobsFile = new AtomicFile(new File(jobDir, "jobs.xml"));
        mJobSet = new JobSet();
        readJobMapFromDisk(mJobSet);
    }

// frameworks/base/services/core/java/com/android/server/job/JobStore.java
/** Used by the {@link JobSchedulerService} to instantiate the JobStore. */
static JobStore initAndGet(JobSchedulerService jobManagerService) {
  synchronized (sSingletonLock) {
    if (sSingleton == null) {
      sSingleton = new JobStore(jobManagerService.getContext(),
                                jobManagerService.getLock(), 									Environment.getDataDirectory());
    }
    return sSingleton;
  }
}

2.4、创建和注册预置条件的监听器StateController

创建和注册预置条件的监听器,StateControler内部依然是通过广播实现的,监听到相应广播然后通知到监听者,当满足条件后,就会通过Handler 发送相应的消息触发任务执行。

StateControler类型说明
ConnectivityController注册监听网络连接状态的广播
TimeController注册监听job时间到期的广播
IdleController注册监听屏幕亮/灭,dream进入/退出,状态改变的广播
BatteryController注册监听电池是否充电,电量状态的广播
AppIdleController监听app是否空闲
ContentObserverController通过ContentObserver监测content URIs的变化
DeviceIdleJobsController根据doze状态为app设置约束。
public class ConnectivityController extends StateController implements
        ConnectivityManager.OnNetworkActiveListener {

    private final ConnectivityManager mConnManager;
    /** Singleton. */
    private static ConnectivityController mSingleton;
    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);//主动监听网络相关广播
    }
	...
    /**
     * Update all jobs tracked by this controller.
     * @param uid only update jobs belonging to this UID, or {@code -1} to update all tracked jobs.
     */
    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();
            }
        }
    }

    /**
     * We know the network has just come up. We want to run any jobs that are ready.
     */
    @Override
    public synchronized void onNetworkActive() {
        synchronized (mLock) {
            for (int i = 0; i < mTrackedJobs.size(); i++) {
                final JobStatus js = mTrackedJobs.get(i);
                if (js.isReady()) {
                    mStateChangedListener.onRunJobNow(js);
                }
            }
        }
    }
    private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            updateTrackedJobs(-1);
        }
    };
}

Android O以后禁止了一些广播的发送后,都是由这些Controller进行动态注册广播,由这些Controller触发相应的预置回调接口,从而转交给JobScheduler进行处理

/**
 * 是否正在充电
 */
public static boolean isCharging(Context context){
    //注册个包含充电状态的广播,并且是一个持续的广播
    IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
    Intent intent = context.registerReceiver(null,filter);
    //获取充电状态
    int isPlugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
    boolean acPlugged = isPlugged == BatteryManager.BATTERY_PLUGGED_AC;
    boolean usbPlugged = isPlugged == BatteryManager.BATTERY_PLUGGED_USB;
    boolean wifiPlugged = isPlugged == BatteryManager.BATTERY_PLUGGED_WIRELESS;
    return acPlugged || usbPlugged || wifiPlugged;
}

3、JobSchedulerService的真正启动

与其他系统服务一样,执行发布,这样其他应用就可以直接通过Binder使用这个服务的能力了。

    @Override
    public void onStart() {
        publishLocalService(JobSchedulerInternal.class, new LocalService());
        publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
    }

最后由SystemServiceRegistry的静态代码块中完成注册工作,可以看到当客户端请求获取JOB_SCHEDULER_SERVICE服务, 返回的是继承自JobScheduler 的JobSchedulerImpl实例。

        registerService(Context.JOB_SCHEDULER_SERVICE, JobScheduler.class,
                new StaticServiceFetcher<JobScheduler>() {
            @Override
            public JobScheduler createService() {
                IBinder b = ServiceManager.getService(Context.JOB_SCHEDULER_SERVICE);
                return new JobSchedulerImpl(IJobScheduler.Stub.asInterface(b));
            }});

至此JobSchedulerService 服务启动完成。

三、JobService

Entry point for the callback from the {@link android.app.job.JobScheduler}

抽象类JobService 继承自Service类,在JobScheduler监测到系统状态达到对应启动条件时,会启动JobService执行任务。所以我们需要继承JobService创建一个继承自JobService的Service,并必须实现两个方法:onStartJob(JobParameters params)onStopJob(JobParameters params)

public abstract class JobService extends Service {
    final JobHandler mHandler;
    final JobSchedulerStub mJobSchedulerStub;
    
    IJobService mBinder = new IJobService.Stub() {
        public void startJob(JobParameters jobParams) {
            ensureHandler();
            //向主线程的Handler发送MSG_EXECUTE_JOB消息
            Message m = Message.obtain(mHandler, MSG_EXECUTE_JOB, jobParams);
            m.sendToTarget();
        }
        public void stopJob(JobParameters jobParams) {
            ensureHandler();
            //向主线程的Handler发送MSG_STOP_JOB消息
            Message m = Message.obtain(mHandler, MSG_STOP_JOB, jobParams);
            m.sendToTarget();
        }
    };
    void ensureHandler() {
       synchronized (mHandlerLock) {
           if (mHandler == null) {
               mHandler = new JobHandler(getMainLooper());
           }
       }
   }
   public final IBinder onBind(Intent intent) {
      return mBinder.asBinder();
  }
	...
}

当JobService运行在App进程时,则mHandler即App进程的主线程关联的Handler。当分别向主线程发送消息启动和停止任务时,就会分别回调onStartJob和onStopJob方法。

1、boolean OnStartJob(JobParameters params)

当作业开始执行时会触发onStartJob(JobParameters params)方法(系统用来触发已经被执行的任务)并返回一个boolean值。若值为false,系统会认为在它返回时,任务已经执行完毕;而返回true,则系统任务这个任务正要被执行,因此当任务执行完毕时你需要调用jobFinished(JobParameters params, boolean needsRescheduled)来通知系统。

  • 如果返回值是false,该系统假定任何任务运行不需要很长时间并且到方法返回时已经完成。
  • **如果返回值是true,那么系统假设该任务是需要一些时间并且是需要在我们自己应用执行的。**当给定的任务完成时,需要通过调用jobFinished(JobParameters params, boolean needsRescheduled)方法来停止该任务告知系统该任务已经处理完成。

换言之,onStartJob方法在系统判定达到约束条件时被调用,我们可以在此处执行我们的业务逻辑

2、boolean OnStopJob(JobParameters params)

当系统接收到一个取消请求时,系统会触发onStopJob(JobParameters params)方法取消正在等待执行的任务也同样返回一个boolean值很重要的一点是如果onStartJob(JobParameters params)返回false,那么系统假定在接收到一个取消请求时已经没有正在运行的任务。换句话说,onStopJob(JobParameters params)在这种情况下不会被调用。当收到取消请求时,onStopJob(JobParameters params)是系统用来取消挂起的任务的。如果onStartJob(JobParameters params)返回 false,当取消请求被接收时,该系统假定没有目前运行的工作,它根本就不调用onStopJob(JobParameters params)。因此就需要我们手动调用jobFinished (JobParameters params, boolean needsReschedule)方法了。

要注意的是,onStartJob和onStopJob方法是运行在主线程中的,我们不可以在其中做耗时操作,否则可能导致ANR,可以使用另一个线程处理程序或运行时间更长的任务异步任务处理。因此通常在上面自定义的JobService类中创建一个Handler或者AsyncTask来处理需要进行的Job。

3、void jobFinished (JobParameters params, boolean needsReschedule)

回调通知已完成执行的JobManager,这可以从任何线程调用,因为它最终将在应用程序的主线程上运行。当系统收到该消息时,它将释放正在保存的唤醒。

  • params——传入的param需要和onStartJob中的param一致
  • needsReschedule——如果这项工作应按照计划时间指定的停止条件进行重新安排,则传入true。 否则的话传入false,让系统知道这个任务是否应该在最初的条件下被重复执行**

当任务完成时,需要调用jobFinished(JobParameters params, boolean needsRescheduled)让系统知道完成了哪项任务,它可以开始排队接下来的操作。如果不这样做,工作将只运行一次,应用程序将不被允许执行额外的工作

四、JobScheduler

This is an API for scheduling various types of jobs against the framework that will be executed in your application’s own process.

JobScheduler 从代码角度上来看是给我们开发者提供了一系列管理调度JobInfo的API,同时从另一个角度它还是一个Android的系统服务——JobSchedulerService相关的Binder对象。从上面得知JobScheduler 的实现类是JobSchedulerImpl,主要就是通过Binder 调用com.android.server.job.JobSchedulerService.JobSchedulerStub里的Binder远程接口方法。

方法参数说明
int schedule(JobInfo job)对应的任务信息把要执行的任务添加到调度集合中,如果传入的参数里携带的jobId已经存在则会覆盖旧ID的作业,而如果传入的jobId的作业当前正在运行,则会将其停止。如果schedule方法失败了,它会返回一个小于0的错误码。否则它会返回我们在JobInfo.Builder中定义的标识id。
void cancel(int jobId)jobId取消jobId对应的作业,如果任务当前正在执行,它会立即停止且它的Job Service#onStopjob (Job Parameters)方法的返回值将被忽略。
void cancelAll()\取消所有当前应用配置的Job,cancalAll()小心有坑,因为该方法的功能是取消该uid下的所有jobs,即当存在多个app通过shareUid的方式,那么在其中任意一个app执行cancalAll(),则会把所有同一uid下的app中的jobs都cancel掉
List< JobInfo > getAllPendingJobs()\获取所有当前应用配置的Job
JobInfo getPendingJob(int jobId)jobId获取jobId对应的Job
public class JobSchedulerImpl extends JobScheduler {
    IJobScheduler mBinder;

    /* package */ JobSchedulerImpl(IJobScheduler binder) {
        mBinder = binder;
    }

    @Override
    public int schedule(JobInfo job) {
        try {
            //{@link com.android.server.job.JobSchedulerService.JobSchedulerStub#schedule}
            return mBinder.schedule(job);
        } catch (RemoteException e) {
            return JobScheduler.RESULT_FAILURE;
        }
    }

    @Override
    public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag) {
        return mBinder.scheduleAsPackage(job, packageName, userId, tag);
    }

    @Override
    public void cancel(int jobId) {
        mBinder.cancel(jobId);
    }

    @Override
    public void cancelAll() {
       mBinder.cancelAll();
    }

    @Override
    public List<JobInfo> getAllPendingJobs() {
        return mBinder.getAllPendingJobs();
    }

    @Override
    public JobInfo getPendingJob(int jobId) {
        return mBinder.getPendingJob(jobId);
    }
}

1、JobSchedulerService.JobSchedulerStub#schedule

JobSchedulerImpl里通过mBinder调用schedule方法,然后传递到JobSchedulerService 服务端Binder对象JobSchedulerStub中

// IJobScheduler implementation
@Override
public int schedule(JobInfo job) throws RemoteException {
	return JobSchedulerService.this.schedule(job, uid);
}

在JobSchedulerStub里调用JobSchedulerService.this.schedule方法,

//@link com.android.server.job.JobSchedulerService.JobSchedulerStub#schedule
 public int schedule(JobInfo job, int uId) {
     return scheduleAsPackage(job, uId, null, -1, null);
 }


    public int scheduleAsPackage(JobInfo job, int uId, String packageName, int userId,
            String tag) {
        //创建JobStatus
        JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
        JobStatus toCancel;
        synchronized (mLock) {
            //先取消该uid下的任务
            toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
            if (toCancel != null) {
                cancelJobImpl(toCancel, jobStatus);
            }
            //开始追踪该任务
            startTrackingJob(jobStatus, toCancel);
        }
        //向system_server进程的主线程发送message
        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
        return JobScheduler.RESULT_SUCCESS;
    }

然后通过JobHandler处理Message消息,其他过程大同小异。

五、JobInfo & JobInfo.Builder

JobInfo是对任务主体信息的封装,比如说任务的执行条件、绑定的JobService类名、策略、重试策略、任务执行时间、是否持久化等等。通过构造者模式 JobInfo.Builder的构造JobInfo时需要传入一个jobId和绑定的JobService类名,其中jobId是Job的唯一标志,后续通过该jobId来取消Job

Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the parameters required to schedule work against the calling application.

JobInfo.Builder成员方法参数说明备注
addTriggerContentUri(JobInfo.TriggerContentUri uri)添加一个TriggerContentUri(原理是利用ContentObserver来监控一个Content Uri),当且仅当其发生变化时将触发任务的执行。为了持续监控Content的变化,需要在最近的任务触发后再调度一个新的任务(触发器URI不能与setPeriodic(long)setPersisted(boolean)组合使用。要持续监控内容更改,需要在完成JobService处理最近的更改之前,调度新的JobInfo监控相同的URI。因为设置此属性与定期或持久化Job不兼容,这样做会在调用build()时抛出IllegalArgumentException异常。)
setBackoffCriteria(long mills, int policy)mills表示第一次尝试重试的时间间隔,policy表示重试策略设置回退/重试的策略,类似网络原理中的冲突退避,当一个任务的调度失败时需要重试,所采取的策略预设的时间间隔有:DEFAULT_INITIAL_BACKOFF_MILLIS 30000MAX_BACKOFF_DELAY_MILLIS 18000000
而预设的策略有:BACKOFF_POLICY_EXPONENTIAL 二进制退避,等待间隔呈指数增长和BACKOFF_POLICY_LINEAR
setExtras(PersistableBundle extras)Job中附带的数据设置附带的额外数据,类似于Bundle的作用,是持久的,所以只允许原始类型。可以通过pendingJob.getExtras()获取
setMinimumLatency(long minLatencyMillis)设置任务的延迟执行时间(毫秒),相当于post delay。
setOverrideDeadline(long maxDeadlineMillis)设置任务最晚的延迟时间 。如果到了规定的时间时其他条件还未满足,任务也会被启动。
setPeriodic(long ms)设置任务运行的周期,即每X毫秒,运行一次
setPeriodic(long intervalMillis, long flexMillis)设置在Job周期末的一个flex长度的窗口,任务都有可能被执行。Andoid API 24及以上
setPersisted(boolean isPersisted)设置是否支持持久化,当设备重启之后系统根据值决定相应任务是否还要继续执行。
setRequiredNetworkType(int networkType)配置任务执行的网络条件NETWORK_TYPE_NONE——默认选择,不管是否有网络这个任务都会被执行
NETWORK_TYPE_ANY——需要任意一种网络才使得任务可以执行。
NETWORK_TYPE_UNMETERED——不是蜂窝网络( 比如在WIFI连接时 )时任务才会被执行
setRequiresCharging(boolean requiresCharging)设置设备在充电时这个任务才会被执行,这个也并非只是插入充电器,而且还要在电池处于健康状态的情况下才会触发,一般来说是手机电量>15%
setRequiresDeviceIdle(boolean isDeviceIdle)指定Job在空闲状态才能运行,设备处于屏幕关闭或dreaming状态(类似window的休眠动画状态)71分钟后,执行工作。
setTransientExtras(Bundle extras)设置作业可以携带的额外数据,类似Intent携带Bundle作用。Android API 26 及以上
setTriggerContentMaxDelay(long durationMs)设置从第一次检测到内容更改到Job之前允许的最大总延迟(以毫秒为单位),设置从Content变化到任务被执行,中间的最大延迟。Android API 24及以上
setTriggerContentMaxDelay(long durationMs)设置从第一次检测到内容更改到Job之前允许的最大总延迟(以毫秒为单位),设置从Content变化到任务被执行,中间的最大延迟。Android API 24及以上
setExtras(PersistableBundle extra)设置作业可以携带的额外数据,类似Intent携带Bundle作用。

六、JobScheduler的使用步骤

1、继承JobService 重写onStartJob和onStopJob方法实现定制的JobService

作业被启动后会调用onStartJob方法,而因为JobService运行在主线程,所以要使用子线程、Handler或者一个异步任务来运行耗时的操作以防止阻塞主线程。如果要执行耗时的操作,就需要创建一个线程去做;如果onStartJob执行的是不耗时的任务,就可以返回false,表示任务执行结束。如果onStartJob起了一个线程执行耗时任务,就要返回true,表示任务还在执行,需要等任务真正结束后手动调用JobFinished()方法告诉系统任务已经结束。

1.1、结合Handler形式处理耗时操作

public class JobServiceWithHandler  extends JobService {
    
    private Messenger mActivityMessenger;
     @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        mActivityMessenger = intent.getParcelableExtra(MESSENGER_INTENT_KEY);
        return START_NOT_STICKY;
    }
    
    private final Handler mJobHandler = new Handler( new Handler.Callback() {
        @Override
        public boolean handleMessage( Message msg ) {
            //TODO 业务操作
            //...
            jobFinished( (JobParameters) msg.obj, false );//任务执行完毕后通知
            return true;
        }
    } );

    @Override
    public boolean onStartJob(JobParameters params) {
        mJobHandler.sendMessage( Message.obtain( mJobHandler, 1, params ) );
        
        /* GOOGLE 官网demo 模拟的是通过Messenger 跨进程通信
         sendMessage(MSG_COLOR_START, params.getJobId());
        long duration = params.getExtras().getLong(WORK_DURATION_KEY);
        // Uses a handler to delay the execution of jobFinished().
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                sendMessage(MSG_COLOR_STOP, params.getJobId());
                jobFinished(params, false);
            }
        }, duration);*/

        
  /*      new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                //Do 。。。。
                jobFinished(params, false);
            }
        }, duration);
	*/
        //onStartJob返回true的时候,意味着耗时操作花费的事件比onStartJob执行的事件更长,并且意味着必须在合适时机手动调用jobFinished方法,否则该应用中的其他Job将不会被执行
        return true;
    }
    @Override
    public boolean onStopJob(JobParameters params) {
        mJobHandler.removeMessages( 1 );
        //sendMessage(MSG_COLOR_STOP, params.getJobId());
        // 当系统收到一个cancel job的请求时,并且这个Job仍然在执行(onStartJob返回true),系统就会调用onStopJob方法。 但不管是否调用onStopJob,系统只要收到取消请求,都会取消该Job
        // true 需要重试  false 不再重试 丢弃job
        return false;
    }
    
     private void sendMessage(int messageID, @Nullable Object params) {
        if (mActivityMessenger == null) {
            return;
        }
        Message m = Message.obtain();
        m.what = messageID;
        m.obj = params;
        mActivityMessenger.send(m);
    } 
}

需要到AndroidManifest.xml中添加一个service节点让你的应用拥有绑定和使用这个JobService的权限。

<service android:name=".JobServiceWithHandler"
    android:permission="android.permission.BIND_JOB_SERVICE" />

1.2、结合AsyncTask 处理耗时操作

public class JobServiceWithAsyncTask extends JobService {
    private JobParameters mJobParameters;
    private final AsyncTask<Void, Void, Void> mTask = new AsyncTask<Void, Void, Void>() {

        @Override
        protected Void doInBackground(Void... params) {
            // TODO 耗时操作
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            // TODO 耗时操作执行完毕后,告知系统
            jobFinished(mJobParameters, true);
            super.onPostExecute(result);
        }
    };

    @Override
    public boolean onStartJob(JobParameters params) {
        // 返回true,表示该工作耗时,同时工作处理完成后需要调用jobFinished销毁
        mJobParameters = params;
        mTask.execute();
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        return false;
    }
}

同样需要在清单文件中注册

<service android:name=".JobServiceWithAsyncTask"
    android:permission="android.permission.BIND_JOB_SERVICE" />

2、初始化获取JobScheduler对象实例

JobScheduler mJobScheduler = (JobScheduler) getSystemService( Context.JOB_SCHEDULER_SERVICE );

3、构建作业JobInfo对象预置触发条件绑定JobService

使用JobInfo.Builder来构建一个JobInfo对象并绑定定制的JobService。

// jobId :0
PersistableBundle extras = new PersistableBundle();
extras.putString("DATA","xxxx");
//创建一个job
JobInfo jobInfo = new
    JobInfo.Builder(0,new ComponentName(context, JobServiceWithHandler.class))
    //bInfo.Builder(0,new ComponentName(context, JobServiceWithAsyncTask.class))
    //只在充电的时候
    .setRequiresCharging(true)
    //不是蜂窝网络
    .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
    .setExtras(extras).build();

4、通过JobScheduler对象实例调度指定作业

  //提交任务
if( mJobScheduler.schedule( jobInfo) <= 0 ) {
   //If something goes wrong
}

提交了之后,就静待条件满足系统自动执行了,完。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CrazyMo_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值