JobScheduler相关-使用及源码学习

前言

JobScheduler是Android5.0 开始引入了一个新系统服务。它将后台任务调度直接交给系统服务(JobSchedulerSevice)管理,并且可以设置许多约束条件,如周期调度,延迟调度,网络连接,电源插入,还有AndroidL引入的空闲模式,在条件符合的情况下,系统服务BindService的方式把应用内Manifest中配置的JobService启动起来,并通过进程间通信Binder方式调用JobService的onStartJob、onStopJob等方法来进行Job的管理。即便在执行任务之前应用程序进程被杀,也不会导致任务中断,Jobservice不会因应用退出而退出,但确实是运行在该应用进程中;常用于有条件的后台任务(约束条件为空不能执行),无约束条件的直接用IntentService;本质还是个Service,因此主线程,如(onStartJob)不能进行耗时操作,因此需要配合多线程的方式,如Handler,AsyncTask等使用

使用范例







    
        
            

            
        
    
    

public class MainActivity extends AppCompatActivity {

    private Button btn;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn = (Button)findViewById(R.id.button);
        ComponentName JobServicename = new ComponentName(this,TestJobService.class);
        final JobInfo.Builder job_builder = new JobInfo.Builder(0,JobServicename).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
        final JobScheduler jobScheduler = (JobScheduler)getSystemService(Context.JOB_SCHEDULER_SERVICE);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                jobScheduler.schedule(job_builder.build());
            }
        });

    }
}
import android.annotation.SuppressLint;
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.WifiManager;
import android.util.Log;

import java.util.concurrent.ExecutionException;

public class TestJobService extends JobService {

    private TestAsyncTask task = null;

    public TestJobService() {
        super();
    }
  
 //JobService本质还是个Service,因此onStartJob,onStopJob也会在主线程调用;因此耗时操作不能在onStartJob中执行,可以在其中用AsyncTask来进行耗时操作
    @Override
    public void onCreate() {
        Log.i("weijuncheng","----------------------------->onCreate");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("weijuncheng","----------------------------->onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Log.i("weijuncheng","----------------------------->onDestory");
        super.onDestroy();
    }

    @Override
    public boolean onStartJob(JobParameters params) {
        Log.i("weijuncheng","----------------------------->onStartJob");


        task = new TestAsyncTask();
        task.execute();
        //wifi确实disable了,但还是不调用onStopJob
        /*
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        WifiManager wifiManager = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
        if(wifiManager.isWifiEnabled()){
            wifiManager.setWifiEnabled(false);
            Log.i("weijuncheng","----------------------------->wifi disable");
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        */

        /*
        try {
            task.get(); //用于阻塞,得到AysncTask的返回结果(注意,在主线程不能调用get,因为是阻塞式的,如果调用就相当于主线程耗时,会触发ANR)
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        */
        //不要这段就是非阻塞的
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //WifiManager wifiManager = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
        //if(wifiManager.isWifiEnabled()){
        //    wifiManager.setWifiEnabled(false);
        //    Log.i("weijuncheng","----------------------------->wifi disable");
        //}
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.i("weijuncheng","----------------------------->onStartJob done");
        return true; //return false;就不会调用onStopJob;即使条件不再满足,也不会走到终止流程
   // 在任务开始执行时触发。返回false表示执行完毕,返回true表示需要开发者自己调用jobFinished方法通知系统已执行完成。
   
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        Log.i("weijuncheng","----------------------------->onStopJob");
        task.cancel(true);
        return false; //在任务停止执行时触发。返回true 为重新调度,返回false 表示不会重新调度,job完全被停止了
    }
}
import android.os.AsyncTask;
import android.util.Log;

public class TestAsyncTask extends AsyncTask {
    private boolean flag = true;

    @Override
    protected void onCancelled(Void aVoid) {
        flag = false;
        Log.i("weijuncheng","TestAsyncTask onCancelled 1");
        super.onCancelled(aVoid);
    }

    @Override
    protected void onCancelled() {
        flag = false;
        Log.i("weijuncheng","TestAsyncTask onCancelled 2");
        super.onCancelled();
    }

    @Override
    protected Void doInBackground(Void... voids) {
        Log.i("weijuncheng","TestAsyncTask doInBackground");
        new Thread(){
            @Override
            public void run() {
                super.run();
                for(int i = 0;i<5*60;i++){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if(flag) {
                        Log.i("weijuncheng", "TestAsyncTask doInBackground doing");
                    }else{
                        Log.i("weijuncheng","TestAsyncTask doInBackground cancelled");
                        break;
                    }



                }
            }
        }.start();
        try {
            Thread.sleep(1000*60*5); //模拟子线程中的耗时操作,这个操作需要5min
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        Log.i("weijuncheng","TestAsyncTask doInBackground done");
        return null;
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        Log.i("weijuncheng","TestAsyncTask onPostExecute done");
        super.onPostExecute(aVoid);
    }
}

执行一会关掉wifi,log如下

02-21 07:52:45.170  1755  1755 I weijuncheng: ----------------------------->onCreate
02-21 07:52:45.175  1755  1755 I weijuncheng: ----------------------------->onStartJob
02-21 07:52:45.177  1755  1789 I weijuncheng: TestAsyncTask doInBackground
02-21 07:52:46.178  1755  1791 I weijuncheng: TestAsyncTask doInBackground doing
02-21 07:52:47.177  1755  1755 I weijuncheng: ----------------------------->onStartJob done
02-21 07:52:47.179  1755  1791 I weijuncheng: TestAsyncTask doInBackground doing
02-21 07:52:48.179  1755  1791 I weijuncheng: TestAsyncTask doInBackground doing
02-21 07:52:49.179  1755  1791 I weijuncheng: TestAsyncTask doInBackground doing
02-21 07:52:52.180  1755  1791 I chatty  : uid=10106(com.test.weijuncheng.testjobscheduler) Thread-2 identical 3 lines
02-21 07:52:53.180  1755  1791 I weijuncheng: TestAsyncTask doInBackground doing
02-21 07:52:54.181  1755  1791 I weijuncheng: TestAsyncTask doInBackground doing
02-21 07:52:55.168  1755  1755 I weijuncheng: ----------------------------->onStopJob
02-21 07:52:55.170  1755  1789 W System.err: java.lang.InterruptedException
02-21 07:52:55.179  1755  1755 I weijuncheng: ----------------------------->onDestory
02-21 07:52:55.181  1755  1791 I weijuncheng: TestAsyncTask doInBackground doing
02-21 07:52:55.182  1755  1789 W System.err:    at java.lang.Thread.sleep(Native Method)
02-21 07:52:55.183  1755  1789 W System.err:    at java.lang.Thread.sleep(Thread.java:373)
02-21 07:52:55.183  1755  1789 W System.err:    at java.lang.Thread.sleep(Thread.java:314)
02-21 07:52:55.184  1755  1789 W System.err:    at com.test.weijuncheng.testjobscheduler.TestAsyncTask.doInBackground(TestAsyncTask.java:49)
02-21 07:52:55.184  1755  1789 W System.err:    at com.test.weijuncheng.testjobscheduler.TestAsyncTask.doInBackground(TestAsyncTask.java:6)
02-21 07:52:55.184  1755  1789 W System.err:    at android.os.AsyncTask$2.call(AsyncTask.java:333)
02-21 07:52:55.185  1755  1789 W System.err:    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
02-21 07:52:55.186  1755  1789 W System.err:    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
02-21 07:52:55.186  1755  1789 W System.err:    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
02-21 07:52:55.186  1755  1789 W System.err:    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
02-21 07:52:55.187  1755  1789 W System.err:    at java.lang.Thread.run(Thread.java:764)
02-21 07:52:55.187  1755  1789 I weijuncheng: TestAsyncTask doInBackground done
02-21 07:52:55.187  1755  1755 I weijuncheng: TestAsyncTask onCancelled 1
02-21 07:52:55.187  1755  1755 I weijuncheng: TestAsyncTask onCancelled 2
02-21 07:52:56.182  1755  1791 I weijuncheng: TestAsyncTask doInBackground cancelled

源码分析

jobscheduler的调用是一个典型的跨进程通信的流程
客户端(JobScheduler.scheduler)-system_server系统进程(中转)-服务端(提供JobService)

服务端-JobService

public abstract class JobService extends Service {
    private static final String TAG = "JobService";

    /**
     * Job services must be protected with this permission:
     *
     * 
     *     <service android:name="MyJobService"
     *              android:permission="android.permission.BIND_JOB_SERVICE" >
     *         ...
     *     </service>
     * 
* *

If a job service is declared in the manifest but not protected with this * permission, that service will be ignored by the system. */ public static final String PERMISSION_BIND = "android.permission.BIND_JOB_SERVICE"; private JobServiceEngine mEngine; /** @hide */ public final IBinder onBind(Intent intent) { if (mEngine == null) { mEngine = new JobServiceEngine(this) { @Override public boolean onStartJob(JobParameters params) { return JobService.this.onStartJob(params); } @Override public boolean onStopJob(JobParameters params) { return JobService.this.onStopJob(params); } }; } return mEngine.getBinder(); } public abstract boolean onStartJob(JobParameters params); public abstract boolean onStopJob(JobParameters params);

服务端本质上还是个Service,可以被bindService,从而通过返回的binder对象调用JobService中重载的onStartJob和onStopJob

客户端-JobScheduler.scheduler

当调用scheduler() 便开始启动该job 的任务,但是不一定立马执行

@SystemService(Context.JOB_SCHEDULER_SERVICE)
public abstract class JobScheduler {
    /** @hide */
    @IntDef(prefix = { "RESULT_" }, value = {
            RESULT_FAILURE,
            RESULT_SUCCESS,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface Result {}

    /**
     * Returned from {@link #schedule(JobInfo)} when an invalid parameter was supplied. This can occur
     * if the run-time for your job is too short, or perhaps the system can't resolve the
     * requisite {@link JobService} in your package.
     */
    public static final int RESULT_FAILURE = 0;
    /**
     * Returned from {@link #schedule(JobInfo)} if this job has been successfully scheduled.
     */
    public static final int RESULT_SUCCESS = 1;

    /**
     * Schedule a job to be executed.  Will replace any currently scheduled job with the same
     * ID with the new information in the {@link JobInfo}.  If a job with the given ID is currently
     * running, it will be stopped.
     *
     * @param job The job you wish scheduled. See
     * {@link android.app.job.JobInfo.Builder JobInfo.Builder} for more detail on the sorts of jobs
     * you can schedule.
     * @return the result of the schedule request.
     */
    public abstract @Result int schedule(@NonNull JobInfo job);

其实现类为JobSchedulerImpl

/**
 * Concrete implementation of the JobScheduler interface
 * @hide 
 */
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;
        }
    }

通过binder call调用到system_server中的JobSchedulerService

system_server端-JobSchedulerService

构造函数

/**
 * Initializes the system service.
 * 

* Subclasses must define a single argument constructor that accepts the context * and passes it to super. *

* * @param context The system server context. */ public JobSchedulerService(Context context) { super(context); mLocalPM = LocalServices.getService(PackageManagerInternal.class); mActivityManagerInternal = Preconditions.checkNotNull( LocalServices.getService(ActivityManagerInternal.class)); mHandler = new JobHandler(context.getMainLooper()); mConstants = new Constants(); mConstantsObserver = new ConstantsObserver(mHandler); mJobSchedulerStub = new JobSchedulerStub(); // Set up the app standby bucketing tracker mStandbyTracker = new StandbyTracker(); mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class); mUsageStats.addAppIdleStateChangeListener(mStandbyTracker); // The job store needs to call back publishLocalService(JobSchedulerInternal.class, new LocalService()); // Initialize the job store and set up any persisted jobs mJobs = JobStore.initAndGet(this); // 初始化JobStore ,Jobstore 是干嘛的呢,主要是存放Job 任务的一个列表,并记录Job 的运行情况,时长等详细信息到/data/system/job/jobs.xml // Create the controllers. mControllers = new ArrayList(); mControllers.add(new ConnectivityController(this)); mControllers.add(new TimeController(this)); mControllers.add(new IdleController(this)); mBatteryController = new BatteryController(this); mControllers.add(mBatteryController); mStorageController = new StorageController(this); mControllers.add(mStorageController); mControllers.add(new BackgroundJobsController(this)); mControllers.add(new ContentObserverController(this)); mDeviceIdleJobsController = new DeviceIdleJobsController(this); mControllers.add(mDeviceIdleJobsController); // 初始化各种controller ,每个controller 都对应着JobInfo 里面的一个set 的Job运行条件,目前共有连接,时间,idle(设备空闲),充电,存储,AppIdle,ContentObserver,DeviceIdle(Doze) 的控制器 // If the job store determined that it can't yet reschedule persisted jobs, // we need to start watching the clock. if (!mJobs.jobTimesInflatedValid()) { Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling"); context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED)); } }

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();
    final int userId = UserHandle.getUserId(uid);

    enforceValidJobRequest(uid, job);
    if (job.isPersisted()) {
        if (!canPersistJobs(pid, uid)) {
            throw new IllegalArgumentException("Error: requested job be persisted without"
                    + " holding RECEIVE_BOOT_COMPLETED permission.");
        }
    }

    validateJobFlags(job, uid);

    long ident = Binder.clearCallingIdentity();
    try {
        return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
                null);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

JobSchedulerService#scheduleAsPackage

public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
        int userId, String tag) {
    try {
        if (ActivityManager.getService().isAppStartModeDisabled(uId,
                job.getService().getPackageName())) {  //这里通过AMS来判断packageName该应该是否允许启动该服务
            Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
                    + " -- package not allowed to start");
            return JobScheduler.RESULT_FAILURE;
        }
    } catch (RemoteException e) {
    }

    synchronized (mLock) {
        final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId()); // 判断系统中该Uid 的App对应的Jobid 是否已经存在系统中

        if (work != null && toCancel != null) {
            // Fast path: we are adding work to an existing job, and the JobInfo is not
            // changing.  We can just directly enqueue this work in to the job.
            if (toCancel.getJob().equals(job)) {

                toCancel.enqueueWorkLocked(ActivityManager.getService(), work);

                // If any of work item is enqueued when the source is in the foreground,
                // exempt the entire job.
                toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);

                return JobScheduler.RESULT_SUCCESS;
            }
        }

        JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag); //根据jobInfo创建jobStatus

        // Give exemption if the source is in the foreground just now.
        // Note if it's a sync job, this method is called on the handler so it's not exactly
        // the state when requestSync() was called, but that should be fine because of the
        // 1 minute foreground grace period.
        jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);

        if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
        // Jobs on behalf of others don't apply to the per-app job cap
        if (ENFORCE_MAX_JOBS && packageName == null) {
            if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
                Slog.w(TAG, "Too many jobs for uid " + uId);
                throw new IllegalStateException("Apps may not schedule more than "
                            + MAX_JOBS_PER_APP + " distinct jobs");
            }
        }

        // This may throw a SecurityException.
        jobStatus.prepareLocked(ActivityManager.getService());

        if (toCancel != null) {
            cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app"); // 如果系统中已经存在了同一个uid 里面的同一个jobId 的job ,那么先cancle 这个job
        }
        if (work != null) {
            // If work has been supplied, enqueue it into the new job.
            jobStatus.enqueueWorkLocked(ActivityManager.getService(), work); //如果执行了work队列那么 将jobStatus 放入指定的work队列里
        }
        startTrackingJobLocked(jobStatus, toCancel); // 开始将App 的所有的Job的放到mJobs里表里,并且对每个job 指定对应的不同Controller
        StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
                uId, null, jobStatus.getBatteryName(),
                StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
                JobProtoEnums.STOP_REASON_CANCELLED);

        // If the job is immediately ready to run, then we can just immediately
        // put it in the pending list and try to schedule it.  This is especially
        // important for jobs with a 0 deadline constraint, since they will happen a fair
        // amount, we want to handle them as quickly as possible, and semantically we want to
        // make sure we have started holding the wake lock for the job before returning to
        // the caller.
        // If the job is not yet ready to run, there is nothing more to do -- we are
        // now just waiting for one of its controllers to change state and schedule
        // the job appropriately.
        if (isReadyToBeExecutedLocked(jobStatus)) {
           //如果一个job 满足一定条件需要立即执行,那么会将其放在pending 列表中,并且在后面马上处理
            // This is a new job, we can just immediately put it on the pending
            // list and try to run it.
            mJobPackageTracker.notePending(jobStatus);
            addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
            maybeRunPendingJobsLocked();
        }
    }
    return JobScheduler.RESULT_SUCCESS;
}

JobSchedulerService#startTrackingJobLocked

当设置一个Job 的时候,会循环所有的Controller 来为其指定限制该Job 的Controller

private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
    if (!jobStatus.isPreparedLocked()) {
        Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
    }
    jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
    final boolean update = mJobs.add(jobStatus);
    if (mReadyToRock) {
        for (int i = 0; i < mControllers.size(); i++) {
            StateController controller = mControllers.get(i);
            if (update) {
                controller.maybeStopTrackingJobLocked(jobStatus, null, true);
            }
            controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
        }
    }
}

如ConnectivityController

ConnectivityController#maybeStartTrackingJobLocked

@GuardedBy("mLock")
@Override
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
    if (jobStatus.hasConnectivityConstraint()) {
        updateConstraintsSatisfied(jobStatus);
        mTrackedJobs.add(jobStatus); //添加到ConnectivityController的mTrackedJobs中
        jobStatus.setTrackingController(JobStatus.TRACKING_CONNECTIVITY); //为JobStatus添加相应Controller
    }
}

监听条件变化,当条件变化时;ConnectivityController便是在注册ConnectivityManager.OnNetworkActiveListener,当连接状态发生改变时候,更新状态参数

/**
 * Callback for use with {@link ConnectivityManager#addDefaultNetworkActiveListener}
 * to find out when the system default network has gone in to a high power state.
 */
public interface OnNetworkActiveListener {
    /**
     * Called on the main thread of the process to report that the current data network
     * has become active, and it is now a good time to perform any pending network
     * operations.  Note that this listener only tells you when the network becomes
     * active; if at any other time you want to know whether it is active (and thus okay
     * to initiate network traffic), you can retrieve its instantaneous state with
     * {@link ConnectivityManager#isDefaultNetworkActive}.
     */
    public void onNetworkActive();
}
/**
 * We know the network has just come up. We want to run any jobs that are ready.
 */
@Override
public void onNetworkActive() {
    synchronized (mLock) {
        for (int i = mTrackedJobs.size()-1; i >= 0; i--) {
            final JobStatus js = mTrackedJobs.valueAt(i); //保存Controller track的JobStatus
            if (js.isReady()) {
                if (DEBUG) {
                    Slog.d(TAG, "Running " + js + " due to network activity.");
                }
                mStateChangedListener.onRunJobNow(js);
            }
        }
    }
}

JobSchedulerService#maybeRunPendingJobsLocked

执行pending list中的待执行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 maybeRunPendingJobsLocked() {
    if (DEBUG) {
        Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
    }
    assignJobsToContextsLocked();
    reportActiveLocked();
}
/**
 * Takes jobs from pending queue and runs them on available contexts.
 * If no contexts are available, preempts lower priority jobs to
 * run higher priority ones.
 * Lock on mJobs before calling this function.
 */
private void assignJobsToContextsLocked() {
    if (DEBUG) {
        Slog.d(TAG, printPendingQueue());
    }

    int memLevel;
    try {
        memLevel = ActivityManager.getService().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;
    boolean[] act = mTmpAssignAct;
    int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
    int numActive = 0;
    int numForeground = 0;
    for (int i=0; i= JobInfo.PRIORITY_TOP_APP) {
                numForeground++;
            }
        }
        act[i] = false;
        preferredUidForContext[i] = js.getPreferredUid();
    }
    if (DEBUG) {
        Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
    }
    for (int i=0; i= JobInfo.PRIORITY_TOP_APP &&
                                numForeground < mConstants.FG_JOB_COUNT)) &&
                        (preferredUid == nextPending.getUid() ||
                                preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
                    // This slot is free, and we haven't yet hit the limit on
                    // concurrent jobs...  we can just throw the job in to here.
                    minPriorityContextId = j;
                    break;
                }
                // No job on this context, but nextPending can't run here because
                // the context has a preferred Uid or we have reached the limit on
                // concurrent jobs.
                continue;
            }
            if (job.getUid() != nextPending.getUid()) {
                continue;
            }
            if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
                continue;
            }
            if (minPriority > nextPending.lastEvaluatedPriority) {
                minPriority = nextPending.lastEvaluatedPriority;
                minPriorityContextId = j;
            }
        }
        if (minPriorityContextId != -1) {
            contextIdToJobMap[minPriorityContextId] = nextPending;
            act[minPriorityContextId] = true;
            numActive++;
            if (priority >= JobInfo.PRIORITY_TOP_APP) {
                numForeground++;
            }
        }
    }
    if (DEBUG) {
        Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
    }
    mJobPackageTracker.noteConcurrency(numActive, numForeground);
    for (int i=0; i

在assignJobsToContextsLocked 完成各种计算后,将mActiveService的可以运行的Job,调用executeRunnableJob 方法;调用到JobServiceContext 中,开始启动服务bindService。在当服务binder上App端的JobService 服务,回调回onServiceConnected()

JobServiceContext#executeRunnableJob

/**
 * Give a job to this context for execution. Callers must first check {@link #getRunningJobLocked()}
 * and ensure it is null to make sure this is a valid context.
 * @param job The status of the job that we are going to run.
 * @return True if the job is valid and is running. False if the job cannot be executed.
 */
boolean executeRunnableJob(JobStatus job) {
    synchronized (mLock) {
        if (!mAvailable) {
            Slog.e(TAG, "Starting new runnable but context is unavailable > Error.");
            return false;
        }

        mPreferredUid = NO_PREFERRED_UID;

        mRunningJob = job;
        mRunningCallback = new JobCallback();
        final boolean isDeadlineExpired =
                job.hasDeadlineConstraint() &&
                        (job.getLatestRunTimeElapsed() < sElapsedRealtimeClock.millis());
        Uri[] triggeredUris = null;
        if (job.changedUris != null) {
            triggeredUris = new Uri[job.changedUris.size()];
            job.changedUris.toArray(triggeredUris);
        }
        String[] triggeredAuthorities = null;
        if (job.changedAuthorities != null) {
            triggeredAuthorities = new String[job.changedAuthorities.size()];
            job.changedAuthorities.toArray(triggeredAuthorities);
        }
        final JobInfo ji = job.getJob();
        mParams = new JobParameters(mRunningCallback, job.getJobId(), ji.getExtras(),
                ji.getTransientExtras(), ji.getClipData(), ji.getClipGrantFlags(),
                isDeadlineExpired, triggeredUris, triggeredAuthorities, job.network);
        mExecutionStartTimeElapsed = sElapsedRealtimeClock.millis();

        final long whenDeferred = job.getWhenStandbyDeferred();
        if (whenDeferred > 0) {
            final long deferral = mExecutionStartTimeElapsed - whenDeferred;
            EventLog.writeEvent(EventLogTags.JOB_DEFERRED_EXECUTION, deferral);
            if (DEBUG_STANDBY) {
                StringBuilder sb = new StringBuilder(128);
                sb.append("Starting job deferred for standby by ");
                TimeUtils.formatDuration(deferral, sb);
                sb.append(" ms : ");
                sb.append(job.toShortString());
                Slog.v(TAG, sb.toString());
            }
        }

        // Once we'e begun executing a job, we by definition no longer care whether
        // it was inflated from disk with not-yet-coherent delay/deadline bounds.
        job.clearPersistedUtcTimes();

        mVerb = VERB_BINDING;
        scheduleOpTimeOutLocked();
        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()));; //根据jobStatus中的信息调用bindService


        if (!binding) {
            if (DEBUG) {
                Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable.");
            }
            mRunningJob = null;
            mRunningCallback = null;
            mParams = null;
            mExecutionStartTimeElapsed = 0L;
            mVerb = VERB_FINISHED;
            removeOpTimeOutLocked();
            return false;
        }
        mJobPackageTracker.noteActive(job);
        try {
            mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
        } catch (RemoteException e) {
            // Whatever.
        }
        final String jobPackage = job.getSourcePackageName();
        final int jobUserId = job.getSourceUserId();
        UsageStatsManagerInternal usageStats =
                LocalServices.getService(UsageStatsManagerInternal.class);
        usageStats.setLastJobRunTime(jobPackage, jobUserId, mExecutionStartTimeElapsed);
        JobSchedulerInternal jobScheduler =
                LocalServices.getService(JobSchedulerInternal.class);
        jobScheduler.noteJobStart(jobPackage, jobUserId);
        mAvailable = false;
        mStoppedReason = null;
        mStoppedTime = 0;
        return true;
    }
}

JobServiceContext#onServiceConnected

/**
 * We acquire/release a wakelock on onServiceConnected/unbindService. This mirrors the work
 * we intend to send to the client - we stop sending work when the service is unbound so until
 * then we keep the wakelock.
 * @param name The concrete component name of the service that has been connected.
 * @param service The IBinder of the Service's communication channel,
 */
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
    JobStatus runningJob;
    synchronized (mLock) {
        // This isn't strictly necessary b/c the JobServiceHandler is running on the main
        // looper and at this point we can't get any binder callbacks from the client. Better
        // safe than sorry.
        runningJob = mRunningJob;

        if (runningJob == null || !name.equals(runningJob.getServiceComponent())) {
            closeAndCleanupJobLocked(true /* needsReschedule */,
                    "connected for different component");
            return;
        }
        this.service = IJobService.Stub.asInterface(service);
        final PowerManager pm =
                (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
        PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                runningJob.getTag());
        wl.setWorkSource(deriveWorkSource(runningJob));
        wl.setReferenceCounted(false);
        wl.acquire();

        // We use a new wakelock instance per job.  In rare cases there is a race between
        // teardown following job completion/cancellation and new job service spin-up
        // such that if we simply assign mWakeLock to be the new instance, we orphan
        // the currently-live lock instead of cleanly replacing it.  Watch for this and
        // explicitly fast-forward the release if we're in that situation.
        if (mWakeLock != null) {
            Slog.w(TAG, "Bound new job " + runningJob + " but live wakelock " + mWakeLock
                    + " tag=" + mWakeLock.getTag());
            mWakeLock.release();
        }
        mWakeLock = wl;
        doServiceBoundLocked();
    }
}
@GuardedBy("mLock")
void doServiceBoundLocked() {
    removeOpTimeOutLocked();
    handleServiceBoundLocked();
}
/** Start the job on the service. */
@GuardedBy("mLock")
private void handleServiceBoundLocked() {
    if (DEBUG) {
        Slog.d(TAG, "handleServiceBound for " + getRunningJobNameLocked());
    }
    if (mVerb != VERB_BINDING) {
        Slog.e(TAG, "Sending onStartJob for a job that isn't pending. "
                + VERB_STRINGS[mVerb]);
        closeAndCleanupJobLocked(false /* reschedule */, "started job not pending");
        return;
    }
    if (mCancelled) {
        if (DEBUG) {
            Slog.d(TAG, "Job cancelled while waiting for bind to complete. "
                    + mRunningJob);
        }
        closeAndCleanupJobLocked(true /* reschedule */, "cancelled while waiting for bind");
        return;
    }
    try {
        mVerb = VERB_STARTING;
        scheduleOpTimeOutLocked();
        service.startJob(mParams); //调用onStartJob
    } catch (Exception e) {
        // We catch 'Exception' because client-app malice or bugs might induce a wide
        // range of possible exception-throw outcomes from startJob() and its handling
        // of the client's ParcelableBundle extras.
        Slog.e(TAG, "Error sending onStart message to '" +
                mRunningJob.getServiceComponent().getShortClassName() + "' ", e);
    }
}

总结

源码的大致流程如图


JobScheduler.png

使用时需要注意,需要注意的是这个job service运行在你的主线程,这意味着你需要使用子线程,handler, 或者一个异步任务来运行耗时的操作以防止阻塞主线程

你可能感兴趣的:(JobScheduler相关-使用及源码学习)