zoukankan      html  css  js  c++  java
  • 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 - 一个学渣 - 博客园

  • 相关阅读:
    用Live Writers发布Blog
    ScribeFire
    再再上传一个图片
    jQuery页面加载初始化常用的三种方法
    JS 20200101T00:00:00.000000Z 日期格式转换、Sun Jul 04 2021 00:00:00 GMT+0800 (中国标准时间)转20210704 00:00
    vue 组件传值 props $emit $event
    jquery或js如何判断一个层是显示还是隐藏
    js合并俩个数组的三种方法
    uniapp生命周期
    SVN版本冲突
  • 原文地址:https://www.cnblogs.com/Robin132929/p/13785767.html
Copyright © 2011-2022 走看看