JobService源碼探究之 Job自行Cancel後即使onStopJob返回true也不會被再啓動

上章節遺留的疑問二,本章將繼續探究緣由。


疑問二

自行cancel了JobService後,onStopJob()即使返回true也不能被重新啓動?


照例直接上源碼。
cancel是IJobScheduler的API,我們直接查看該API的實現。


frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java


public final class JobSchedulerService extends com.android.server.SystemService
        implements StateChangedListener, JobCompletedListener {
    ...
    final class JobSchedulerStub extends IJobScheduler.Stub {
        ...
        @Override
        public void cancel(int jobId) throws RemoteException {
            final int uid = Binder.getCallingUid();
            long ident = Binder.clearCallingIdentity();
            try {
                JobSchedulerService.this.cancelJob(uid, jobId);★1
            }...
        }
    }
    ...
    public void cancelJob(int uid, int jobId) {
        JobStatus toCancel;
        synchronized (mLock) {
            toCancel = mJobs.getJobByUidAndJobId(uid, jobId); // 從JobStore中獲取指定UID下的指定ID的JobStatus
            if (toCancel != null) {
                cancelJobImplLocked(toCancel, null, "cancel() called by app");★2
            }
        }
    }
    ...
    private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {

        cancelled.unprepareLocked(ActivityManager.getService());
        stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */); // 從JobStore中移除該Job的追蹤記錄
        // Remove from pending queue.
        if (mPendingJobs.remove(cancelled)) {// 從運行中JobStatus列表中移除該Job
            mJobPackageTracker.noteNonpending(cancelled);
        }
        // Cancel if running.
        stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);★3 即將執行Job停止處理
        reportActiveLocked();
    }
    ...
    private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
        for (int i=0; i<mActiveServices.size(); i++) {
            JobServiceContext jsc = mActiveServices.get(i);
            final JobStatus executing = jsc.getRunningJobLocked();
            if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
                jsc.cancelExecutingJobLocked(reason, debugReason);★4告訴JobServiceContext去停止該Job
                return true;
            }
        }
        return false;
    }
}

我們繼續看看JobServiceContext具體做了什麼處理。


public final class JobServiceContext implements ServiceConnection {
    ...
    void cancelExecutingJobLocked(int reason, String debugReason) {
        doCancelLocked(reason, debugReason);★1
    }
    ...
    void doCancelLocked(int arg1, String debugReason) {
        ...
        mParams.setStopReason(arg1);// 設置REASON_CANCELED作爲停止Job原因
        if (arg1 == JobParameters.REASON_PREEMPT) {
            mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
                    NO_PREFERRED_UID;
        }
        handleCancelLocked(debugReason);★2
    }
    ...
    private void handleCancelLocked(String reason) {
        ...
        switch (mVerb) {
            case VERB_BINDING:
            case VERB_STARTING:
                mCancelled = true;
                applyStoppedReasonLocked(reason);★3-1 如果JobService還未執行走這個處理
                break;
            case VERB_EXECUTING:
                sendStopMessageLocked(reason);★3-2 如果JobService已經執行走這個處理
                break;
               ...
        }
    }

    // 假設JobService已經執行,那直接走到這
    private void sendStopMessageLocked(String reason) {
        removeOpTimeOutLocked();
        ...
        try {
            mVerb = VERB_STOPPING;//將Job狀態置爲VERB_STOPPING
            scheduleOpTimeOutLocked();
            service.stopJob(mParams);★4 IPC調用JobService的onStopJob()
        }...
    }
}


同樣的JobServcie處理完onStopJob()後會將返回值通過IJobCallback會發給JobServiceContext。

public abstract class JobServiceEngine {
    static final class JobInterface extends IJobService.Stub {
        ...
        public void stopJob(JobParameters jobParams) throws RemoteException {
            JobServiceEngine service = mService.get();
            if (service != null) {
                Message m = Message.obtain(service.mHandler, MSG_STOP_JOB, jobParams);★1
                m.sendToTarget();
            }
        }
    }
    ...
    class JobHandler extends Handler {
        ...
        @Override
        public void handleMessage(Message msg) {
            final JobParameters params = (JobParameters) msg.obj;
            switch (msg.what) {
                case MSG_STOP_JOB:
                    try {
                        boolean ret = JobServiceEngine.this.onStopJob(params);★2
                        ackStopMessage(params, ret);★3
                    }...
                ...
            }
        }
    }
    ...
    private void ackStopMessage(JobParameters params, boolean reschedule) {
        final IJobCallback callback = params.getCallback();
        final int jobId = params.getJobId();
        if (callback != null) {
            try {
                callback.acknowledgeStopMessage(jobId, reschedule);★4
            }...
        }...
   }
}


我們假設onStopJob()返回的是true,那麼★4傳給JobServiceContext的reschedule就是true。


再次回到JobServiceContext。


public final class JobServiceContext implements ServiceConnection {
    ...
    final class JobCallback extends IJobCallback.Stub {
        @Override
        public void acknowledgeStopMessage(int jobId, boolean reschedule) {
            doAcknowledgeStopMessage(this, jobId, reschedule);★1
        }
        ...
    }
    ...
    void doAcknowledgeStopMessage(JobCallback cb, int jobId, boolean reschedule) {
        doCallback(cb, reschedule, null);★2
    }
    ...
    void doCallback(JobCallback cb, boolean reschedule, String reason) {
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mLock) {
                if (!verifyCallerLocked(cb)) {
                    return;
                }
                doCallbackLocked(reschedule, reason);★3 此刻的reschedule爲true
            }
        }...
    }
    ...
    void doCallbackLocked(boolean reschedule, String reason) {
        ...
        removeOpTimeOutLocked();//在這裏移除回調onStopJob時發送的8s延時msg

        if (mVerb == VERB_STARTING) {
            handleStartedLocked(reschedule);
        } else if (mVerb == VERB_EXECUTING ||
                mVerb == VERB_STOPPING) {
            handleFinishedLocked(reschedule, reason);★4 此刻狀態爲STOPPING,調用finish邏輯
        }...
    }
    ...
    private void handleFinishedLocked(boolean reschedule, String reason) {
        switch (mVerb) {
            case VERB_EXECUTING:
            case VERB_STOPPING:
                closeAndCleanupJobLocked(reschedule, reason);★5 
                break;
            ...
        }
    }
    ...
    private void closeAndCleanupJobLocked(boolean reschedule, String reason) {
        ...
        applyStoppedReasonLocked(reason);
        completedJob = mRunningJob;
        mJobPackageTracker.noteInactive(completedJob);
        ...
        if (mWakeLock != null) {
            mWakeLock.release();//釋放WakeLock
        }
        mContext.unbindService(JobServiceContext.this);★6告訴AMS解綁該JobService,最終會調到它的onDestroy()
        mWakeLock = null;
        mRunningJob = null;
        mRunningCallback = null;
        mParams = null;
        mVerb = VERB_FINISHED;// 將Job狀態置爲VERB_FINISHED
        mCancelled = false;
        service = null;
        mAvailable = true;
        removeOpTimeOutLocked();
        mCompletedListener.onJobCompletedLocked(completedJob, reschedule); ★7 將結束的Job當作參數調用結束後回調處理
    }
    ...
    public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
        ...
        final JobStatus rescheduledJob = needsReschedule
                ? getRescheduleJobForFailureLocked(jobStatus) : null; // 從結束的Job中複製出新的JobStatus
        ...
        // 從JobStore中移除原有Job的追蹤記錄,因爲之前的JobStatus在cancel的時候已經從JobStore中移除了。所以本函數返回false
        // 然後發送MSG_CHECK_JOB_GREEDY的msg
        if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
            ...
            mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();★8
            return;
        }
        ...
    }

    final private class JobHandler extends Handler {
        ...
        @Override
        public void handleMessage(Message message) {
            synchronized (mLock) {
                ...
                switch (message.what) {
                    ...
                    case MSG_CHECK_JOB_GREEDY:
                        queueReadyJobsForExecutionLocked();★9 將準備好執行的Jobs排入隊列
                        break;
                    ...
                }
                maybeRunPendingJobsLocked(); ★10 輪循mPendingJobs準備執行JobService
                ...
            }
        }
    }

    private void queueReadyJobsForExecutionLocked() {
        ...
        // ★9-1 遍歷通過JobStore中存儲的所有Job,然後回調ReadyQueueFunctor添加Job到ReadyJobQueueFunctor中
        mJobs.forEachJob(mReadyQueueFunctor);
        
        // 將ReadyJobQueueFunctor中保存的jobs列表添加到待執行列表mPendingJobs中
        mReadyQueueFunctor.postProcess();
    }
}

執行cancel處理的時候已經從JobStore裏移除了原有的JobStatus對象,
同時在發送MSG_CHECK_JOB_GREEDY前有沒有將新的JobStatus對象保存到JobStore中。


所以★9-1在遍歷存儲的Jobs時候,並不會將新的JobStatus插入到待執行隊列裏。
進而導致★10的時候不會重新執行該JobService。


那爲什麼發生條件不滿足時候Job被強制停止後能再度啓動呢?
查看源碼我們知道。


比如設備進入IDLE狀態的時候,JobSchedulerService直接通知JobServiceContext去取消沒有設置IDLE條件下運行的Job。
而並沒有事先去JobStore理移除該Job。


等到JobServiceContext成功停止了該Job後。
JobSchedulerService在執行Job結束的回調(onJobCompletedLocked)時,將會把JobStore裏保存的原有JobStatus移除,然後添加爲新創建的JobStaus。


這樣一來在MSG_CHECK_JOB_GREEDY的msg響應的時候,新的Job就可以得到執行。



到這裏,疑問二就解決了。


下次我們探討下之前遺留的兩個思考以及一個總結。


思考一



如果我們在onStartJob()裏處理耗時邏輯,導致onStartJob()沒有及時返回給JobSchedulerContext。
最終結果是怎麼樣?


是ANR?
還是因爲超時,該Job可能被強制停止和銷燬?


思考二



如果onStartJob()裏起了新線程處理耗時邏輯,但是返回值返回了false,那麼系統還會銷燬Job嗎?
如果會的話,新線程是否會導致內存泄漏?


總結

JobService和Service的區別

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