【Android 电量优化】JobScheduler 相关源码分析 ( JobSchedulerService 源码分析 | 任务检查 | 任务执行 )

简介: 【Android 电量优化】JobScheduler 相关源码分析 ( JobSchedulerService 源码分析 | 任务检查 | 任务执行 )

文章目录

一、回调 StateChangedListener 接口

二、JobHandler 处理 ( 任务检查 )

三、maybeRunPendingJobsH 方法

四、assignJobsToContextsLocked 方法 ( 任务执行 )

五、JobSchedulerService 部分源码注释



推荐代码查看网站 :


https://www.androidos.net.cn/sourcecode ( 推荐 )


http://androidxref.com/






一、回调 StateChangedListener 接口


上一篇博客 【Android 电量优化】JobScheduler 相关源码分析 ( ConnectivityController 底层源码分析 | 构造函数 | 追踪任务更新 | 注册接收者监听连接变化 ) 中 ConnectivityController 最后调用了 mStateChangedListener 任务状态改变监听器接口的 onControllerStateChanged 方法 , 该接口实际上是 JobSchedulerService 类型的对象 ;



StateController 状态控制器创建时 , 会传入 mStateChangedListener , 该状态改变监听器就是 JobSchedulerService , 其实现了 StateChangedListener 接口 ; 如下代码中 , ConnectivityController 创建时 , 通过 get 方法设置了 JobSchedulerService 为状态监听器 ;


public final class JobSchedulerService extends com.android.server.SystemService
        implements StateChangedListener, JobCompletedListener {
    // ... 
    public JobSchedulerService(Context context) {
  // ...
        // 创建控制器集合
        mControllers = new ArrayList<StateController>();
        // 网络连接控制器
        mControllers.add(ConnectivityController.get(this));
        // ... 
    }
    // ...
}



在实现接口的 onControllerStateChanged 方法中 , 传递消息给 com.android.server.job.JobSchedulerService.JobHandler , 通知如下内容 : 一些控制器的状态发生了改变 , 以便去遍历集合并开启或停止相应的任务 ;


// 
public final class JobSchedulerService extends com.android.server.SystemService
        implements StateChangedListener, JobCompletedListener {
    // ...
    /**
     * 实现的 StateChangedListener 接口方法
     * 传递消息给 com.android.server.job.JobSchedulerService.JobHandler , 
     * 通知如下内容 : 一些控制器的状态发生了改变 , 以便去遍历集合并开启或停止相应的任务
     */
    @Override
    public void onControllerStateChanged() {
      // 发送了 Handler 信息
        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    }
    // ... 
}




二、JobHandler 处理 ( 任务检查 )


JobHandler 是定义在 JobSchedulerService 中的内部类 , 在该类中通过接收不同的 Message 信息 , 进行任务超时处理 , 任务检查 , 任务贪婪检查 , 任务停止 4 44 个操作 ;



构造函数 : 使用主线程的 context.getMainLooper() 作为参数 ;



处理消息 : 根据不同的消息的 what 标识 , 进行不同的任务处理 ;


MSG_JOB_EXPIRED : 处理超时任务 , 首先 获取任务状态 , 任务状态可能是空的 , 这是控制器表示其状态的一种方式 , 所有已准备的任务应该马上被执行 ;


MSG_CHECK_JOB : 检查任务 , 查看任务执行是否满足条件 , 如果满足就启动任务 ; 如果当前正在执行任务 , 将本次准备好了的任务放入待执行队列中准备执行 ; 反之如果当前没有执行任务 , 检查任务集合 , 如果合适运行其中的一些工任务 ;


MSG_CHECK_JOB_GREEDY : 贪婪检查任务 , 不管当前有没有正在执行任务 , 都将本次准备好了的任务放入待执行队列中准备执行 ;


MSG_STOP_JOB : 停止正在执行的任务 ;


上述操作都是针对任务队列的 ;



maybeRunPendingJobsH 方法是真正执行任务的核心逻辑 ;



public final class JobSchedulerService extends com.android.server.SystemService
        implements StateChangedListener, JobCompletedListener {
  // ...
    final JobHandler mHandler;
  // ... 
  // JobHandler 内部类
    private class JobHandler extends Handler {
  // 构造函数 , 使用主线程的 context.getMainLooper() 作为参数
        public JobHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message message) {
            synchronized (mLock) {
                if (!mReadyToRock) {
                    return;
                }
            }
            // 根据 message.what 处理消息
            switch (message.what) {
                case MSG_JOB_EXPIRED:
                  // 处理超时任务
                    synchronized (mLock) {
                      // 获取任务状态 
                        JobStatus runNow = (JobStatus) message.obj;
                        // runNow 任务状态可能是空的 , 
                        // 这是控制器表示其状态的一种方式 , 
                        // 所有已准备的任务应该马上被执行 ; 
                        if (runNow != null && !mPendingJobs.contains(runNow)
                                && mJobs.containsJob(runNow)) {
                            mJobPackageTracker.notePending(runNow);
                            mPendingJobs.add(runNow);
                        }
                        // 如果当前正在执行任务 , 将本次准备好了的任务放入待执行队列中准备执行
                        queueReadyJobsForExecutionLockedH();
                    }
                    break;
                case MSG_CHECK_JOB:
                  // 检查任务 
                    synchronized (mLock) {
                      // 查看任务执行是否满足条件 , 如果满足就启动任务 
                        if (mReportedActive) {
                            // 如果当前正在执行任务 , 将本次准备好了的任务放入待执行队列中准备执行
                            queueReadyJobsForExecutionLockedH();
                        } else {
                            // 检查任务集合 , 如果合适运行其中的一些工任务
                            maybeQueueReadyJobsForExecutionLockedH();
                        }
                    }
                    break;
                case MSG_CHECK_JOB_GREEDY:
                  // 贪婪的检查任务 , 直接将当前准备好的任务放入待执行队列中
                    synchronized (mLock) {
                        queueReadyJobsForExecutionLockedH();
                    }
                    break;
                case MSG_STOP_JOB:
                  // 停止任务 
                    cancelJobImpl((JobStatus)message.obj, null);
                    break;
            }
            // 这里是真正执行任务的核心逻辑
            maybeRunPendingJobsH();
            // 移除 MSG_CHECK_JOB 任务  
            // JOB_EXPIRED 异步任务不能移除 , 防止处理队列时 JOB_EXPIRED 类型消息到达
            removeMessages(MSG_CHECK_JOB);
        }
}



三、maybeRunPendingJobsH 方法


maybeRunPendingJobsH 方法中 , 根据可用的执行上下文 , 协调等待队列中的任务 ; 控制器可以强制将任务放入等待队列中 , 即使该任务已经在运行中 ; 在这里我们可以决定是否真正地执行该操作 ;



在 assignJobsToContextsLocked 方法中 , 启动任务 ;



public final class JobSchedulerService extends com.android.server.SystemService

       implements StateChangedListener, JobCompletedListener {

       // ...

       /**

        * 根据可用的执行上下文 , 协调等待队列中的任务 ;

        * 控制器可以强制将任务放入等待队列中 , 即使该任务已经在运行中 ;

        * 在这里我们可以决定是否真正地执行该操作 ;

        */

       private void maybeRunPendingJobsH() {

           synchronized (mLock) {

               if (DEBUG) {

                   Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");

               }

               // 在该函数中启动任务

               assignJobsToContextsLocked();

               reportActive();

           }

       }

       // ...

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20





四、assignJobsToContextsLocked 方法 ( 任务执行 )


assignJobsToContextsLocked 方法作用 : 从等待队列中获取任务 , 并在可用的上下文中执行它们 , 如果当前没有可用的上下文 , 执行高优先级任务 , 取代执行低优先级任务 ;



assignJobsToContextsLocked 方法代码逻辑 :


获取可执行任务数 : 获取内存等级 , 根据内存等级确定最大的激活任务数 , 不同的可用内存等级 , 有不同的任务数 , 内存容量高 , 可同时执行的任务多 ;


记录任务 : 使用 JobStatus[] contextIdToJobMap 记录可执行任务 ;


获取任务 : 开始遍历 mPendingJobs 待执行任务集合 , 如果获取到可执行任务 , 放入 contextIdToJobMap 集合中 ;


执行任务 : 遍历 contextIdToJobMap 集合 , 从该集合中取出可执行任务并执行 ;



执行任务方法 : 使用 mActiveServices.get(i).executeRunnableJob(pendingJob) 方法执行任务 , mActiveServices 集合元素类型是 JobServiceContext , 调用该 JobServiceContext 类对象的 executeRunnableJob 方法 , 传入 pendingJob 待执行任务 , 即可执行该任务 ;






五、JobSchedulerService 部分源码注释


public final class JobSchedulerService extends com.android.server.SystemService
        implements StateChangedListener, JobCompletedListener {
    /** 任务的主要集合. */
    final JobStore mJobs;
    final JobHandler mHandler;
    /**
     * 该数组实际存储了 mActiveServices 数组状态 .
     * 该数组第 i 个索引存储了第 i 个 JobServiceContext 的任务 . 
     * 我们会操作该数组 , 直到我们已经知道了哪些任务应该在哪些 JobServiceContext 上执行 .
     */
    JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
    /**
     * 追踪那些已经激活或者等待执行额任务对应的服务 . 
     * 对应的索引由 JobStatus.getServiceToken() 方法提供 .
     */
    final List<JobServiceContext> mActiveServices = new ArrayList<>();
    public JobSchedulerService(Context context) {
        super(context);
        // 创建 Handler 
        mHandler = new JobHandler(context.getMainLooper());
        // 创建控制器集合
        mControllers = new ArrayList<StateController>();
        // 网络连接控制器
        mControllers.add(ConnectivityController.get(this));
        // ... 
    }
    /**
     * 实现的 StateChangedListener 接口方法
     * 传递消息给 com.android.server.job.JobSchedulerService.JobHandler , 
     * 通知如下内容 : 一些控制器的状态发生了改变 , 以便去遍历集合并开启或停止相应的任务
     */
    @Override
    public void onControllerStateChanged() {
      // 发送了 Handler 信息
        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    }
  // JobHandler 内部类
    private class JobHandler extends Handler {
  // 构造函数 , 使用主线程的 context.getMainLooper() 作为参数
        public JobHandler(Looper looper) {
            super(looper);
        }
        @Override
        public void handleMessage(Message message) {
            synchronized (mLock) {
                if (!mReadyToRock) {
                    return;
                }
            }
            // 根据 message.what 处理消息
            switch (message.what) {
                case MSG_JOB_EXPIRED:
                  // 处理超时任务
                    synchronized (mLock) {
                      // 获取任务状态 
                        JobStatus runNow = (JobStatus) message.obj;
                        // runNow 任务状态可能是空的 , 
                        // 这是控制器表示其状态的一种方式 , 
                        // 所有已准备的任务应该马上被执行 ; 
                        if (runNow != null && !mPendingJobs.contains(runNow)
                                && mJobs.containsJob(runNow)) {
                            mJobPackageTracker.notePending(runNow);
                            mPendingJobs.add(runNow);
                        }
                        // 如果当前正在执行任务 , 将本次准备好了的任务放入待执行队列中准备执行
                        queueReadyJobsForExecutionLockedH();
                    }
                    break;
                case MSG_CHECK_JOB:
                  // 检查任务 
                    synchronized (mLock) {
                      // 查看任务执行是否满足条件 , 如果满足就启动任务 
                        if (mReportedActive) {
                            // 如果当前正在执行任务 , 将本次准备好了的任务放入待执行队列中准备执行
                            queueReadyJobsForExecutionLockedH();
                        } else {
                            // 检查任务集合 , 如果合适运行其中的一些工任务
                            maybeQueueReadyJobsForExecutionLockedH();
                        }
                    }
                    break;
                case MSG_CHECK_JOB_GREEDY:
                  // 贪婪的检查任务 , 直接将当前准备好的任务放入待执行队列中
                    synchronized (mLock) {
                        queueReadyJobsForExecutionLockedH();
                    }
                    break;
                case MSG_STOP_JOB:
                  // 停止任务 
                    cancelJobImpl((JobStatus)message.obj, null);
                    break;
            }
            // 这里是真正执行任务的核心逻辑
            maybeRunPendingJobsH();
            // 移除 MSG_CHECK_JOB 任务  
            // JOB_EXPIRED 异步任务不能移除 , 防止处理队列时 JOB_EXPIRED 类型消息到达
            removeMessages(MSG_CHECK_JOB);
        }
  // 设置状态变化 , 将满足条件的任务放入 mPendingJobs 集合中 
        private void maybeQueueReadyJobsForExecutionLockedH() {
            if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
            noteJobsNonpending(mPendingJobs);
            mPendingJobs.clear();
            mJobs.forEachJob(mMaybeQueueFunctor);
            mMaybeQueueFunctor.postProcess();
        }
        /**
         * 根据可用的执行上下文 , 协调等待队列中的任务 ; 
         * 控制器可以强制将任务放入等待队列中 , 即使该任务已经在运行中 ; 
         * 在这里我们可以决定是否真正地执行该操作 ; 
         */
        private void maybeRunPendingJobsH() {
            synchronized (mLock) {
                if (DEBUG) {
                    Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
                }
                // 在该函数中启动任务 
                assignJobsToContextsLocked();
                reportActive();
            }
        }
    /**
     * 从等待队列中获取任务 , 并在可用的上下文中执行它们 ; 
     * 如果当前没有可用的上下文 ; 
     * 这里的上下文指的是四大组件或者 Application ; 
     * 执行高优先级任务 , 取代执行低优先级任务 ; 
     */
    private void assignJobsToContextsLocked() {
        if (DEBUG) {
            Slog.d(TAG, printPendingQueue());
        }
  // 获取内存等级
        int memLevel;
        try {
            memLevel = ActivityManagerNative.getDefault().getMemoryTrimLevel();
        } catch (RemoteException e) {
            memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
        }
        // 根据内存等级确定最大的激活任务数 
        // 不同的可用内存等级 , 有不同的任务数 , 内存容量高 , 可同时执行的任务多 
        switch (memLevel) {
            case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
                mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
                break;
            case ProcessStats.ADJ_MEM_FACTOR_LOW:
                mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
                break;
            case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
                mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
                break;
            default:
                mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
                break;
        }
        // 用于记录可执行任务 , 
  JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
  // ... 
  // 开始遍历 mPendingJobs 待执行任务集合 
  // 启动任务 
        for (int i=0; i<mPendingJobs.size(); i++) {
          // 获取一个待执行任务 
            JobStatus nextPending = mPendingJobs.get(i);
            // 如果当前任务正在执行 , 处理下一个任务
            int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
            if (jobRunningContext != -1) {
                continue;
            }
    // 进行一系列判断 ... 
            if (minPriorityContextId != -1) {
              // 获取到可执行任务 , 放入 contextIdToJobMap 集合中
                contextIdToJobMap[minPriorityContextId] = nextPending;
                act[minPriorityContextId] = true;
                numActive++;
                if (priority >= JobInfo.PRIORITY_TOP_APP) {
                    numForeground++;
                }
            }
        }
        mJobPackageTracker.noteConcurrency(numActive, numForeground);
        // 遍历 contextIdToJobMap 集合 , 从该集合中取出可执行任务并执行
        for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
            boolean preservePreferredUid = false;
            if (act[i]) {
                JobStatus js = mActiveServices.get(i).getRunningJob();
                if (js != null) {
                    // ... 
                } else {
                  // 取出要执行的任务
                    final JobStatus pendingJob = contextIdToJobMap[i];
                    // ...
                    // executeRunnableJob 方法用于正式执行任务 
                    // mActiveServices 集合元素类型是 JobServiceContext 
                    // 调用的 executeRunnableJob 方法定义在 JobServiceContext 中 
                    if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
                        Slog.d(TAG, "Error executing " + pendingJob);
                    }
                    if (mPendingJobs.remove(pendingJob)) {
                        mJobPackageTracker.noteNonpending(pendingJob);
                    }
                }
            }
            if (!preservePreferredUid) {
                mActiveServices.get(i).clearPreferredUid();
            }
        }
    }
}



该代码路径为 /frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java , 点击链接可跳转查看完整源码 ;



本篇博客涉及到的源码 :


/frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java


目录
相关文章
|
1月前
|
Android开发
Android基于gradle task检查各个module之间资源文件冲突情况
Android基于gradle task检查各个module之间资源文件冲突情况
Android基于gradle task检查各个module之间资源文件冲突情况
|
3月前
|
移动开发 监控 前端开发
构建高效Android应用:从优化布局到提升性能
【7月更文挑战第60天】在移动开发领域,一个流畅且响应迅速的应用程序是用户留存的关键。针对Android平台,开发者面临的挑战包括多样化的设备兼容性和性能优化。本文将深入探讨如何通过改进布局设计、内存管理和多线程处理来构建高效的Android应用。我们将剖析布局优化的细节,并讨论最新的Android性能提升策略,以帮助开发者创建更快速、更流畅的用户体验。
67 10
|
1月前
|
Android开发
Android gradle task任务检查各个module之间资源文件冲突.md
Android gradle task任务检查各个module之间资源文件冲突.md
Android gradle task任务检查各个module之间资源文件冲突.md
|
2月前
|
存储 缓存 编解码
Android经典面试题之图片Bitmap怎么做优化
本文介绍了图片相关的内存优化方法,包括分辨率适配、图片压缩与缓存。文中详细讲解了如何根据不同分辨率放置图片资源,避免图片拉伸变形;并通过示例代码展示了使用`BitmapFactory.Options`进行图片压缩的具体步骤。此外,还介绍了Glide等第三方库如何利用LRU算法实现高效图片缓存。
67 20
Android经典面试题之图片Bitmap怎么做优化
|
1月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
49 4
|
21天前
|
安全 Android开发 iOS开发
深入探索iOS与Android系统的差异性及优化策略
在当今数字化时代,移动操作系统的竞争尤为激烈,其中iOS和Android作为市场上的两大巨头,各自拥有庞大的用户基础和独特的技术特点。本文旨在通过对比分析iOS与Android的核心差异,探讨各自的优势与局限,并提出针对性的优化策略,以期为用户提供更优质的使用体验和为开发者提供有价值的参考。
|
1月前
|
Android开发 Kotlin
Android面试题之Kotlin中如何实现串行和并行任务?
本文介绍了 Kotlin 中 `async` 和 `await` 在并发编程中的应用,包括并行与串行任务的处理方法。并通过示例代码展示了如何启动并收集异步任务的结果。
28 0
|
2月前
|
Java Android开发 UED
安卓应用开发中的内存管理优化技巧
在安卓开发的广阔天地里,内存管理是一块让开发者既爱又恨的领域。它如同一位严苛的考官,时刻考验着开发者的智慧与耐心。然而,只要我们掌握了正确的优化技巧,就能够驯服这位考官,让我们的应用在性能和用户体验上更上一层楼。本文将带你走进内存管理的迷宫,用通俗易懂的语言解读那些看似复杂的优化策略,让你的开发之路更加顺畅。
64 2
|
2月前
|
Java Android开发 开发者
安卓应用开发中的线程管理优化技巧
【9月更文挑战第10天】在安卓开发的海洋里,线程管理犹如航行的风帆,掌握好它,能让应用乘风破浪,反之则可能遭遇性能的暗礁。本文将通过浅显易懂的语言和生动的比喻,带你探索如何优雅地处理安卓中的线程问题,从基础的线程创建到高级的线程池运用,让你的应用运行更加流畅。
|
3月前
|
Ubuntu Android开发
安卓系统调试与优化:(一)bootchart 的配置和使用
本文介绍了如何在安卓系统中配置和使用bootchart工具来分析系统启动时间,包括安装工具、设备端启用bootchart、PC端解析数据及分析结果的详细步骤。
197 0
安卓系统调试与优化:(一)bootchart 的配置和使用