美文网首页
Android 多线程编程:IntentService & Ha

Android 多线程编程:IntentService & Ha

作者: 大队辅导猿 | 来源:发表于2019-06-12 11:02 被阅读0次

    因为 Android 是使用 Java 开发的,所以当我们谈及 Android 中的多线程,必然绕不过 Java 中的多线程编程。但在这篇文章中,我们不会过多地分析 Java 中的多线程编程的知识。我们会在以后分析 Java 并发编程的时候分析 Java 中的多线程、线程池和并发 API 的用法。

    我们先来总结一下 Android 多线程编程的演变过程:首先是 Java 的 Thread。因为本身在创建一个线程和销毁一个线程的时候会有一定的开销,当我们任务的执行时间相比于这个开销很小的时候,单独创建一个线程就显得不划算。所以,当程序中存在大量的、小的任务的时候,建议使用线程池来进行管理。但我们一般也很少主动去创建线程池,这是因为——也许是考虑到开发者自己去维护一个线程池比较复杂—— Android 中已经为我们设计了 AsyncTask。AsyncTask 内部封装了一个线程池,我们可以使用它来执行耗时比较短的任务。但 AsyncTask 也有一些缺点:1).如果你的程序中存在很多的不同的任务的时候,你可能要为每个任务定义一个 AsyncTask 的子类。2).从异步线程切换到主线程的方式不如 RxJava 简洁。所以,在实际开发的过程中,我通常使用 RxJava 来实现异步的编程。尤其是局部的优化、不值得专门定义一个 AsyncTask 类的时候,RxJava 用起来更加舒服。

    上面的多线程创建的只是普通的线程,对系统来说,优先级比较低。在 Android 中还提供了 IntentService 来执行优先级相对较高的任务。启动一个 IntentService 任务的时候会将任务添加到其内部的、异步的消息队列中执行。此外,IntentService 又继承自 Service,所以这让它具有异步和较高的优先级两个优势。

    在之前的文章中,我们已经分析过 AsyncTask、RxJava 以及用来实现线程切换的 Handler. 这里奉上这些文章的链接:

    你可以通过以上的文章来了解这部分的内容。在本篇文章中,我们主要来梳理下另外两个多线程相关的 API,HandlerThread 和 IntentService。

    1、异步消息队列:HandlerThread

    如果你之前还没有了解过 Handler 的实现的话,那么最好通过我们上面的那篇文章 《Android 消息机制:Handler、MessageQueue 和 Looper》 了解一下。因为 HandlerThread 就是通过封装一个 Looper 来实现的。

    1.1 HandlerThread 的使用

    HandlerThread 继承自线程类 Thread,内部又维护了一个 Looper,Looper 内又维护了一个消息队列。所以,我们可以使用 HandlerThread 来创建一个异步的线程,然后不断向该线程发送任务。这些任务会被封装成消息放进 HandlerThread 的消息队列中被执行。所以,我们可以用 HandlerThread 来创建异步的消息队列。

    在使用 HandlerThread 的时候有两个需要注意的地方:

    1. 因为 HandlerThread 内部的 Looper 的初始化和开启循环的过程都在 run() 方法中执行,所以,在使用 HandlerThread 之前,你必须调用它的 start() 方法。
    2. 因为 HandlerThread 的 run() 方法使用 Looper 开启一个了无限循环,所以,当不再使用它的时候,应该调用它的 quitSafely()quit() 方法来结束该循环。

    在使用 HandlerThread 的时候只需要创建一个它的实例,然后使用它的 Looper 来创建 Handler 实例,并通过该 Handler 发送消息来将任务添加到队列中。下面是一个使用示例:

    myHandlerThread = new HandlerThread("MyHandlerThread");
    myHandlerThread.start();
    handler = new Handler( myHandlerThread.getLooper() ){
        @Override
        public void handleMessage(Message msg) {
            // ... do something
        }
    };
    handler.sendEmptyMessage(1);
    

    这里我们创建了 HandlerThread 实例之后用它来创建 Handler 然后通过 Handler 把任务加入到消息队列中进行执行。

    显然,使用 HandlerThread 可以很轻松地实现一个消息队列。你只需要在创建了 Handler 之后向它发送消息,然后所有的任务将被加入到队列中执行。当然,它也有缺点。因为所有的任务将会被按顺序执行,所以一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。

    1.2 HandlerThread 源码解析

    下面是该 API 的源码,实现也比较简单,我们直接通过注释来对主要部分进行说明:

    public class HandlerThread extends Thread {
        int mPriority;
        int mTid = -1;
        Looper mLooper;
        private @Nullable Handler mHandler;
    
        public HandlerThread(String name) {
            super(name);
            mPriority = Process.THREAD_PRIORITY_DEFAULT;
        }
        
        protected void onLooperPrepared() { }
    
        // 在这个方法开启了 Looper 循环,因为是一个无限循环,所以不适用的时候应该将其停止
        @Override
        public void run() {
            mTid = Process.myTid();
            Looper.prepare();
            synchronized (this) {
                mLooper = Looper.myLooper();
                notifyAll();
            }
            Process.setThreadPriority(mPriority);
            onLooperPrepared();
            Looper.loop();
            mTid = -1;
        }
        
        // 获取该 HandlerThread 对应的 Looper
        public Looper getLooper() {
            if (!isAlive()) {
                return null;
            }
            synchronized (this) {
                while (isAlive() && mLooper == null) {
                    try {
                        wait();
                    } catch (InterruptedException e) { }
                }
            }
            return mLooper;
        }
    
        public boolean quit() {
            Looper looper = getLooper();
            if (looper != null) {
                looper.quit();
                return true;
            }
            return false;
        }
    
        public boolean quitSafely() {
            Looper looper = getLooper();
            if (looper != null) {
                looper.quitSafely();
                return true;
            }
            return false;
        }
    
        // ... 无关代码
    }
    

    上面的代码比较简单明了,在 run() 方法中初始化 Looper 并执行。如果 Looper 还没有被创建,那么当调用 getLooper() 方法获取 Looper 的时候会让线程阻塞。当 Looper 创建完毕之后会唤醒所有阻塞的线程继续执行。另外,就是两个停止 Looper 的方法。它们基本上就是对 Looper 进行了一层封装。

    2、IntentService

    2.1 使用 IntentService

    IntentService 继承自 Serivce,因此它比普通的多线程任务优先级要高。这使得它相比于普通的异步任务不容易被系统 kill 掉。它内部也是通过一个 Looper 来实现的,所以也是一种消息队列。在研究它的源码之前,我们先来看一下它的使用。

    IntentService 的使用是比较简单的,只需要:1).继承它并实现其中的 onHandleIntent() 方法;2). 将 IntentService 注册到 manifest 中;3). 像开启一个普通的服务那样开启一个 IntentService 即可。下面是该类的一个使用示例:

    public class FileRecognizeTask extends IntentService {
    
        public static void start(Context context) {
            Intent intent = new Intent(context, FileRecognizeTask.class);
            context.startService(intent);
        }
        
        public FileRecognizeTask() {
            super("FileRecognizeTask");
        }
    
        @Override
        protected void onHandleIntent(@androidx.annotation.Nullable @Nullable Intent intent) {
            // 你的需要异步执行的业务逻辑
        }
    }
    

    OK,介绍完了 IntentService 的使用,我们再来分析一下它的源码。

    2.2 IntentService 源码分析

    实现 IntentService 的时候使用到了我们上面分析过的 HandlerThread. 首先,在 onCreate() 回调方法中创建了一个 HandlerThread,然后使用它的 Looper 创建了一个 ServiceHandler:

    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();
    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
    

    ServiceHandler 是 IntentSerice 的内部类,其定义如下:

    private final class ServiceHandler extends Handler {
    
        public ServiceHandler(Looper looper) {
            super(looper);
        }
    
        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }
    

    ServiceHandler 用来执行被添加到队列中的消息。它会回调 IntentService 中的 onHandleIntent() 方法,也就是我们实现业务逻辑的方法。当消息执行完毕之后,会调用 Service 的 stopSelf(int) 方法来尝试停止服务。注意这里调用的是 stopSelf(int) 而不是 stopSelf()。它们之间的区别是,当还存在没有完成的任务的时候 stopSelf(int) 不会立即停止服务,而 stopSelf() 方法会立即停止服务。

    IntentSerivce 的 onCreate() 方法会在第一次启动的时候被调用,来创建服务。而 onStartCommond() 方法会在每次启动的时候被调用。下面是该方法的定义。

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }
    
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
    

    onStartCommand() 内部调用了 onStart() 来处理请求。在 onStart() 方法中会通过 mServiceHandler 创建一个消息,并将该消息发送给 mServiceHandler. 该消息会在被 mServiceHandler 放进消息队列中排队,并在合适的时机被执行。

    因此,我们可以总结一下 IntentService 的工作过程:首先,当我们第一次启动 IntentService 的时候会初始化一个 Looper 和 Handler;然后调用它的 onStartCommond() 方法,把请求包装成消息之后发送到消息队列中等待执行;当消息被 Handler 处理的时候会回调 IntentService 的 onHandleIntent() 方法来执行。此时,如果又有一个任务需要执行,那么 IntentService 的 onStartCommond() 方法会再次被执行并把请求封装之后放入队列中。当队列中的所有的消息都执行完毕,并且没有新加入的请求,那么此时服务就会自动停止,否则服务还会继续在后台执行。

    这里,同样也应该注意下,IntentService 中的任务是按照被添加的顺序来执行的。

    总结

    以上就是我们对 IntentService 和 HandlerThread 的分析。它们都是使用了 Handler 来实现,所以搞懂它们的前提是搞懂 Handler。关于 Handler,还是推荐一下笔者的另一篇文章 《Android 消息机制:Handler、MessageQueue 和 Looper》


    相关文章

      网友评论

          本文标题:Android 多线程编程:IntentService & Ha

          本文链接:https://www.haomeiwen.com/subject/ujukfctx.html