从JobSchedulerService开始探究学习安卓framework层

碎片化学习是个不好的习惯,要系统学习,建立自己的知识体系


一,简介

1. JobScheduler

      Google在Android 5.0中引入JobScheduler来执行一些需要满足特定条件但不紧急的后台任务,APP利用JobScheduler来执行这些特殊的后台任务时来减少电量的消耗。

2. JobScheduler 的出现是为了解决什么问题?

 Google 经过测试发现,每次唤醒设备,1-2秒的时候,都会消耗2分钟的待机电量,可见每次唤醒设备的时候,不仅仅是点亮了屏幕,系统也在后台处理很多事情。JobScheduler的作用是减少了设备唤醒的次数,从而减少了那些不必要的耗电
3.使用时注意的问题

 在你的任务完成的时候你需要结束你的job。因为JobScheduler为你的job持有了一个wake lock。如果你没有调用jobFinished(JobParameters params, boolean needsReschedule) 。JobScheduler将会为你的App持有一个wake lock,CPU一直处于唤醒状态,这会严重消耗设备的电量。

4.那些情况下可以考虑使用JobScheduler搭理我们的任务

Use the android.app.job.JobInfo.Builder to configure how the scheduled task should run. You can schedule the task to run under specific conditions, such as:

  • The device is charging//充电状态
  • The device is connected to an unmetered network//wifi网络
  • The system deems the device to be idle//空闲状态
  • Completion with a minimum delay or within a specific deadline.//定期任务
这些任务有一个很大的特点-时效性不强,例如上传统计打点,备份等等一些任务。
JobScheduler是如何做到省电的,可以参考知乎网友的解答:
https://www.zhihu.com/question/24360587


二.从源码角度分析JobScheduler


1.JobSchedulerService的启动
JobSchedulerService作为系统服务,在System_Server进程起来时启动它。

  JobSchedulerService启动过程


[systemServer.java]
systemServer进程在启动过程中会启动各种系统基础服务
 * The main entry point from zygote.
 */
public static void main(String[] args) {
    new SystemServer().run();
}

[systemServer.java]--- run()方法中调用了启动了各种系统服务
1.startBootstrapServices();
2.startCoreServices();
3. startOtherServices();

private void run() {
     ............................................
   Looper.prepareMainLooper();
    // Initialize native services.
    System.loadLibrary("android_servers");

    // Check whether we failed to shut down last time we tried.
    // This call may not return.
    performPendingShutdown();

    // Initialize the system context.
    createSystemContext();

    // Create the system service manager.
    mSystemServiceManager new SystemServiceManager(mSystemContext);
    LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);

    // Start services.
    try {
        startBootstrapServices();
        startCoreServices();
        startOtherServices();
    catch (Throwable ex) {
        Slog.e("System""******************************************");
        Slog.e("System""************ Failure starting system services"ex);
        throw ex;
    }

    // For debug builds, log event loop stalls to dropbox for analysis.
    if (StrictMode.conditionallyEnableDebugLogging()) {
        Slog.i(TAG"Enabled StrictMode for system server main thread.");
    }

    // Loop forever.
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}
       
[systemServer.java]--- startOtherServices():
启动
JobSchedulerService等其他服务,实际调用的是 [ SystemServiceManager.java]---startService
                                    

mSystemServiceManager.startService(JobSchedulerService.class);

[ SystemServiceManager.java]---startService:
SystemServiceManager 启动的服务是继承SystemService的服务
启动过程:1.创建服务 2.记录服务 3.回调onStart()

@SuppressWarnings("unchecked")
public <extends SystemService> startService(Class<T> serviceClass) {
    final String name = serviceClass.getName();
    Slog.i(TAG"Starting " + name);

    // Create the service.
    if (!SystemService.class.isAssignableFrom(serviceClass)) {
        throw new RuntimeException("Failed to create " + name
                + ": service must extend " + SystemService.class.getName());
    }
    final service;
    try {
        Constructor<T> constructor = serviceClass.getConstructor(Context.class);
        service = constructor.newInstance(mContext);
    catch (InstantiationException ex) {
        throw new RuntimeException("Failed to create service " + name
                + ": service could not be instantiated"ex);
    catch (IllegalAccessException ex) {
        throw new RuntimeException("Failed to create service " + name
                + ": service must have a public constructor with a Context argument"ex);
    catch (NoSuchMethodException ex) {
        throw new RuntimeException("Failed to create service " + name
                + ": service must have a public constructor with a Context argument"ex);
    catch (InvocationTargetException ex) {
        throw new RuntimeException("Failed to create service " + name
                + ": service constructor threw an exception"ex);
    }

    // Register it.
    mServices.add(service);

    // Start it.
    try {
        service.onStart();
    catch (RuntimeException ex) {
        throw new RuntimeException("Failed to start service " + name
                + ": onStart threw an exception"ex);
    }
    return service;
}

[ JobSchedulerService.java ]---onStart() :
调用父类[SystemService.java]---
publishBinderService()将 JobSchedulerService的IBinder对象 mJobSchedulerStub 发布出去,让其他服务或app可获取到。系统服务是公共的服务,为其他app提供服务

@Override
public void onStart() {
    publishBinderService(Context.JOB_SCHEDULER_SERVICEmJobSchedulerStub);
}

[SystemService.java]--- publishBinderService()
最终调用[ServiceManager.java]---addService

/**
 * Publish the service so it is accessible to other services and apps.
 */
protected final void publishBinderService(String nameIBinder service,
        boolean allowIsolated) {
    ServiceManager.addService(nameserviceallowIsolated)
;
}

2.JobSchedulerService的获取过程
系统服务的获取都是从上下文环境中获取。应用上下文是一个抽象的概念,后期会详细整理Context.

JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
获取JobScheduler服务实际是通过ContextImpl去拿系统服务的
[ContextImpl.java]---getSystemService()
@Override
public Object getSystemService(String name) {
    return SystemServiceRegistry.getSystemService(this, name);
}
[SystemServiceRegistry.java]---getSystemService()
看一下源码中SystemServiceRegistry类的解释,
/**
 * Manages all of the system services that can be returned by {@link Context#getSystemService}.
 * Used by {@link ContextImpl}.
 */
final class SystemServiceRegistry {

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

                       ..................
}

/**
 * Gets a system service from a given context.
 */
public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}

} 从源码中可以看到,在静态代码块中注册了JobSchedulerService,实质是把发布到ServiceManager中的JobScheduler binder对象获取到,传给JobSchedulerImpl构造函数,JobSchedulerImpl相当与我们常见的manager.所以虽然中间经过了一些包装代理,但通过getSystemService拿到的最终是IJobScheduler的binder对象。到这里我们已经获取到了JobSchedulerService对象.

3.应用端JobSchedulerService的使用过程
应用将自己的任务通过JobScheduler来打理需要经过以下步骤:
3.1 创建一个JobService的子类
类关系:XXXJobService extends JobService extends Service
该服务类跑在用户进程中,具体任务跑在主线程中,所以耗时的任务需另开一个线程运行。
3.2 定制一个Job
通过JobInfo.Builder构建一个具体任务,比如在充电状态,WIFI状态...
3.3 向系统注册任务
通过JobScheduler.schedule向系统注册任务

具体使用细节可在网上查找...
4.系统是如何调度这些任务的

还是从源码看起, JobScheduler.schedule(job)最终会调到JobSchedulerService中的schedule方法
// IJobScheduler implementation
@Override
public int schedule(JobInfo job) throws RemoteException {
    if (DEBUG) {
        Slog.d(TAG, "Scheduling job: " + job.toString());
    }
    final int pid = Binder.getCallingPid();
    final int uid = Binder.getCallingUid();

    enforceValidJobRequest(uid, job);//一些合法性的检查
    if (job.isPersisted()) {
        if (!canPersistJobs(pid, uid)) {
            throw new IllegalArgumentException("Error: requested job be persisted without"
                    + " holding RECEIVE_BOOT_COMPLETED permission.");//手机重启job是否有效权限检验
        }
    }

    long ident = Binder.clearCallingIdentity();
    try {
        return JobSchedulerService.this.schedule(job, uid);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}
schedule方法具体实现,应用程序将自己的job在这里交给系统调度执行
/**
 * Entry point from client to schedule the provided job.
 * This cancels the job if it's already been scheduled, and replaces it with the one provided.
 * @param job JobInfo object containing execution parameters//应用程序JobService中定义的job
 * @param uId The package identifier of the application this job is for.//该Job来自那个应用程序
 * @return Result of this operation. See JobScheduler#RESULT_* return codes.
 */
public int schedule(JobInfo job, int uId) {
    JobStatus jobStatus = new JobStatus(job, uId);
    cancelJob(uId, job.getId());
    try {
        if (ActivityManagerNative.getDefault().getAppStartMode(uId,
                job.getService().getPackageName()) == ActivityManager.APP_START_MODE_DISABLED) {
            Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()//该Job所在的package是否被disable掉
                    + " -- package not allowed to start");
            return JobScheduler.RESULT_FAILURE;
        }
    } catch (RemoteException e) {
    }
    startTrackingJob(jobStatus);
    mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    return JobScheduler.RESULT_SUCCESS;
}

以上方法中做了重要的三件事
step1:cancelJob(uId, job.getId());
如果任务已经在等待队列或已经在执行则从等待队列中移除或取消任务的执行(不详细展开,可以参考源码),为什么要移除暂时没有想明白
step2:startTrackingJob(jobStatus);

/**
 * Called when we have a job status object that we need to insert in our
 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
 * about.
 */
private void startTrackingJob(JobStatus jobStatus) {
    boolean update;
    boolean rocking;
    synchronized (mJobs) {
        update = mJobs.add(jobStatus);
        rocking = mReadyToRock;
    }
    if (rocking) {
        for (int i=0; i<mControllers.size(); i++) {
            StateController controller = mControllers.get(i);
            if (update) {
                controller.maybeStopTrackingJob(jobStatus);//如果当前job更换了以前的一个job,则停止追踪以前的job
            }
            controller.maybeStartTrackingJob(jobStatus);//继续追踪新的job
        }
    }
}
开始追踪该job,从源码中可以看到追踪job的实质是 循环获取所有控制器,并回调控制器的开始追踪方法。关于控制器在JobSchedulerService的构造中我们可以看到,在构造方法中初始化了所有的控制器,每个控制器对应创建job时的每个条件
比如:网络控制器---ConnectivityController,时间控制器---TimeController.
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);
}



step3:mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
从binder线程向主线程发送消息(主线程是System_server的主线程),检查手机当前环境下有没有可执行的job。
private class JobHandler extends Handler {

    public JobHandler(Looper looper) {
        super(looper);
    }

    @Override
    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();//检查是否有可执行的job
                }
                break;
            case MSG_STOP_JOB:
                cancelJobImpl((JobStatus)message.obj);
                break;
        }
        maybeRunPendingJobsH();
        // Don't remove JOB_EXPIRED in case one came along while processing the queue.
        removeMessages(MSG_CHECK_JOB);
    }
maybeRunPendingJobsH();该方法中才真正决定是否要执行job
    /**
     * Reconcile jobs in the pending queue against available execution contexts.
     * A controller can force a job into the pending queue even if it's already running, but
     * here is where we decide whether to actually execute it.
     */
    private void maybeRunPendingJobsH() {

                if (availableContext != null) {

                    if (!availableContext.executeRunnableJob(nextPending)) {
                        if (DEBUG) {
                            Slog.d(TAG, "Error executing " + nextPending);
                        }
                        mJobs.remove(nextPending);
                    }
                    it.remove();
                }
}
executeRunnableJob(nextPending)具体执行
boolean executeRunnableJob(JobStatus job) {

                        ....................
        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()));
                      ........................
 }
}
到这里最终通过mContext.bindServiceAsUser绑定了到了我们自定义的JobService,然后回调OnStartJob最终调度我们的方法执行

IJobService.aidl接口
oneway interface IJobService {
    /** Begin execution of application's job. */
    void startJob(in JobParameters jobParams);
    /** Stop execution of application's job. */
    void stopJob(in JobParameters jobParams);
}


总结一下:

我们定义一个继承JobService的子类,实现OnStartJob,OnStopJob方法,在这两个方法中实现我们的任务。在父类JobSevice中实现了IJobService.aidl接口,executeRunnableJob中会绑定我们自定义的服务,绑定成功之后会回调startJob方法,该方法在binder线程中通过handle发送一个消息,最终在主线程中回调我们定义的OnStartJob方法。

整个过程无处不体现着安卓进程间,线程间强大的通信机制。所以一定要搞明白binder,handler通信机制。




你可能感兴趣的:(it,Android,计算机)