这次从Handler一直说到IntentService

本文深入探讨了Android中Handler、Looper、MessageQueue等核心组件的工作原理,解析了ThreadLocal、HandlerThread和IntentService在异步任务处理中的应用,以及它们之间的联系与区别。

 

2018年终于终于快结束了!这一年发生了很多事,有很多的成长,无论是技术还是人生。2018是很难忘的一年,以此写篇博客,记录过去,期待2019年的美好。希望2019年能有好的开始,完美的结尾!加油加油!

 

前言

这篇博客来自于整理很早之前的一篇学习笔记,Handler很早之前已经写过一篇文章了,当时的理解可能不是很深,所以后来又深入学习了下,有了一个全新的认识。学习之前先了解一些基础知识点,本篇涉及到ThreadLocal,Handler,Looper,Message,MessageQueue,HandlerThread,IntentService等知识点。

 

知识点

ThreadLocal:

ThreadLocal是一个线程的数据存储类,ThreadLocal为每个使用该数据的线程提供独立的数据副本,所以每个线程都可以独立改变自己的数据副本,而不会影响其他线程对应的数据,其实也可以理解为在不同的线程间对同一个共享数据的隔离,两种理解角度我觉得都没问题,就看个人怎么理解。

那么ThreadLocal是怎么做到为每个线程提供独立的数据副本的呢?就是利用内部的ThreadLocalMap类,它是ThreadLocal的一个静态内部类,ThreadLocalMap类以当前ThreadLocal实例为key,以数据为value,内部使用一个实体数组实现,数组初始大小为16,随着数据的增多会调用reSize方法对数组扩容,扩容大小为当前数组大小的2倍

        static class ThreadLocalMap {
            //ThreadLocalMap的内部静态实体类,以ThreadLocal的弱引用为key,以数据为value
            static class Entry extends WeakReference<ThreadLocal<?>> {
                Object value;
    
                Entry(ThreadLocal<?> k, Object v) {
                    super(k);
                    value = v;
                }
            }
    
            //ThreadLocal内部以实体数组的方式,为每个线程存储所对应的value值
            private Entry[] table;
            //ThreadLocalMap的set方法
            private void set(ThreadLocal<?> key, Object value) {
                ...
                tab[i] = new Entry(key, value);
                //计算需不需要对实体数组扩容
                int sz = ++size;
                if (!cleanSomeSlots(i, sz) && sz >= threshold)
                    rehash();
                }
            ...
        }

ThreadLocal的set方法就是数据的存储方法,首先会获取当前线程的ThreadLocalMap实例,再调用ThreadLocalMap的set方法存储当前线程对应的值,篇幅原因ThreadLocal的get方法和set方法的实现,可以去百度或查看源码了解一下。

Message:

Message是实现了Parcelable接口的实体类,表示一个消息载体,其中的target变量表示当前发送和要处理此消息的Handler对象

MessageQueue:

MessageQueue消息队列主要有消息的插入读取操作,读取操作往往伴随着删除操作。MessageQueue的内部通过单链表实现,因为单链表的插入和删除有优势。

 

Handler

对Handler的理解我们从一段代码看起

        new Thread(new Runnable() {
            @Override public void run() {
                Looper.prepare();
                handler = new TestHandler();
                //do something
                handler.sendEmptyMessage(1);
                Looper.loop();

                //Looper.quit()退出循环后执行
                //do something
            }
        }).start();

上面的代码就是完整的使用Handler的示例,那么Looper.prepare()方法的作用是什么呢,其实就是创建一个Looper实例,并将当前looper实例设置给当前线程的ThreadLocal变量,从判断我们可知,一个线程只能有一个Looper,当然的,也只能有一个MessageQueue(消息队列),相关源码如下:

    //Looper的prepare方法
    public static void prepare() {
        prepare(true);
    }
    
    private static void prepare(boolean quitAllowed) {
        //一个线程只能有一个Looper对象
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
    //looper的构造方法
    private Looper(boolean quitAllowed) {
        //在Looper对象初始化时,创建消息队列,Looper在线程中是唯一的,那么消息队列也是唯一的
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

那为什么Handler必须需要Looper呢,Looper在消息处理过程中的作用又是什么呢,其实想想也是,Handler是消息(Message)的处理者,而Looper的作用就是循环的取出消息,交给Handler去处理。示例中Looper.loop()方法的作用就是开启消息循环,源码如下,我们看看,其实就是循环从消息队列中取出一个消息,并把该消息分发到对应的Handler回调中。

    public static void loop() {
        //获取当前线程的Looper对象
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        ...
        //开启消息循环
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // 没有消息时退出本次循环
                return;
            }
            ...
            try {
                //这里msg.target变量就是当前发出这条消息的 Handler,
                //这也是为什么有多个Handler时发消息时,只能接受到自己的消息的原因
                msg.target.dispatchMessage(msg);
            } finally {
            }
            ...
            msg.recycleUnchecked();
        }
    }

明白了消息的循环和消息的分发,我们再看看消息的发送过程,消息的发送就是把消息放置消息队列中,以handler.sendEmptyMessage(1)方法为例,最终会调用MessageQueue的enqueueMessage方法。MessageQueue会根据post delay的时间排序放入到链表中,链表头的时间小,尾部时间最大。因此能保证时间Delay最长的不会block住时间短的。当每次post message的时候会进入到MessageQueue的next()方法,会根据其delay时间和链表头的比较,如果更短则,放入链表头,并且看时间是否有delay,如果有,则block,等待时间到来唤醒执行,否则将唤醒立即执行。

tips:

MessageQueue.IdleHandler可以用来在线程空闲的时候,进行一个操作

        Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
            @Override public boolean queueIdle() {
                /**
                 * 返回值boolean 意思是needKeep
                 * true,表示要保留保留, 代表不移除这个idleHandler,可以反复执行
                 * false代表执行完毕之后就移除这个idleHandler, 也就是只执行一次
                 */
                //do something
                return false;
            }
        });

总结:

学习完Handler的原理,再结合上面的示例,可能有人就有想法了:既然在Looper.loop()方法可以开启一个消息循环,去处理Handler发送的各种消息,那么我们可以把示例作为一个不间断的异步任务呀,有任务处理的时候,就发送一条消息,然后处理相应的任务,处理完调用Looper.quit()退出,哈哈,对的,刚才说的这种异步任务就是接下来要说的HandlerThread。

 

HandlerThread

HandlerThread继承自Thread,可以用来执行多个耗时操作,而不需要多次开启线程,也就是刚才我们总结中说的那样。HandlerThread的用法如下:

        //HandlerThread的用法
        HandlerThread handlerThread = new HandlerThread("Test_HandlerThread");
        handlerThread.start();
        Handler handler = new Handler(handlerThread.getLooper()) {
            @Override public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //在此处理耗时任务
                post(new Runnable() {
                    @Override public void run() {
                        //ui do something
                    }
                });
            }
        };
        handler.sendEmptyMessage(1);
        //结束任务
        handlerThread.quit();

我们看看HandlerThread的run方法的实现,Android为我们实现了Handler在子线程中的创建。哈哈,是不是和前面Handler中的示例一样。而HandlerThread的退出,其实就是调用Looper.quit()结束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处理多个任务,那执行过程会是怎样的呢?答案就是任务会放在任务队列中,一个一个的执行。其实细细一想我们就会明白,这里所谓的任务队列就是MessageQueue,就是消息队列,HandlerThread的耗时任务会在Handler的callback中执行,而Handler的callback又运行在Looper的loop方法,只有处理完一个消息(Handler的callback方法执行结束),这里可以说执行完一个任务,才会进行下一次循环,才会取出下一个消息,再次处理。所以我们也就能理解HandlerThread和IntentService处理多个任务时的执行过程。

总结:

看完Handler的示例和HandlerThread的原理,可能有人又有想法了,如果我们在Service中使用Handler的示例和HandlerThread,那不就可以让Service一直循环的后台执行任务了嘛,执行完调用对应方法,岂不美滋滋,是的,这种是接下来要说的IntentService。

 

IntentService

IntentService继承自Service,内部的实现很简单,使用HandlerThread实现,我们直接读源码并分析其实现

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;
    
    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);
        }
    }

    public IntentService(String name) {
        super();
        mName = name;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //我们在这里可以看到标准的HandlerThread的使用
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @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;
    }

    //任务结束调用stopSelf方法后,结束HandlerThread
    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        //IntentService默认IBinder返回null
        return null;
    }

    //耗时任务的处理方法
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

 

总结

至此我们就明白了异步消息处理在Android中的应用是有很多场景,其中消息处理这块是重中之重。在阅读Looper的源码的时候,发现一个方法:prepareMainLooper(),字面意思可知,是初始化主线程的Looper,很好奇看了看该方法在android中的调用地方,发现在android中有两个调用,分别是ActivityThread类的main方法和SystemServer类的main方法中,ActivityThread就是我们说的主线程,而SystemServer就是Android的系统进程,相关的详细介绍会放到ActivityThread的理解和APP的启动过程这篇博客中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值