JobIntentService源碼解析
一、什麼是JobIntentService
JobIntentService用於執行加入到隊列中的任務,在 android O或更高版本上運行時,工作將通過 jobscheduler 作爲作業分派。 在舊版本上運行時,它將使用 Context.startService
二、JobIntentService源碼分析
先來看下基本使用示例:
public class SimpleJobIntentService extends JobIntentService {
/**
* Unique job ID for this service.
*/
static final int JOB_ID = 1000;
/**
* Convenience method for enqueuing work in to this service.
*/
static void enqueueWork(Context context, Intent work) {
enqueueWork(context, SimpleJobIntentService.class, JOB_ID, work);
}
@Override
protected void onHandleWork(Intent intent) {
// We have received work to do. The system or framework is already
// holding a wake lock for us at this point, so we can just go.
Log.i("SimpleJobIntentService", "Executing work: " + intent);
String label = intent.getStringExtra("label");
if (label == null) {
label = intent.toString();
}
toast("Executing: " + label);
for (int i = 0; i < 5; i++) {
Log.i("SimpleJobIntentService", "Running service " + (i + 1)
+ "/5 @ " + SystemClock.elapsedRealtime());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
Log.i("SimpleJobIntentService", "Completed service @ " + SystemClock.elapsedRealtime());
}
@Override
public void onDestroy() {
super.onDestroy();
toast("All work complete");
}
final Handler mHandler = new Handler();
// Helper for showing tests
void toast(final CharSequence text) {
mHandler.post(new Runnable() {
@Override public void run() {
Toast.makeText(SimpleJobIntentService.this, text, Toast.LENGTH_SHORT).show();
}
});
}
}
上面是Google提供的JobIntentService使用示例。
基本流程是繼承JobIntentService重寫onHandleWork方法,並在該方法內部處理收到的任務。任務的提交則是在enqueueWork中。
接下來我們來看下JobIntentService的源碼,我們從enqueueWork開始
public static void enqueueWork(@NonNull Context context, @NonNull Class cls, int jobId,
@NonNull Intent work) {
enqueueWork(context, new ComponentName(context, cls), jobId, work);//注意此處構造的ComponentName傳入的class是JobIntentService.class 後面會用到
}
public static void enqueueWork(@NonNull Context context, @NonNull ComponentName component,
int jobId, @NonNull Intent work) {
if (work == null) {
throw new IllegalArgumentException("work must not be null");
}
synchronized (sLock) {
WorkEnqueuer we = getWorkEnqueuer(context, component, true, jobId);
we.ensureJobId(jobId);
we.enqueueWork(work);
}
}
static WorkEnqueuer getWorkEnqueuer(Context context, ComponentName cn, boolean hasJobId,
int jobId) {
WorkEnqueuer we = sClassWorkEnqueuer.get(cn);
if (we == null) {
if (Build.VERSION.SDK_INT >= 26) {//根據不同的版本號構造不同的實現類
if (!hasJobId) {
throw new IllegalArgumentException("Can't be here without a job id");
}
we = new JobWorkEnqueuer(context, cn, jobId);
} else {
we = new CompatWorkEnqueuer(context, cn);
}
sClassWorkEnqueuer.put(cn, we);
}
return we;
}
enqueueWork內部經過調用最終通過getWorkEnqueuer來獲取一個WorkEnqueuer類型實例並調用了WorkEnqueuer的enqueueWork把intent作爲參數傳入。getWorkEnqueuer內部會根據系統版本的不同構造不同的WorkEnqueuer,簡單來說就是Android 8.0以及上返回的是JobWorkEnqueuer實例,Android 8.0之前版本返回CompatWorkEnqueuer實例。JobWorkEnqueuer和CompatWorkEnqueuer都是WorkEnqueuer的實現類。
WorkEnqueuer到底是幹嘛的呢,一起來看下
abstract static class WorkEnqueuer {
final ComponentName mComponentName;
boolean mHasJobId;
int mJobId;
WorkEnqueuer(Context context, ComponentName cn) {
mComponentName = cn;
}
void ensureJobId(int jobId) {
if (!mHasJobId) {
mHasJobId = true;
mJobId = jobId;
} else if (mJobId != jobId) {
throw new IllegalArgumentException("Given job ID " + jobId
+ " is different than previous " + mJobId);
}
}
abstract void enqueueWork(Intent work);
public void serviceStartReceived() {
}
public void serviceProcessingStarted() {
}
public void serviceProcessingFinished() {
}
}
WorkEnqueuer是JobIntentService的一個內部抽象類,其主要作用類似一個任務分發的中轉站,因爲JobIntentService針對不同Android版本最後執行的操作不同,所以我們需要把啓動時傳入的任務交給WorkEnqueuer,WorkEnqueuer收到任務後根據不同Android版本構造不同的實現類去處理這些任務。
從getWorkEnqueuer開始之後的流程大多是根據系統版本分爲兩個分支即Android 8.0以及上、Android 8.0之前版本。所以我們分別來看下這兩個不同分支的流程。
Android O之前版本
getWorkEnqueuer返回的是CompatWorkEnqueuer類型實例。
CompatWorkEnqueuer是用來處理Android 8.0以下版本的任務。
static final class CompatWorkEnqueuer extends WorkEnqueuer {
private final Context mContext;
private final PowerManager.WakeLock mLaunchWakeLock;
private final PowerManager.WakeLock mRunWakeLock;
boolean mLaunchingService;
boolean mServiceProcessing;
CompatWorkEnqueuer(Context context, ComponentName cn) {
super(context, cn);
mContext = context.getApplicationContext();
// Make wake locks. We need two, because the launch wake lock wants to have
// a timeout, and the system does not do the right thing if you mix timeout and
// non timeout (or even changing the timeout duration) in one wake lock.
//此處需要注意的是因爲需要喚醒鎖 所以使用JobIntentService
需要android.Manifest.permission.WAKE_LOCK權限
PowerManager pm = ((PowerManager) context.getSystemService(Context.POWER_SERVICE));
mLaunchWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
cn.getClassName() + ":launch");
mLaunchWakeLock.setReferenceCounted(false);
mRunWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
cn.getClassName() + ":run");
mRunWakeLock.setReferenceCounted(false);
}
@Override
void enqueueWork(Intent work) {
Intent intent = new Intent(work);
intent.setComponent(mComponentName);
if (DEBUG) Log.d(TAG, "Starting service for work: " + work);
if (mContext.startService(intent) != null) {//調用startService來處理
synchronized (this) {
if (!mLaunchingService) {
mLaunchingService = true;
if (!mServiceProcessing) {
// If the service is not already holding the wake lock for
// itself, acquire it now to keep the system running until
// we get this work dispatched. We use a timeout here to
// protect against whatever problem may cause it to not get
// the work.
mLaunchWakeLock.acquire(60 * 1000); }
}
}
}
}
@Override
public void serviceStartReceived() {
synchronized (this) {
// Once we have started processing work, we can count whatever last
// enqueueWork() that happened as handled.
mLaunchingService = false;
}
}
@Override
public void serviceProcessingStarted() {
synchronized (this) {
// We hold the wake lock as long as the service is processing commands.
if (!mServiceProcessing) {
mServiceProcessing = true;
// Keep the device awake, but only for at most 10 minutes at a time
// (Similar to JobScheduler.)
mRunWakeLock.acquire(10 * 60 * 1000L);
mLaunchWakeLock.release();
}
}
}
@Override
public void serviceProcessingFinished() {
synchronized (this) {
if (mServiceProcessing) {
// If we are transitioning back to a wakelock with a timeout, do the same
// as if we had enqueued work without the service running.
if (mLaunchingService) {
mLaunchWakeLock.acquire(60 * 1000);
}
mServiceProcessing = false;
mRunWakeLock.release();
}
}
}
}
CompatWorkEnqueuer中我們主要關注下其enqueueWork
函數。在該函數內部調用了startService來啓動一個service,這裏這個service就是JobIntentService,因爲傳入的intent中的ComponentName是JobIntentService,這個Component的構造是在上面的JobIntentService的enqueueWork函數中完成的。
void enqueueWork(Intent work) {
Intent intent = new Intent(work);
intent.setComponent(mComponentName);
if (DEBUG) Log.d(TAG, "Starting service for work: " + work);
if (mContext.startService(intent) != null) {//1 調用是startService啓動JobIntentService
synchronized (this) {
if (!mLaunchingService) {
mLaunchingService = true;
if (!mServiceProcessing) {
// If the service is not already holding the wake lock for
// itself, acquire it now to keep the system running until
// we get this work dispatched. We use a timeout here to
// protect against whatever problem may cause it to not get
// the work.
mLaunchWakeLock.acquire(60 * 1000);
}
}
}
}
}
JobIntentService繼承自service,JobIntentService啓動會先調用構造函數,看下其構造函數
public JobIntentService() {
if (Build.VERSION.SDK_INT >= 26) {
mCompatQueue = null;
} else {
mCompatQueue = new ArrayList<>();
}
}
mCompatQueue是一個CompatWorkItem list,用來存儲當前的CompatWorkItem。CompatWorkItem繼承自GenericWorkItem
,GenericWorkItem是描述一個正在分發的任務的抽象,GenericWorkItem有兩個實現類CompatWorkItem和WrapperWorkItem。
interface GenericWorkItem {
Intent getIntent();
void complete();
}
CompatWorkItem適用於Android O之前的版本,主要是從service的onStartCommand函數中接收intent。
Android O之前版本在JobIntentService構造函數中爲mCompatQueue進行賦值。
接下來我們看下其onCreate函數
public void onCreate() {
super.onCreate();
if (DEBUG) Log.d(TAG, "CREATING: " + this);
if (Build.VERSION.SDK_INT >= 26) {
//...
} else {
mJobImpl = null;
ComponentName cn = new ComponentName(this, this.getClass());
mCompatWorkEnqueuer = getWorkEnqueuer(this, cn, false, 0);
}
}
可以看到是通過getWorkEnqueuer獲取一個WorkEnqueuer並賦值給mCompatWorkEnqueuer。因爲之前已經通過getWorkEnqueuer創建的CompatWorkEnqueuer實例此處會返回已經創建的CompatWorkEnqueuer實例。
之後service啓動根據生命週期會調用onStartCommand。
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
if (mCompatQueue != null) {//1 判斷mCompatQueue 是否爲空
mCompatWorkEnqueuer.serviceStartReceived();
if (DEBUG) Log.d(TAG, "Received compat start command #" + startId + ": " + intent);
synchronized (mCompatQueue) {
mCompatQueue.add(new CompatWorkItem(intent != null ? intent : new Intent(),
startId));//2 構造CompatWorkItem存到mCompatQueue
ensureProcessorRunningLocked(true);//3 調用ensureProcessorRunningLocked
}
return START_REDELIVER_INTENT;
} else {
if (DEBUG) Log.d(TAG, "Ignoring start command: " + intent);
return START_NOT_STICKY;
}
}
onStartCommand會判斷mCompatQueue 是否爲空,我們知道mCompatQueue 已經在構造函數中賦值了是非空的。然後會把當前的intent等信息封裝進CompatWorkItem並存到mCompatQueue中。之後調用了ensureProcessorRunningLocked。
CommandProcessor mCurProcessor;
void ensureProcessorRunningLocked(boolean reportStarted) {
if (mCurProcessor == null) {//1 判空
mCurProcessor = new CommandProcessor();//2 構造CommandProcessor
if (mCompatWorkEnqueuer != null && reportStarted) {
mCompatWorkEnqueuer.serviceProcessingStarted();
}
if (DEBUG) Log.d(TAG, "Starting processor: " + mCurProcessor);
mCurProcessor.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);//3 執行mCurProcessor
}
}
在ensureProcessorRunningLocked中首先會判斷mCurProcessor是否爲空,因爲之前並未給mCurProcessor賦值所以此處是爲空的。然後會構造CommandProcessor實例並給mCurProcessor賦值。之後就是執行mCurProcessor。
我們來看下 CommandProcessor,CommandProcessor繼承自AsyncTask,在上面的ensureProcessorRunningLocked函數中會在最後啓動這個AsyncTask,然後其最終會走到doInBackground函數中(這個涉及AsyncTask的相關知識 有興趣的可以自行查找相關資料 此處不再展開)。在其doInBackground函數中調用了onHandleWork。還記得我們之前的使用示例代碼麼我們是在onHandleWork處理任務的。
final class CommandProcessor extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
GenericWorkItem work;
if (DEBUG) Log.d(TAG, "Starting to dequeue work...");
while ((work = dequeueWork()) != null) {
if (DEBUG) Log.d(TAG, "Processing next work: " + work);
onHandleWork(work.getIntent());
if (DEBUG) Log.d(TAG, "Completing work: " + work);
work.complete();
}
if (DEBUG) Log.d(TAG, "Done processing work!");
return null;
}
@Override
protected void onCancelled(Void aVoid) {
processorFinished();
}
@Override
protected void onPostExecute(Void aVoid) {
processorFinished();
}
}
至此Android O之前版本(8.0之前版本)的流程就分析完了。
Android O以及之後的版本
在最開始的getWorkEnqueuer中如果是Android O以及之後的版本會構造JobWorkEnqueuer實例並返回。
static WorkEnqueuer getWorkEnqueuer(Context context, ComponentName cn, boolean hasJobId,
int jobId) {
WorkEnqueuer we = sClassWorkEnqueuer.get(cn);
if (we == null) {
if (Build.VERSION.SDK_INT >= 26) {
if (!hasJobId) {
throw new IllegalArgumentException("Can't be here without a job id");
}
we = new JobWorkEnqueuer(context, cn, jobId);//構造JobWorkEnqueuer實例
} else {
//...
}
sClassWorkEnqueuer.put(cn, we);
}
return we;
}
然後調用了JobWorkEnqueuer的enqueueWork方法。
JobWorkEnqueuer是用來處理Android 8.0及以上版本的任務分發。其enqueueWork的操作是調用JobScheduler.enqueue啓動JobIntentService
。
static final class JobWorkEnqueuer extends JobIntentService.WorkEnqueuer {
private final JobInfo mJobInfo;
private final JobScheduler mJobScheduler;
JobWorkEnqueuer(Context context, ComponentName cn, int jobId) {
super(context, cn);
ensureJobId(jobId);
JobInfo.Builder b = new JobInfo.Builder(jobId, mComponentName);//構造JobInfo 此處傳入的ComponentName是JobIntentService.class
mJobInfo = b.setOverrideDeadline(0).build();
mJobScheduler = (JobScheduler) context.getApplicationContext().getSystemService(
Context.JOB_SCHEDULER_SERVICE);
}
@Override
void enqueueWork(Intent work) {//enqueueWork的操作是調用JobScheduler.enqueue
if (DEBUG) Log.d(TAG, "Enqueueing work: " + work);
mJobScheduler.enqueue(mJobInfo, new JobWorkItem(work));
}
}
之後便是JobIntentService的啓動流程。
構造函數中mCompatQueue 置空,onCreate中構造JobServiceEngineImpl實例並賦值給mJobImpl。
public JobIntentService() {
if (Build.VERSION.SDK_INT >= 26) {
mCompatQueue = null;
} else {
//...
}
}
public void onCreate() {
super.onCreate();
if (DEBUG) Log.d(TAG, "CREATING: " + this);
if (Build.VERSION.SDK_INT >= 26) {
mJobImpl = new JobServiceEngineImpl(this);
mCompatWorkEnqueuer = null;
} else {
//...
}
}
JobServiceEngineImpl繼承自JobServiceEngine並實現了CompatJobEngine
接口
static final class JobServiceEngineImpl extends JobServiceEngine
implements JobIntentService.CompatJobEngine {
static final String TAG = "JobServiceEngineImpl";
static final boolean DEBUG = false;
final JobIntentService mService;
final Object mLock = new Object();
JobParameters mParams;
final class WrapperWorkItem implements JobIntentService.GenericWorkItem {
final JobWorkItem mJobWork;
WrapperWorkItem(JobWorkItem jobWork) {
mJobWork = jobWork;
}
@Override
public Intent getIntent() {
return mJobWork.getIntent();
}
@Override
public void complete() {
synchronized (mLock) {
if (mParams != null) {
mParams.completeWork(mJobWork);
}
}
}
}
JobServiceEngineImpl(JobIntentService service) {
super(service);
mService = service;
}
@Override
public IBinder compatGetBinder() {
return getBinder();
}
@Override
public boolean onStartJob(JobParameters params) {
if (DEBUG) Log.d(TAG, "onStartJob: " + params);
mParams = params;
// We can now start dequeuing work!
mService.ensureProcessorRunningLocked(false);
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
if (DEBUG) Log.d(TAG, "onStartJob: " + params);
boolean result = mService.doStopCurrentWork();
synchronized (mLock) {
// Once we return, the job is stopped, so its JobParameters are no
// longer valid and we should not be doing anything with them.
mParams = null;
}
return result;
}
/**
* Dequeue some work.
*/
@Override
public JobIntentService.GenericWorkItem dequeueWork() {
JobWorkItem work;
synchronized (mLock) {
if (mParams == null) {
return null;
}
work = mParams.dequeueWork();
}
if (work != null) {
work.getIntent().setExtrasClassLoader(mService.getClassLoader());
return new WrapperWorkItem(work);
} else {
return null;
}
}
}
之後就是JobService運行過程(感興趣的看去可以JobService使用解析 此處不再展開)最終會走到JobServiceEngineImpl的onStartJob方法。
public boolean onStartJob(JobParameters params) {
if (DEBUG) Log.d(TAG, "onStartJob: " + params);
mParams = params;
// We can now start dequeuing work!
mService.ensureProcessorRunningLocked(false);
return true;
}
onStartJob調用 mService.ensureProcessorRunningLocked即JobIntentService中的ensureProcessorRunningLocked,在Android 0之前版本流程分析中我們已經說明過,之後的流程跟Android 0之前版本流程是一樣的。
JobIntentService詳解及使用_Houson_c的博客-CSDN博客
JobIntentService - 一個學渣 - 博客園