当前位置: 首页 > article >正文

Android JobScheduler介绍

Android JobScheduler介绍

介绍

JobScheduler是在Android 5.0添加的,它可以检测网络状态、设备是否充电中、低电量、低存储等状态,当所有条件都满足时就会触发执行对应的JobService来完成任务。同时具备了重试、定时执行、持久化任务(设备重启后可恢复任务)等功能。可谓是十分强大
JobScheduler的使用大致分为三步:

  1. 创建JobService类
  2. 创建一个JobInfo
  3. 获取JobScheduler系统服务执行任务
1 创建JobService

JobScheduler的原理大致是是通过监听手机状态,当条件满足时启动Service执行任务。
因此在使用JobScheduler之前,我们需要定义一个Service,当然并不是随随便便定义,而是需要派生自JobService。
只需要重写onStopJob和onStartJob即可。

class DemoJobService: JobService() {
    
    override fun onStartJob(params: JobParameters?): Boolean {
        //do sth...
        
        //这个返回值是有讲究的
        //true表示Service的工作在一个独立线程中执行,工作完成之后需要调用jobFinish方法通知JobScheduler工作完成
        //false表示Service的工作已经完成,JobScheduler收到通知之后会释放资源
        return false
    }

    //该方法会在一些极端场景下触发
    //比如当前的Job需要有Wifi连接的场景下才可执行,但是在执行期间
    //用户关闭Wifi,那么就会触发该方法
    override fun onStopJob(params: JobParameters?): Boolean {
        //do sth...
        //true表示需要进行重试
        //false表示不再进行重试,Job将会被丢弃
        return true
    }
}

同时,在manifest中注册时,还需要设置一个权限(否则会报错),如下

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
    //...
    <service android:name=".DemoJobService" 
             android:permission="android.permission.BIND_JOB_SERVICE"/>
</application>
2 创建JobInfo

JobInfo是对任务的描述,比如说需要监听哪些状态、重试策略、任务执行时间、是否持久化等等。
JobInfo.Builder的构造函数需要传入一个jobId,是Job的唯一标志,后续通过该jobId来取消Job。
通过Builder模式构造JobInfo。

val jobInfo = JobInfo.Builder(1, ComponentName(packageName, DemoJobService::class.java.name))
    .setBackoffCriteria(1000,JobInfo.BACKOFF_POLICY_LINEAR) //重试机制
    .setMinimumLatency(1000)//设置延迟时间
    .setOverrideDeadline(10000)//设置最后期限,如果达到该时间点,Job还没被执行,那么会强制执行一次
//                .setPeriodic(2000)//每隔2s执行一次,跟上面两个会冲突
    .setPersisted(true)//持久化,就算手机关机,启动之后也可以恢复Job,需要RECEIVE_BOOT_COMPLETED权限
    .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)//设置Job依赖的网络类型
    .setRequiresCharging(false)//是否要求设备处于充电状态
    .setRequiresDeviceIdle(false)//是否要求设备处于空闲状态
    .build()

JobInfo其他的设置方法:

.setMinimumLatency(5000)//5秒 最小延时、
.setOverrideDeadline(60000)//maximum最多执行时间  
 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)//免费的网络---wifi 蓝牙 USB
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)//任意网络---
  /**
   设置重试/退避策略,当一个任务调度失败的时候执行什么样的测量采取重试。
    initialBackoffMillis:第一次尝试重试的等待时间间隔ms
    *backoffPolicy:对应的退避策略。比如等待的间隔呈指数增长。
    */
  .setBackoffCriteria(long initialBackoffMillis, int backoffPolicy)
.setBackoffCriteria(JobInfo.MAX_BACKOFF_DELAY_MILLIS, JobInfo.BACKOFF_POLICY_LINEAR)
  //.setPeriodic (long intervalMillis)//设置执行周期,每隔一段时间间隔任务最多可以执行一次。
//.setPeriodic(long intervalMillis,long flexMillis)//在周期执行的末端有一个flexMiliis长度的窗口期,任务就可以在这个窗口期执行。
 //设置设备重启后,这个任务是否还要保留。需要权限:  RECEIVE_BOOT_COMPLETED //ctrl+shift+y/u x
//.setPersisted(boolean isPersisted);
// .setRequiresCharging(boolean )//是否需要充电
// .setRequiresDeviceIdle(boolean)//是否需要等设备出于空闲状态的时候
// .addTriggerContentUri(uri)//监听uri对应的数据发生改变,就会触发任务的执行。
// .setTriggerContentMaxDelay(long duration)//设置Content发生变化一直到任务被执行中间的最大延迟时间
 //设置Content发生变化一直到任务被执行中间的延迟。如果在这个延迟时间内content发生了改变,延迟时间会重写计算。
  // .setTriggerContentUpdateDelay(long durationMilimms)
3 执行任务

最后通过getSystemService获取JobScheduler服务执行任务就可以了

//执行任务
val jobScheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
jobScheduler.schedule(jobInfo)

//取消任务
jobScheduler.cancel(1)
4 最后

JobScheduler上手还是比较简单的,由于本文是基于api23的源码,因此还有很多新功能没有在源码中展示出来,如setRequiresBatteryNotLow(设置Job对电量的要求)、setRequiresStorageNotLow(设置Job对存储空间的要求)等.

特性&适用

特性
  1. 支持在一个任务上组合多个条件
  2. 内置条件:设备待机、设备充电和连接网络
  3. 支持持续的job,这意味着设备重启后,之前被中断的job可以继续执行
  4. 支持设置job的最后执行期限
  5. 根据你的配置,可以设置job在后台运行还是在主线程中运行
适用

需要在Android设备满足某种场合才需要去执行处理数据:

  1. 应用具有可以推迟的非面向用户的工作(定期数据库数据更新)
  2. 应用具有当插入设备时希望优先执行的工作(充电时才希望执行的工作备份数据)
  3. 需要访问网络或 Wi-Fi 连接时需要进行的任务(如向服务器拉取内置数据)
  4. 希望作为一个批次定期运行的许多任务(s)
特征
  1. Job Scheduler只有在Api21或以上的系统支持。
  2. Job Scheduler是将多个任务打包在一个场景下执行。
  3. 在系统重启以后,任务会依然保留在Job Scheduler当中,因此不需要监听系统启动状态重复设定。
  4. 如果在一定期限内还没有满足特定执行所需情况,Job Scheduler会将这些任务加入队列,并且随后会进行执行。

使用Job Scheduler,应用需要做的事情就是判断哪些任务是不紧急的,可以交给Job Scheduler来处理,Job Scheduler集中处理收到的任务,选择合适的时间,合适的网络,再一起进行执行。把时效性不强的工作丢给它做。

源码分析

JobScheduler是系统服务JobSchedulerService,JobScheduler是一个抽象类;以下是JobScheduler的源码:

public abstract class JobScheduler {
public static final int RESULT_FAILURE = 0;
public static final int RESULT_SUCCESS = 1;
//这个是提交一个工作任务JobInfo
public abstract int schedule(JobInfo job);
public abstract int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag);
 //这个是取消一个工作任务
public abstract void cancel(int jobId);
//这个是取消所有的工作任务
public abstract void cancelAll();
  //得到所有将来要执行的工作任务
public abstract @NonNull List<JobInfo> getAllPendingJobs();
//根据Jobid获得将来要执行的工作任务
public abstract @Nullable JobInfo getPendingJob(int jobId);
}

JobScheduler的实现类是JobSchedulerImpl:

public class JobSchedulerImpl extends JobScheduler {
IJobScheduler mBinder;

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

@Override
public int schedule(JobInfo job) {
    try {
        //这个mBinder是JobSchedulerService中的IJobScheduler.Stub,
        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<JobInfo> getAllPendingJobs() {
    try {
        return mBinder.getAllPendingJobs();
    } catch (RemoteException e) {
        return null;
    }
}
}

在代码中 调用mJobScheduler.schedule(job);其实是调了JobScheduler的实现类JobSchedulerImpl中的schedule方法;然后再调了mBinder.schedule(job);这个mBinder就是JobSchedulerService,调用了JobSchedulerService类里面IJobScheduler.Stub内部类的schedule方法; JobSchedulerService是在哪里启动的呢?先看一下的源码,从源码分析JobSchedulerService是一个系统服务;

public class JobSchedulerService extends com.android.server.SystemService
    implements StateChangedListener, JobCompletedListener {
static final boolean DEBUG = false;
/** The number of concurrent jobs we run at one time. */
private static final int MAX_JOB_CONTEXTS_COUNT
        = ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
static final String TAG = "JobSchedulerService";
/** Master list of jobs. */
final JobStore mJobs;

static final int MSG_JOB_EXPIRED = 0;
static final int MSG_CHECK_JOB = 1;
//....省略了n多代码
}

是系统服务那应该就是在 SystemServer启动的,先看一下SystemServer的源码;

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

手机开机启动会走SystemServer中的主函数main方法,main方法调用了run方法,下面是run方法中的代码:

private void run() {
     //....省略了n多代码
    // Start services.
    //这里启动一些系统服务
    try {
        startBootstrapServices();
        startCoreServices();
      //会走这个
        startOtherServices();
    } catch (Throwable ex) {
        Slog.e("System", "******************************************");
        Slog.e("System", "************ Failure starting system services", ex);
        throw ex;
    }
 //....省略了n多代码
}

从run方法可以看出,这里启动了一系列的系统服务,里面调用了startOtherServices()方法,那接下看一下startOtherServices方法,向下看:

private void startOtherServices() {
      //....省略了n多代码
         mSystemServiceManager.startService(TwilightService.class);

        //这里就是启动JobSchedulerService
      mSystemServiceManager.startService(JobSchedulerService.class);

        if (!disableNonCoreServices) {
            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
                mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
            }

            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
                mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
            }

            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
                mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
            }
        }
 //....省略了n多代码
}

在startOtherServices方法方法中调用了mSystemServiceManager.startService(JobSchedulerService.class);这里就启动了JobSchedulerService服务;接下来我们分析JobSchedulerService的源码; 先看一下JobSchedulerService的构造方法:

public JobSchedulerService(Context context) {
    super(context);
    // Create the controllers.
    mControllers = new ArrayList<StateController>();
    //以下控制类都是继承了StateController类
    //网络联接控制类
    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());
  //new了IJobScheduler.Stub,这个其实就是前面JobSchedulerImpl类中mBinder,这里用到了进程间通信binder机制,不明白的可能先学习一下进程间通信机制;
    mJobSchedulerStub = new JobSchedulerStub();
    mJobs = JobStore.initAndGet(this);
}

创建了5个不同的StateController,分别添加到mControllers。

类型说明
ConnectivityController注册监听网络连接状态的广播
TimeController注册监听job时间到期的广播
IdleController注册监听屏幕亮/灭,dream进入/退出,状态改变的广播
BatteryController注册监听电池是否充电,电量状态的广播
AppIdleController监听app是否空闲

前面提到调用mBinder.schedule(job);其实是调用了JobSchedulerService类里面IJobScheduler.Stub内部类的schedule方法;接下看一下这个方法:

final class JobSchedulerStub extends IJobScheduler.Stub {
   //....省略了n多代码
    @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();
     //....省略了n多代码
        long ident = Binder.clearCallingIdentity();
        try {
        //这个是关键代码,调用JobSchedulerService中的schedule
            return JobSchedulerService.this.schedule(job, uid);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

}

从上面代码看出,是调用了调用JobSchedulerService中的schedule方法,好了,看一下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;
}

从上面代码可以看出是通过Handler发消息,MSG_CHECK_JOB是目标源,看一下JobHandler 中的方法

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;
                    if (runNow != null && !mPendingJobs.contains(runNow)
                            && mJobs.containsJob(runNow)) {
                        mPendingJobs.add(runNow);
                    }
                    queueReadyJobsForExecutionLockedH();
                }
                break;
            case MSG_CHECK_JOB:
                synchronized (mJobs) {
                    maybeQueueReadyJobsForExecutionLockedH();
                }
                break;
        }
        maybeRunPendingJobsH();
      
        removeMessages(MSG_CHECK_JOB);
    }

通过Handler发消息,然后调用 maybeQueueReadyJobsForExecutionLockedH()方法,

private void queueReadyJobsForExecutionLockedH() {
        ArraySet<JobStatus> jobs = mJobs.getJobs();
        if (DEBUG) {
            Slog.d(TAG, "queuing all ready jobs for execution:");
        }
      //遍历要处理的目标任务,把目标任务加到集合PendingJobs中
        for (int i=0; i<jobs.size(); i++) {
            JobStatus job = jobs.valueAt(i);
            if (isReadyToBeExecutedLocked(job)) {
                if (DEBUG) {
                    Slog.d(TAG, "    queued " + job.toShortString());
                }
                //把目标任务加到待处理目标任务集合中
                mPendingJobs.add(job);
            } else if (isReadyToBeCancelledLocked(job)) {
                stopJobOnServiceContextLocked(job);
            }
        }
        if (DEBUG) {
            final int queuedJobs = mPendingJobs.size();
            if (queuedJobs == 0) {
                Slog.d(TAG, "No jobs pending.");
            } else {
                Slog.d(TAG, queuedJobs + " jobs queued.");
            }
        }
    }

这个方法主要是遍历将来要处理的工作任务然后一个个加到待处理工作任务集合中去;这个方法执行完后就会执行JobHandler中的maybeRunPendingJobsH()方法;

private void maybeRunPendingJobsH() {
        synchronized (mJobs) {
            if (mDeviceIdleMode) {
                // If device is idle, we will not schedule jobs to run.
                return;
            }
            Iterator<JobStatus> it = mPendingJobs.iterator();
            if (DEBUG) {
                Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
            }
            //通过遍历待处理任务集合,一个个处理待处理任务
            while (it.hasNext()) {
                JobStatus nextPending = it.next();
                JobServiceContext availableContext = null;
                for (int i=0; i<mActiveServices.size(); i++) {
                    JobServiceContext jsc = mActiveServices.get(i);
                    final JobStatus running = jsc.getRunningJob();
                    if (running != null && running.matches(nextPending.getUid(),
                            nextPending.getJobId())) {
                        // Already running this job for this uId, skip.
                        availableContext = null;
                        break;
                    }
                    if (jsc.isAvailable()) {
                        availableContext = jsc;
                    }
                }
                if (availableContext != null) {
                    if (DEBUG) {
                        Slog.d(TAG, "About to run job "
                                + nextPending.getJob().getService().toString());
                    }
    //这个方法就是处理待处理任务的方法
                    if (!availableContext.executeRunnableJob(nextPending)) {
                        if (DEBUG) {
                            Slog.d(TAG, "Error executing " + nextPending);
                        }
                        mJobs.remove(nextPending);
                    }
                    it.remove();
                }
            }
        }
    }
}

从上面源码分析可以得出,这个方法通过遍历待处理任务集合,处理任务,这里调用了availableContext.executeRunnableJob(nextPending)方法,这个就是处理待处理任务的方法,接下来我们一起看看这个方法的源码,分析下:

public class JobServiceContext extends IJobCallback.Stub   implements ServiceConnection {

 @Override
public void onServiceConnected(ComponentName name, IBinder service) {
    if (!name.equals(mRunningJob.getServiceComponent())) {
        mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
        return;
    }
    this.service = IJobService.Stub.asInterface(service);
    final PowerManager pm =
            (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
    mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mRunningJob.getTag());
//这里也用WakeLock锁,防止手机休眠
    mWakeLock.setWorkSource(new WorkSource(mRunningJob.getUid()));
    mWakeLock.setReferenceCounted(false);
    mWakeLock.acquire();
    mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
}
}

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

/** Start the job on the service. */
    private void handleServiceBoundH() {
        if (DEBUG) {
            Slog.d(TAG, "MSG_SERVICE_BOUND for " + mRunningJob.toShortString());
        }
        if (mVerb != VERB_BINDING) {
            Slog.e(TAG, "Sending onStartJob for a job that isn't pending. "
                    + VERB_STRINGS[mVerb]);
            closeAndCleanupJobH(false /* reschedule */);
            return;
        }
        if (mCancelled.get()) {
            if (DEBUG) {
                Slog.d(TAG, "Job cancelled while waiting for bind to complete. "
                        + mRunningJob);
            }
            closeAndCleanupJobH(true /* reschedule */);
            return;
        }
        try {
            mVerb = VERB_STARTING;
            scheduleOpTimeOut();
      //我们就是要找这个方法, 看到这里明白了吧,这个就是调用了jobService中的startjob
            service.startJob(mParams);
        } catch (RemoteException e) {
            Slog.e(TAG, "Error sending onStart message to '" +
                    mRunningJob.getServiceComponent().getShortClassName() + "' ", e);
        }
    }

从上面源码可以看出,最终是调用了jobService中的startjob方法, 这样就明白了,是如何触发调用jobService中的startjob方法的;

前面在JobSchedulerService中提到了控件类StateController类,这个是一个抽象类,有很多实现类,这个我只分析一个ConnectivityController实现类,其他都差不多,接下来分析一下ConnectivityController源码:

public class ConnectivityController extends StateController implements
    ConnectivityManager.OnNetworkActiveListener {
private static final String TAG = "JobScheduler.Conn";
//工作任务状态集合
private final List<JobStatus> mTrackedJobs = new LinkedList<JobStatus>();
  //这个是手机网络连接改变广播,网络发生改变,会触发这个广播
private final BroadcastReceiver mConnectivityChangedReceiver =
        new ConnectivityChangedReceiver();
/** Singleton. */
private static ConnectivityController mSingleton;
private static Object sCreationLock = new Object();
/** Track whether the latest active network is metered. */
private boolean mNetworkUnmetered;
/** Track whether the latest active network is connected. */
private boolean mNetworkConnected;

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();
    }
}

@Override
public void maybeStartTrackingJob(JobStatus jobStatus) {
    if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) {
        synchronized (mTrackedJobs) {
            jobStatus.connectivityConstraintSatisfied.set(mNetworkConnected);
            jobStatus.unmeteredConstraintSatisfied.set(mNetworkUnmetered);
            mTrackedJobs.add(jobStatus);
        }
    }
}

@Override
public void maybeStopTrackingJob(JobStatus jobStatus) {
    if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) {
        synchronized (mTrackedJobs) {
            mTrackedJobs.remove(jobStatus);
        }
    }
}

/**
 * @param userId Id of the user for whom we are updating the connectivity state.
 */
private void updateTrackedJobs(int userId) {
    synchronized (mTrackedJobs) {
        boolean changed = false;
        for (JobStatus js : mTrackedJobs) {
            if (js.getUserId() != userId) {
                continue;
            }
            boolean prevIsConnected =
                    js.connectivityConstraintSatisfied.getAndSet(mNetworkConnected);
            boolean prevIsMetered = js.unmeteredConstraintSatisfied.getAndSet(mNetworkUnmetered);
            if (prevIsConnected != mNetworkConnected || prevIsMetered != mNetworkUnmetered) {
                changed = true;
            }
        }
        if (changed) {
            mStateChangedListener.onControllerStateChanged();
        }
    }
}

/**
 * We know the network has just come up. We want to run any jobs that are ready.
 */
public synchronized void onNetworkActive() {
    synchronized (mTrackedJobs) {
        for (JobStatus js : mTrackedJobs) {
            if (js.isReady()) {
                if (DEBUG) {
                    Slog.d(TAG, "Running " + js + " due to network activity.");
                }
                mStateChangedListener.onRunJobNow(js);
            }
        }
    }
}

class ConnectivityChangedReceiver extends BroadcastReceiver {
    /**
     * We'll receive connectivity changes for each user here, which we process independently.
     * We are only interested in the active network here. We're only interested in the active
     * network, b/c the end result of this will be for apps to try to hit the network.
     * @param context The Context in which the receiver is running.
     * @param intent The Intent being received.
     */
    // TODO: Test whether this will be called twice for each user.
    @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 {
            if (DEBUG) {
                Slog.d(TAG, "Unrecognised action in intent: " + action);
            }
        }
    }
};

@Override
public void dumpControllerState(PrintWriter pw) {
    pw.println("Conn.");
    pw.println("connected: " + mNetworkConnected + " unmetered: " + mNetworkUnmetered);
    for (JobStatus js: mTrackedJobs) {
        pw.println(String.valueOf(js.hashCode()).substring(0, 3) + ".."
                + ": C=" + js.hasConnectivityConstraint()
                + ", UM=" + js.hasUnmeteredConstraint());
    }
}
 }

上面是网络联接控制类ConnectivityController,当网络发生改变时,会触发网络连接改变广播,然后调用updateTrackedJobs(userid)方法,在updateTrackedJobs方法中,会判断网络是否有改变,有改变的会调 mStateChangedListener.onControllerStateChanged()方法;这样又调用了JobSchedulerService类中onControllerStateChanged方法:

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

在onControllerStateChanged方法中通过handler发消息,然后调用了maybeQueueReadyJobsForExecutionLockedH();

private void maybeQueueReadyJobsForExecutionLockedH() {
        int chargingCount = 0;
        int idleCount =  0;
        int backoffCount = 0;
        int connectivityCount = 0;
        List<JobStatus> runnableJobs = new ArrayList<JobStatus>();
        ArraySet<JobStatus> jobs = mJobs.getJobs();
        for (int i=0; i<jobs.size(); i++) {
            JobStatus job = jobs.valueAt(i);
            if (isReadyToBeExecutedLocked(job)) {
                if (job.getNumFailures() > 0) {
                    backoffCount++;
                }
                if (job.hasIdleConstraint()) {
                    idleCount++;
                }
                if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()) {
                    connectivityCount++;
                }
                if (job.hasChargingConstraint()) {
                    chargingCount++;
                }
                runnableJobs.add(job);
            } else if (isReadyToBeCancelledLocked(job)) {
                stopJobOnServiceContextLocked(job);
            }
        }
        if (backoffCount > 0 ||
                idleCount >= MIN_IDLE_COUNT ||
                connectivityCount >= MIN_CONNECTIVITY_COUNT ||
                chargingCount >= MIN_CHARGING_COUNT ||
                runnableJobs.size() >= MIN_READY_JOBS_COUNT) {
            if (DEBUG) {
                Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs.");
            }
            for (int i=0; i<runnableJobs.size(); i++) {
          //把待处理的工作任务加到集合中去
                mPendingJobs.add(runnableJobs.get(i));
            }
        } else {
            if (DEBUG) {
                Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything.");
            }
        }
        if (DEBUG) {
            Slog.d(TAG, "idle=" + idleCount + " connectivity=" +
            connectivityCount + " charging=" + chargingCount + " tot=" +
                    runnableJobs.size());
        }
    }

通过上面方法,把待处理的工作任务加到集合中,然后再调 maybeRunPendingJobsH();这个前面已提到过,就不再说了,一样的;

理解JobScheduler机制

视频
文章


http://www.kler.cn/a/307125.html

相关文章:

  • 陪诊问诊APP开发实战:基于互联网医院系统源码的搭建详解
  • 什么时候需要复写hashcode()和compartTo方法
  • 【面试题】发起一次网络请求,当请求>=1s,立马中断
  • 【练习案例】30个 CSS Javascript 加载器动画效果
  • 代码 RNN原理及手写复现
  • 在linux中使用nload实时查看网卡流量
  • 介绍一些免费 的 html 5模版网站 和配色 网站
  • Spring的核心思想
  • Linux常用命令以及操作技巧
  • 数据库之索引<保姆级文章>
  • 纯血鸿蒙NEXT常用的几个官方网站
  • 你天天用微服务还不知道心跳检测机制是什么?
  • Redis的配置与优化
  • spring模块(六)spring event事件(3)广播与异步问题
  • 鸿蒙OS 资源文件
  • 七、结合Landsat、夜光数据建成区提取——K均值聚类和监督分类提取精确的建成区边界
  • AI为云游戏带来的革新及解决方案:深度技术剖析与未来展望
  • windows下自启springboot项目(jar+nginx)
  • 安卓网址自动添加%,显示网页异常
  • IAPP发布《2024年人工智能治理实践报告》
  • 【Leetcode:1184. 公交站间的距离 + 模拟】
  • 【2025】基于python的网上商城比价系统、智能商城比价系统、电商比价系统、智能商城比价系统(源码+文档+解答)
  • Ready Go
  • 本地部署大语言模型
  • 6. Fabric 拖拽元素到画布
  • 聊聊OceanBase合并和转储