热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JobScheduler5.0源码分析

0.JobScheduler执行代码mJobScheduler(JobScheduler)getSystemService(Context.JOB_SCHEDULER_SERVICE);




0.JobScheduler执行代码
mJobScheduler=(JobScheduler)getSystemService(Context.JOB_SCHEDULER_SERVICE);
mJobInfo=new JobInfo.Builder(i,mComponentName).setMinimumLatency(5*1000).build();
mJobScheduler.schedule(mJobInfo);

这里写图片描述


1.创建服务

1.1 SystemServer代码



上图提到,我们可以从zygote进程孵化一个新的系统服务进程,也叫做SystemServer。这里我们可以先找到main函数,看下他是如何创建一系列的系统子服务的:



/**
* The main entry point from zygote.
*/
public static void main(String[] args) {
new SystemServer().run();
}

private void run() {

//启动服务
try {
startBootstrapServices();
startCoreServices();
startOtherServices();//启动系统级服务
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
}
}

private void startOtherServices() {

mSystemServiceManager.startService(JobSchedulerService.class);

}

1.2 SystemServiceManager代码



上一节我们看到JobSchedulerService远程服务已经被启动了,如何启动呢,实际上是通过反射JobSchedulerService该类并调用该类的构造器初始化的:



public  T startService(Class serviceClass) {
final String name = serviceClass.getName();

final T service;
try {
Constructor 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);
}

mServices.add(service);

return service;
}

1.3 JobSchedulerService远程服务代码



接下来我们看看他们是如何初始化:



public class JobSchedulerService extends com.android.server.SystemService
implements StateChangedListener, JobCompletedListener {

public JobSchedulerService(Context context) {
super(context);
// 初始化所有的控制器(每个JobInfo都会创建一系列的约束,必须免费的网络,系统空闲,时间 电量约束等等)
mCOntrollers= new ArrayList();
mControllers.add(ConnectivityController.get(this));
mControllers.add(TimeController.get(this));
mControllers.add(IdleController.get(this));
mControllers.add(BatteryController.get(this));

//操作任务逻辑的处理器
mHandler = new JobHandler(context.getMainLooper());
//远程服务的代理对象 很重要 至此!!!进程一服务端我们已经分析完毕
mJobSchedulerStub = new JobSchedulerStub();
// 读取任务进度的持久化文件 /system/job/jobs.xml
mJobs = JobStore.initAndGet(this);
}

}

2.关联服务

还记得我们调用代码的顺序吗,当我们获取服务后,我们会调用如下代码:



mJobScheduler.schedule(mJobInfo);   


此刻通过进程二服务端的图你会发现,JobScheduer是一个抽象类,而它还有一个子类实现。当我们调用schedule方法的时候,子类通过代理调用了服务端的schedule():



public abstract class JobScheduler {}

public class JobSchedulerImpl extends JobScheduler {

IJobScheduler mBinder;

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

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

...

}


回到进程一服务端查看JobSchedulerStub代理是如何处理的:



@Override
public int schedule(JobInfo job) throws RemoteException {

...
try {
//调用JobSchedulerService的schedule(job, uid)
return JobSchedulerService.this.schedule(job, uid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}

public class JobSchedulerService{

public int schedule(JobInfo job, int uId) {
//1.封装任务为JobStatus
JobStatus jobStatus = new JobStatus(job, uId);
//2.如果之前任务已经存在 替换
cancelJob(uId, job.getId());
//3.开始跟踪任务 我们之前说过 JobInfo会携带各种约束,这里根据各种约束开始跟踪任务,会调用到约束的maybeStartTrackingJob()。
startTrackingJob(jobStatus);
//4.检查是否需要马上执行任务。
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
return JobScheduler.RESULT_SUCCESS;
}
}

3.触发任务回调

之前我们提到,在创建JobInfo必须设置约束才可以。那么服务端也创建了一些列控制器来跟踪任务的状态。这个我们在JobScheduler创建的时候已经提过了。他们有个共同的父类StateController。



public abstract class StateController {
//开始跟踪任务
public abstract void maybeStartTrackingJob(JobStatus jobStatus);
//结束跟踪任务
public abstract void maybeStopTrackingJob(JobStatus jobStatus);

public abstract void dumpControllerState(PrintWriter pw);

}


以第一个子类ConnectivityController为例他是通过get静态方式初始化的:



public class ConnectivityController extends StateController implements
ConnectivityManager.OnNetworkActiveListener {

public static ConnectivityController get(JobSchedulerService jms) {
synchronized (sCreationLock) {
if (mSingleton == null) {
mSingleton = new ConnectivityController(jms, jms.getContext());
}
return mSingleton;
}
}

private ConnectivityController(StateChangedListener stateChangedListener, Context context) {
super(stateChangedListener, context);
// Register connectivity changed BR.
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mContext.registerReceiverAsUser(
mConnectivityChangedReceiver, UserHandle.ALL, intentFilter, null, null);
ConnectivityService cs =
(ConnectivityService)ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
if (cs != null) {
if (cs.getActiveNetworkInfo() != null) {
mNetworkCOnnected= cs.getActiveNetworkInfo().isConnected();
}
mNetworkUnmetered = mNetworkConnected && !cs.isActiveNetworkMetered();
}
}

}


上面的代码完成了如下几件事:




  1. 传入当前的StateChangedListener状态改变监听器,并且JobSchedulerService已经实现了该监听器。也就是说如果网络改变了可以通过该监听器回调给JobSchedulerService。

  2. 注册了一个叫做ConnectivityChangedReceiver广播接收者。当网络改变的时候就会调用该方法。

    class ConnectivityChangedReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

    final String action = intent.getAction();
    if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {

    //获取网络的类型...
    if (activeNetwork == null) {
    ...
    updateTrackedJobs(userid);
    } else if (activeNetwork.getType() == networkType) {
    ...
    updateTrackedJobs(userid);
    }
    }
    }
    };

    private void updateTrackedJobs(int userId) {
    synchronized (mTrackedJobs) {
    //... 如果网络改变了 则通过监听器告诉JobSchedulerService
    if (changed) {
    mStateChangedListener.onControllerStateChanged();
    }
    }
    }



我们看下当某个条件触发。JobSchedulerService是如何处理的:



@Override
public void onControllerStateChanged() {
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
}

private class JobHandler extends Handler {
...
@Override
public void handleMessage(Message message) {
...
switch (message.what) {
case MSG_JOB_EXPIRED:
synchronized (mJobs) {
JobStatus runNow = (JobStatus) message.obj;
...
if (runNow != null && !mPendingJobs.contains(runNow)
&& mJobs.containsJob(runNow)) {
mPendingJobs.add(runNow);
}
queueReadyJobsForExecutionLockedH();
}
break;
case MSG_CHECK_JOB:
...
maybeQueueReadyJobsForExecutionLockedH();
break;
}
maybeRunPendingJobsH();
removeMessages(MSG_CHECK_JOB);
}

}


上面的代码主要执行三个有效的方法:




  1. maybeQueueReadyJobsForExecutionLockedH();//将任务添加到任务待执行列表中

  2. maybeRunPendingJobsH();//将待执行任务列表中的任务执行

  3. removeMessages(MSG_CHECK_JOB);移除当前任务



maybeRunPendingJobsH()是如何关联我们熟悉的JobService的?



private void maybeRunPendingJobsH() {
synchronized (mJobs) {
Iterator it = mPendingJobs.iterator();
while (it.hasNext()) {
//1.拿到等待执行的任务
JobStatus nextPending = it.next();
...
//2.从执行列表中移除当前任务
if (!availableContext.executeRunnableJob(nextPending)) {
if (DEBUG) {
Slog.d(TAG, "Error executing " + nextPending);
}
//3.从待运行的列表中移除
mJobs.remove(nextPending);
}
it.remove();
}
}
}


JobServiceContext是如何执行executeRunnableJob(nextPending)去执行任务的,代码如下:



boolean executeRunnableJob(JobStatus job) {
//构建任务参数
mParams = new JobParameters(this, job.getJobId(), job.getExtras(),
!job.isConstraintsSatisfied());
//绑定我们当初在JobInfo中传入的服务对象。
boolean binding = mContext.bindServiceAsUser(intent, this,
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
new UserHandle(job.getUserId()));
}


绑定JobService服务后,实现了如下:




  1. onBind()被调用,返回代理对象

    public abstract class JobService extends Service {

    IJobService mBinder = new IJobService.Stub() {
    @Override
    public void startJob(JobParameters jobParams) {
    ensureHandler();
    Message m = Message.obtain(mHandler, MSG_EXECUTE_JOB, jobParams);
    m.sendToTarget();
    }
    @Override
    public void stopJob(JobParameters jobParams) {
    ensureHandler();
    Message m = Message.obtain(mHandler, MSG_STOP_JOB, jobParams);
    m.sendToTarget();
    }
    };

    public final IBinder onBind(Intent intent) {
    return mBinder.asBinder();
    }

    }


  2. JobServiceContext的onServiceConnected()被调用:

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
    //内部wake_lock锁在这里。。。
    mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mRunningJob.getTag());
    mWakeLock.setWorkSource(new WorkSource(mRunningJob.getUid()));
    mWakeLock.setReferenceCounted(false);
    mWakeLock.acquire();
    mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
    }


    private class JobServiceHandler extends Handler {

    @Override
    public void handleMessage(Message message) {
    //case MSG_SERVICE_BOUND:
    handleServiceBoundH();
    }


    private void handleServiceBoundH() {
    ...
    //最最最...终启动了onStartJob();
    service.startJob(mParams);
    }

    }







推荐阅读
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • Explore how Matterverse is redefining the metaverse experience, creating immersive and meaningful virtual environments that foster genuine connections and economic opportunities. ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • 本文介绍了如何使用 Spring Boot DevTools 实现应用程序在开发过程中自动重启。这一特性显著提高了开发效率,特别是在集成开发环境(IDE)中工作时,能够提供快速的反馈循环。默认情况下,DevTools 会监控类路径上的文件变化,并根据需要触发应用重启。 ... [详细]
  • 本文介绍如何利用动态规划算法解决经典的0-1背包问题。通过具体实例和代码实现,详细解释了在给定容量的背包中选择若干物品以最大化总价值的过程。 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • 使用 Azure Service Principal 和 Microsoft Graph API 获取 AAD 用户列表
    本文介绍了一段通用代码示例,该代码不仅能够操作 Azure Active Directory (AAD),还可以通过 Azure Service Principal 的授权访问和管理 Azure 订阅资源。Azure 的架构可以分为两个层级:AAD 和 Subscription。 ... [详细]
  • 本文深入探讨了 Java 中的 Serializable 接口,解释了其实现机制、用途及注意事项,帮助开发者更好地理解和使用序列化功能。 ... [详细]
  • DNN Community 和 Professional 版本的主要差异
    本文详细解析了 DotNetNuke (DNN) 的两种主要版本:Community 和 Professional。通过对比两者的功能和附加组件,帮助用户选择最适合其需求的版本。 ... [详细]
  • 将Web服务部署到Tomcat
    本文介绍了如何在JDeveloper 12c中创建一个Java项目,并将其打包为Web服务,然后部署到Tomcat服务器。内容涵盖从项目创建、编写Web服务代码、配置相关XML文件到最终的本地部署和验证。 ... [详细]
  • 本文介绍了一款用于自动化部署 Linux 服务的 Bash 脚本。该脚本不仅涵盖了基本的文件复制和目录创建,还处理了系统服务的配置和启动,确保在多种 Linux 发行版上都能顺利运行。 ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
author-avatar
麦豪小仙
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有