JobScheduler 详解二

前言

上篇文章JobScheduler 详解一讲述了 JobScheduler 的服务启动,本篇文章将继续上篇文章,以 TimeController 为例,讲述 JobScheduler 的 schedule 和 cancel 流程。仍旧使用上篇文章给出的 demo:

    private static ComponentName sService = new ComponentName("com.example.mi.myjobtest",
            MyJobService.class.getName());
    public static void schedule(Context context) {
        JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
        JobInfo job = new JobInfo.Builder(JOB_ID, sService)
                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)//网络条件,默认值NETWORK_TYPE_NONE
                .setPeriodic(DAY_TIME)//任务执行周期
                .setPersisted(true)//设备重启后是否继续执行
                .setRequiresCharging(true)//设置是否需要充电
                .build();
        js.schedule(job);
    }

一、JobScheduler 的 schedule 流程

先给出 JobScheduler 的 schedule 流程时序图如下:

schedule 时序图

点击查看大图

1.1 JobSchedulerService.schedule

JobSchedulerService.java

    public int schedule(JobInfo job, int uId) {
        JobStatus jobStatus = new JobStatus(job, uId);
        cancelJob(uId, job.getId());
        ...
        startTrackingJob(jobStatus);
        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
        return JobScheduler.RESULT_SUCCESS;
    }

1.1.1 创建 JobStatus

JobStatus.java

    /** Create a newly scheduled job. */
    public JobStatus(JobInfo job, int uId) {
        this(job, uId, 0);
        final long elapsedNow = SystemClock.elapsedRealtime();
        if (job.isPeriodic()) {// 执行过 setPeriodic()
            earliestRunTimeElapsedMillis = elapsedNow;
            latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
        } else {
            earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
                    elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
            latestRunTimeElapsedMillis = job.hasLateConstraint() ?
                    elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME;
        }
    }

1.2 JobSchedulerService.cancelJob

JobSchedulerService.java

    public void cancelJob(int uid, int jobId) {
        JobStatus toCancel;
        synchronized (mJobs) {
            // 根据 uid 和 jobId 来查找相应的 JobStatus
            toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
        }
        if (toCancel != null) {
            cancelJobImpl(toCancel);
        }
    }

1.3 JobSchedulerService.startTrackingJob

JobSchedulerService.java

    private void startTrackingJob(JobStatus jobStatus) {
        boolean update;
        boolean rocking;
        synchronized (mJobs) {
            // 把 jobStatus 添加到 mJobs 中
            update = mJobs.add(jobStatus);
            // mReadyToRock 在 phase600 是变为 true
            rocking = mReadyToRock;
        }
        if (rocking) {
            for (int i=0; i<mControllers.size(); i++) {
                StateController controller = mControllers.get(i);
                if (update) {
                    controller.maybeStopTrackingJob(jobStatus);
                }
                // 检查是否需要将该任务添加到相应的追踪控制器
                controller.maybeStartTrackingJob(jobStatus);
            }
        }
    }

执行完此步之后,通过 JobHandler 发送 MSG_CHECK_JOB 消息,接下来进入其 handleMessage

1.4 JobSchedulerService.JobHandler

1.4.1 handleMessage

JobSchedulerService.java :: JobHandler

    private class JobHandler extends Handler {
            @Override
        public void handleMessage(Message message) {
            ...
            switch (message.what) {
                case MSG_JOB_EXPIRED:
                    ...
                    break;
                case MSG_CHECK_JOB:
                    synchronized (mJobs) {
                        // Check the list of jobs and run some of them if we feel inclined.
                        maybeQueueReadyJobsForExecutionLockedH();
                    }
                    break;
                case MSG_STOP_JOB:
                    cancelJobImpl((JobStatus)message.obj);
                    break;
            }
            maybeRunPendingJobsH();
            removeMessages(MSG_CHECK_JOB);
        }
    }

1.4.2 maybeQueueReadyJobsForExecutionLockedH

JobSchedulerService.java :: JobHandler

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++;
            }
            //将所有 ready 的 jobs 加入 runnableJobs 队列
            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) {
        for (int i=0; i<runnableJobs.size(); i++) {
            //加入到 mPendingJobs 队列
            mPendingJobs.add(runnableJobs.get(i));
        }
    }
}

该方法实现的功能是:
* 先将所有 ready 的 jobs 加入 runnableJobs 队列中
* 如果满足发送条件,那么将 runnableJobs 中的所有 jobStatus 加入到 mPendingJobs 中

1.4.3 isReadyToBeExecutedLocked

JobSchedulerService.java :: JobHandler

        private boolean isReadyToBeExecutedLocked(JobStatus job) {
            final boolean jobReady = job.isReady();
            final boolean jobPending = mPendingJobs.contains(job);
            final boolean jobActive = isCurrentlyActiveLocked(job);
            final boolean userRunning = mStartedUsers.contains(job.getUserId());
            return userRunning && jobReady && !jobPending && !jobActive;
        }

只有当用户是运行的、job 满足触发条件、job 不在 mPendingJobs 队列中、job 不在运行中时才会返回 true

1.4.4 maybeRunPendingJobsH

JobSchedulerService.java :: JobHandler

        private void maybeRunPendingJobsH() {
            synchronized (mJobs) {
                ...
                Iterator<JobStatus> it = mPendingJobs.iterator();
                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 (!availableContext.executeRunnableJob(nextPending)) {
                            // 执行出错,在 mJobs 中删除
                            mJobs.remove(nextPending);
                        }
                        it.remove();
                    }
                }
            }
        }

此段代码的作用是找一个 available 的 JobServiceContext 来运行 nextPending job (如果 job 已经在运行中了则跳过)

1.5 executeRunnableJob

JobServiceContext.java

    boolean executeRunnableJob(JobStatus job) {
        synchronized (mLock) {
            mRunningJob = job;
            // 是否达到 Deadline 
            final boolean isDeadlineExpired =
                    job.hasDeadlineConstraint() &&
                            (job.getLatestRunTimeElapsed() < SystemClock.elapsedRealtime());
            mParams = new JobParameters(this, job.getJobId(), job.getExtras(), isDeadlineExpired);
            mExecutionStartTimeElapsed = SystemClock.elapsedRealtime();

            mVerb = VERB_BINDING;
            scheduleOpTimeOut();
            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()));;

            if (!binding) {
                mRunningJob = null;
                mParams = null;
                mExecutionStartTimeElapsed = 0L;
                mVerb = VERB_FINISHED;
                removeOpTimeOut();
                return false;
            }
            try {
                mBatteryStats.noteJobStart(job.getName(), job.getUid());
            } catch (RemoteException e) {
            }
            // 正在执行 job 置为 false
            mAvailable = false;
            return true;
        }
    }

这是 system_server 进程的主线程来执行 bind Service,从而拉起目标进程,服务启动后会回调到发起端的 onServiceConnected

1.5.1 onServiceConnected

JobServiceContext.java

public void onServiceConnected(ComponentName name, IBinder service) {
    JobStatus runningJob;
    synchronized (mLock) {
        // 即为上面执行的 job
        runningJob = mRunningJob;
    }
    if (runningJob == null || !name.equals(runningJob.getServiceComponent())) {
        mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
        return;
    }
    // 获取远程 JobService 的代理端
    // 后续会调用 service.startJob
    this.service = IJobService.Stub.asInterface(service);
    ...
    mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
}

this.service是指获取远程 IJobService 的代理端,mCallbackHandler 运行在 system_server 的主线程

1.5.2 Send MSG_SERVICE_BOUND

JobServiceContext.java :: JobServiceHandler

private class JobServiceHandler extends Handler {
    public void handleMessage(Message message) {
        switch (message.what) {
            case MSG_SERVICE_BOUND:
                removeOpTimeOut();
                handleServiceBoundH();
                break;
            ...
        }
    }
}

1.5.3 handleServiceBoundH

JobServiceContext.java :: JobServiceHandler

        private void handleServiceBoundH() {
            if (mVerb != VERB_BINDING) {
                closeAndCleanupJobH(false /* reschedule */);
                return;
            }
            if (mCancelled.get()) {
                closeAndCleanupJobH(true /* reschedule */);
                return;
            }
            try {
                mVerb = VERB_STARTING;
                scheduleOpTimeOut();
                service.startJob(mParams);
            } catch (RemoteException e) {
            ...
            }
        }

此处的service是由【小节1.5.1】所赋值,是指 app 端 IJobService 的代理类。经过 binder call 回到app进程

1.6 JobService.startJob

JobService.java

public abstract class JobService extends Service {
    /** Binder for this 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();
        }
    };
}

由于 JobService 运行在 app 端所在进程,那么此处的 mHandler 便是指app进程的主线程。

1.7 JobService.JobHandler

JobService.java :: JobHandler

class JobHandler extends Handler {
    ...
    public void handleMessage(Message msg) {
        final JobParameters params = (JobParameters) msg.obj;
        switch (msg.what) {
            case MSG_EXECUTE_JOB:
                boolean workOngoing = JobService.this.onStartJob(params);
                ackStartMessage(params, workOngoing);
                break;
            case MSG_STOP_JOB: ...
            case MSG_JOB_FINISHED:...
            default: break;
        }
    }
}   

二、JobScheduler 的 cancel 流程

2.1 JobSchedulerStub.cancel

JobSchedulerService.java ::JobSchedulerStub

final class JobSchedulerStub extends IJobScheduler.Stub {
    public void cancel(int jobId) throws RemoteException {
        final int uid = Binder.getCallingUid();
        long ident = Binder.clearCallingIdentity();
        try {
            JobSchedulerService.this.cancelJob(uid, jobId);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

        @Override
        public void cancelAll() throws RemoteException {
            final int uid = Binder.getCallingUid();

            long ident = Binder.clearCallingIdentity();
            try {
                JobSchedulerService.this.cancelJobsForUid(uid, true);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
}

2.2 JobSchedulerService.cancelJob

JobSchedulerService.java

    public void cancelJob(int uid, int jobId) {
        JobStatus toCancel;
        synchronized (mJobs) {
            toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
        }
        if (toCancel != null) {
            cancelJobImpl(toCancel);
        }
    }

    public void cancelJobsForUid(int uid, boolean forceAll) {
        List<JobStatus> jobsForUid;
        synchronized (mJobs) {
            jobsForUid = mJobs.getJobsByUid(uid);
        }
        for (int i=0; i<jobsForUid.size(); i++) {
            JobStatus toRemove = jobsForUid.get(i);
            if (!forceAll) {
                String packageName = toRemove.getServiceComponent().getPackageName();
                try {
                    if (ActivityManagerNative.getDefault().getAppStartMode(uid, packageName)
                            != ActivityManager.APP_START_MODE_DISABLED) {
                        continue;
                    }
                } catch (RemoteException e) {
                }
            }
            cancelJobImpl(toRemove);
        }
    }

由上可知:
* cancel(int jobId) 会调用 cancelJob(int uid, int jobId),cancelAll() 会调用 cancelJobsForUid(int uid, true)

2.3 JobSchedulerService.cancelJobImpl

JobSchedulerService.java

    private void cancelJobImpl(JobStatus cancelled) {
        stopTrackingJob(cancelled);
        synchronized (mJobs) {
            // Remove from pending queue.
            mPendingJobs.remove(cancelled);
            // Cancel if running.
            stopJobOnServiceContextLocked(cancelled);
        }
    }

2.4 JobSchedulerService.stopJobOnServiceContextLocked

private boolean stopJobOnServiceContextLocked(JobStatus job) {
    for (int i=0; i<mActiveServices.size(); i++) {
        JobServiceContext jsc = mActiveServices.get(i);
        final JobStatus executing = jsc.getRunningJob();
        if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
            // 找到匹配的正在执行 job,则取消该 job
            jsc.cancelExecutingJob();
            return true;
        }
    }
    return false;
}

作用是找到匹配的正在执行 job,则取消该 job

2.5 JobServiceContext.cancelExecutingJob

JobServiceContext.java

    void cancelExecutingJob() {
        mCallbackHandler.obtainMessage(MSG_CANCEL).sendToTarget();
    }

向运行在 system_server 主线程的 JobServiceHandler 发送 MSG_CANCEL 消息,接收到该消息,则执行handleCancelH()

2.6 JobServiceContext.handleCancelH

JobServiceContext.java :: JobServiceHandler

        private void handleCancelH() {
            switch (mVerb) {
                case VERB_BINDING:
                case VERB_STARTING:
                    mCancelled.set(true);
                    break;
                case VERB_EXECUTING:
                    if (hasMessages(MSG_CALLBACK)) {
                        // If the client has called jobFinished, ignore this cancel.
                        return;
                    }
                    sendStopMessageH();
                    break;
                case VERB_STOPPING:
                    // Nada.
                    break;
                default:
                    break;
            }
        }

2.7 JobServiceContext.sendStopMessageH

JobServiceContext.java :: JobServiceHandler

        private void sendStopMessageH() {
            removeOpTimeOut();
            if (mVerb != VERB_EXECUTING) {
                closeAndCleanupJobH(false /* reschedule */);
                return;
            }
            try {
                mVerb = VERB_STOPPING;
                scheduleOpTimeOut();
                service.stopJob(mParams);
            } catch (RemoteException e) {
                closeAndCleanupJobH(false /* reschedule */);
            }
        }

之后的过程与 schedule 的过程大同小异

总结

由上面的知识点可知无论是 JobScheduler 的 schedule 过程还是 cancel 过程,都涉及到两次的跨进程调用:

1.从 app 进程进入 system_server 进程的 JobSchedulerStub,采用 IJobScheduler 接口

2.system_server 进程的主线程来执行 bind Service,从而拉起目标进程,服务启动后会回调到发起端的 onServiceConnected;this.service 是指获取远程 IJobService 的代理端,然后通过 IJobService 接口,再次调用到 app 端

3.最终会回调目标应用中的 JobService 的 onStartJob() 方法,可见该方法运行在 app 进程的主线程,那么当存在耗时操作时则必须要采用异步方式,让耗时操作交给子线程去执行,这样就不会阻塞 app 的 UI 线程

本文参考博文

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JobScheduler是Android中的一个调度框架,它可以用来在特定的时间间隔或特定的条件下执行任务,同时也可以用来保持应用的活跃状态。 在Android系统中,应用程序在后台运行时有一定概率被系统回收释放内存,为了提高应用的用户体验和功能完整性,我们可以使用JobScheduler来保活应用。 具体实现方法如下: 1. 在AndroidManifest.xml文件中注册JobService,用于执行任务和保活应用。设置jobService的权限,以及设置该service的处理逻辑。 2. 在需要保活的地方调用JobScheduler的schedule()方法,创建一个JobInfo对象,并设置好相关参数。其中包括指定要执行的JobService、执行的触发条件(如设备空闲时、特定时间间隔、特定网络状态等)、设置重试或重复执行等。 3. 通过JobScheduler.schedule()方法将JobInfo对象提交给系统进行调度。系统会根据设置的条件和触发机制来执行任务。 需要注意的是,JobScheduler的保活机制是通过系统的调度来实现的,并不能保证100%的成功保活。因为在一些特殊的情况下,如低内存、电池低、系统启动等情况下,系统可能会暂停或取消JobScheduler的任务。所以我们还需要配合其他的保活机制来提高保活的成功率,如使用前台服务、双进程守护、自启动、推送等。 总结起来,JobScheduler是Android系统提供的一种调度框架,可以在一定程度上保活应用。通过设置JobInfo的相关参数,然后由系统进行调度执行,以确保应用的持续运行和活跃状态。同时,为了提高保活的成功率,我们还需要结合其他的保活机制来综合应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值