JobIntentService源碼解析

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 - 一個學渣 - 博客園

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章