JobScheduler的使用和原理

1、JobScheduler的使用

1.1 简介

JobScheduler主要用于在未来某个时间下满足一定条件时触发执行某项任务的情况,涉及的条件可以是网络、电量、时间等,例如执行特定的网络、是否只在充电时执行任务等。

1.2 相关API

1.2.1 JobScheduler

JobScheduler类负责将应用需要执行的任务发送给框架,以备对该应用Job的调度,是一个系统服务,可以通过如下方式获取:

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

1.2.2 JobInfo 及 JobInfo.Builder

JobInfo是传递给JobScheduler类的数据容器,它封装了针对调用应用程序调度任务所需的各种约束,也可以认为一个JobInfo对象对应一个任务,JobInfo对象通过JobInfo.Builder创建。它将作为参数传递给JobScheduler:

mJobScheduler.scheduler(mJobInfo);

JobInfo.Builder是JobInfo的一个内部类,用来创建JobInfo的Builder类。

JobInfo.Builder mBuilder = new JobInfo.Builder(id,new ComponentName(this, MyJobService.class));
mJobInfo = mBuilder.build();

1.2.3 JobService

JobService是JobScheduler最终回调的端点,JobScheduler将会回调该类中的onStartJob()开始执行异步任务。它是一个继承于JobService的抽象类,做为系统回调执行任务内容的终端,JobScheduler框架将通过bindService()方式来启动该服务。因此,用户必须在应用程序中创建一个JobService的子类,并实现其onStartJob()等回调方法,以及在AndroidManifest.xml中对它授予如下权限:


1.3 使用流程

1.3.1 创建一个JobService的子类,作为系统回调终端:

public class MyJobService extends JobService {

    @Override
    public boolean onStartJob(final JobParameters params) {
        //todo 执行任务
        return true;
    }
    
    @Override
    public boolean onStopJob(JobParameters params) {
        return false;//返回false表示停止后不再重试执行
    }
}

注意在AndroidManifest.xml中添加权限


当任务开始时会执行onStartJob(JobParameters params)方法,如果返回值是false,则系统认为这个方法返回时,任务已经执行完毕。如果返回值是true,那么系统认为这个任务正要被执行,执行任务的重担就落在了你的肩上。当任务执行完毕时你需要调用jobFinished(JobParameters params, boolean needsRescheduled)来通知系统。

当系统接收到一个取消请求时,系统会调用onStopJob(JobParameters params)方法取消正在等待执行的任务。很重要的一点是如果onStartJob(JobParameters params)返回false,那么系统假定在接收到一个取消请求时已经没有正在运行的任务。换句话说,onStopJob(JobParameters params)在这种情况下不会被调用。

需要注意的是这个Job Service运行在主线程,这意味着你需要使用子线程,handler,或者一个异步任务来运行耗时的操作以防止阻塞主线程。

1.3.2 创建JobInfo.Builder对象,为Job设置约束条件

private ComponentName mServiceComponent;
//根据JobService创建一个ComponentName对象
mServiceComponent = new ComponentName(this, MyJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(mJobId++, mServiceComponent);
builder.setMinimumLatency(1000);//设置延迟调度时间
builder.setOverrideDeadline(2000);//设置该Job截至时间,在截至时间前肯定会执行该Job
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);//设置所需网络类型
builder.setRequiresDeviceIdle(true);//设置在DeviceIdle时执行Job
builder.setRequiresCharging(true);//设置在充电时执行Job
builder.setExtras(extras);//设置一个额外的附加项
//...

1.3.3 获取JobScheduler实例

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

1.3.4 开始调度Job

mJobScheduler.schedule(builder.build());//调度Job
mJobScheduler.cancel(jobId);//取消特定Job
mJobScheduler.cancelAll();//取消应用所有的Job

具体的示例

Google官方的Sample:https://github.com/googlearchive/android-JobScheduler

2、JobScheduler的原理

2.1 JobScheduler如何执行

JobScheduler是一个抽象类,它在系统框架的实现类是android.app.JobSchedulerImpl

public class JobSchedulerImpl extends JobScheduler {
    IJobScheduler mBinder;

   JobSchedulerImpl(IJobScheduler binder) {
        mBinder = binder;
    }

    @Override
    public int schedule(JobInfo job) {
        try {
            return mBinder.schedule(job);
        } catch (RemoteException e) {
            return JobScheduler.RESULT_FAILURE;
        }
    }

    @Override
    public void cancel(int jobId) {
        try {
            mBinder.cancel(jobId);
        } catch (RemoteException e) {}

    }

    @Override
    public void cancelAll() {
        try {
            mBinder.cancelAll();
        } catch (RemoteException e) {}

    }

    @Override
    public List getAllPendingJobs() {
        try {
            return mBinder.getAllPendingJobs();
        } catch (RemoteException e) {
            return null;
        }
    }
}

执行的入口是JobScheduler.scheduler,其实是调了JobSchedulerImpl中的schedule方法;然后再调了mBinder.schedule(job)。这个mBinder就是JobSchedulerService,通过Binder跨进程调用JobSchedulerService。

最后调用到JobSchedulerService中的schedule方法:

public int schedule(JobInfo job, int uId) {
    JobStatus jobStatus = new JobStatus(job, uId);
    cancelJob(uId, job.getId());
    startTrackingJob(jobStatus);
     //通过handler发消息
    mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    return JobScheduler.RESULT_SUCCESS;
}

接着发送MSG_CHECK_JOB消息,消息处理的地方是

private class JobHandler extends Handler {
    ...
    @Override
    public void handleMessage(Message message) {
        ...
        switch (message.what) {
            ...
            case MSG_CHECK_JOB:
                synchronized (mJobs) {
                    //主要是遍历将来要处理的工作任务,然后一个个加到待处理工作任务集合中去
                    maybeQueueReadyJobsForExecutionLockedH();
                }
                break;
        }
        maybeRunPendingJobsH();
    }
    ...
}   

接着执行JobHandler中的maybeRunPendingJobsH方法,处理相应的任务

private void maybeRunPendingJobsH() {
    synchronized (mJobs) {
       ...
       if (!availableContext.executeRunnableJob(nextPending)) {
          ...        
       }
      ... 
}

availableContext是JobServiceContext,即ServiceConnection,这个是进程间通讯ServiceConnection,通过调用availableContext.executeRunnableJob(nextPending)方法,会触发调用onServiceConnected,看到这里应该明白了,onServiceConnected方法中的service就是Jobservice,里面还用了WakeLock锁,防止手机休眠。

public class JobServiceContext extends IJobCallback.Stub implements ServiceConnection {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        ...
        final PowerManager pm =
                (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, runningJob.getTag());
        mWakeLock.setWorkSource(new WorkSource(runningJob.getUid()));
        mWakeLock.setReferenceCounted(false);
        mWakeLock.acquire();
        mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
    }
}

接着,通过Handler发消息,调用了handleServiceBoundH()方法。

/** Start the job on the service. */
private void handleServiceBoundH() {
     ...
    try {
        mVerb = VERB_STARTING;
        scheduleOpTimeOut();
        service.startJob(mParams);
    } catch (RemoteException e) {
        ...
    }
}

从上面源码可以看出,最终是触发调用了JobService中的startJob方法。

2.2 JobInfo.Buidler的设置

builder.setMinimumLatency(1000);//设置延迟调度时间
builder.setOverrideDeadline(2000);//设置该Job截至时间,在截至时间前肯定会执行该Job
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);//设置所需网络类型
builder.setRequiresDeviceIdle(true);//设置在DeviceIdle时执行Job
builder.setRequiresCharging(true);//设置在充电时执行Job
builder.setExtras(extras);//设置一个额外的附加项

从源码看,设置的内容应用于JobStatus,例如网络限制

public class JobStatus {

    public boolean hasConnectivityConstraint() {
        return job.getNetworkType() == JobInfo.NETWORK_TYPE_ANY;
    }

    public boolean hasUnmeteredConstraint() {
        return job.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED;
    }
}

而在JobSchedulerService类,相关的状态控制在其构造函数里:

public JobSchedulerService(Context context) {
    super(context);
    // Create the controllers.
    mControllers = new ArrayList();
    mControllers.add(ConnectivityController.get(this));
    mControllers.add(TimeController.get(this));
    mControllers.add(IdleController.get(this));
    mControllers.add(BatteryController.get(this));
    mControllers.add(AppIdleController.get(this));

    mHandler = new JobHandler(context.getMainLooper());
    mJobSchedulerStub = new JobSchedulerStub();
    mJobs = JobStore.initAndGet(this);
}

例如网络控制类ConnectivityController类

public class ConnectivityController extends StateController implements
            ConnectivityManager.OnNetworkActiveListener {
    //工作任务状态集合
    private final List mTrackedJobs = new LinkedList();
    //这个是手机网络连接改变广播,网络发生改变,会触发这个广播
    private final BroadcastReceiver mConnectivityChangedReceiver =
        new ConnectivityChangedReceiver();
    /**
     * @param userId Id of the user for whom we are updating the connectivity state.
     */
    private void updateTrackedJobs(int userId) {
        ...
       if (changed) {
           mStateChangedListener.onControllerStateChanged();
       }
    }
   
    class ConnectivityChangedReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (DEBUG) {
                Slog.d(TAG, "Received connectivity event: " + intent.getAction() + " u"
                        + context.getUserId());
            }
            final String action = intent.getAction();
            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                final int networkType =
                        intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
                                ConnectivityManager.TYPE_NONE);
                // Connectivity manager for THIS context - important!
                final ConnectivityManager connManager = (ConnectivityManager)
                        context.getSystemService(Context.CONNECTIVITY_SERVICE);
                final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo();
                final int userid = context.getUserId();
                // This broadcast gets sent a lot, only update if the active network has changed.
                if (activeNetwork == null) {
                    mNetworkUnmetered = false;
                    mNetworkConnected = false;
                    updateTrackedJobs(userid);
                } else if (activeNetwork.getType() == networkType) {
                    mNetworkUnmetered = false;
                    mNetworkConnected = !intent.getBooleanExtra(
                            ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
                    if (mNetworkConnected) {  // No point making the call if we know there's no conn.
                        mNetworkUnmetered = !connManager.isActiveNetworkMetered();
                    }
                    updateTrackedJobs(userid);
                }
            } else {
                ...
            }
        }
    }       
}

当网络发生改变时,会调用updateTrackedJobs(userid)方法,在updateTrackedJobs方法中,会判断网络是否有改变,有改变的会调mStateChangedListener.onControllerStateChanged()方法;然后调用了JobSchedulerService类中onControllerStateChanged方法:

public class JobSchedulerService extends com.android.server.SystemService
        implements StateChangedListener, JobCompletedListener {      
    @Override
    public void onControllerStateChanged() {
        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    }             
}

接着也是处理MSG_CHECK_JOB 消息,和上文一样,最终触发调用了JobService中的startJob方法。

2.3 JobSchedulerService的启动

JobSchedulerService是一个系统服务,即应该在SystemServer启动的。阅读SystemServer的源码:

public final class SystemServer {
    private static final String TAG = "SystemServer";
    //手机开机启动后会走这个main方法,然后调用run方法
    public static void main(String[] args) {
        new SystemServer().run();
    }
}

run方法如下:

private void run() {
    ...
    // Start services.
    try {
        startBootstrapServices();
        startCoreServices();
        startOtherServices();
    } catch (Throwable ex) {
       ...
    }
}

接着看startOtherServices()

private void startOtherServices() {
    ...
    mSystemServiceManager.startService(JobSchedulerService.class);
    ...
}

因此,在这里就启动了JobSchedulerService服务。

引用

1.android 性能优化JobScheduler使用及源码分析

2.Android 9.0 JobScheduler(一) JobScheduler的使用

3.Android 9.0 JobScheduler(二) JobScheduler框架结构简述及JobSchedulerService的启动

4.Android 9.0 JobScheduler(三) 从Job的创建到执行

5.Android 9.0 JobScheduler(四) Job约束条件的控制

6.理解JobScheduler机制

你可能感兴趣的:(JobScheduler的使用和原理)