前言
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);
}
}
总结
源码的大致流程如图
使用时需要注意,需要注意的是这个job service运行在你的主线程,这意味着你需要使用子线程,handler, 或者一个异步任务来运行耗时的操作以防止阻塞主线程