Android 并发二三事之 Handler 机制的妙用 HandlerThread

Android 并发第五篇

本篇会讲解如何利用 HandlerThread 实现异步操作。

HandlerThread 本身其实就是一个 Thread ,但是其内部还利用 Handler 机制。

对于提交的任务(或者说是信息 Message)依次处理。

所以在介绍 HandlerThread 原理以及如果使用之前,会首先说一个 Handler 异步机制。

当然 Handler, Looper, Message 之间的关系相信很多人都已经很熟悉了,这里会只着重介绍和本节相关的内容。

一 、Handler 机制:

1、 我们都知道,在子线程中通知主线程更新UI界面,需要使用Handler。

一般我们就直接在 Activity 中直接 初始化一个Handler 对象,像这样:

        Handler uiHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };

重写 handlerMessage() 方法,然后利用 uiHandler 对象在子线程中发送消息。

2、 或者我们也可以直接在主线程直接 new 一个 Handler 对象:

Handler handler = new Handler();

但在子线程中 new Handler()需要这样:

Handler handler = new Handler(Context.getMainLooper());

然后在子线程中:

        handler.post(new Runnable() {
            @Override
            public void run() {
        //执行在主线程中
                Log.d(TAG, "run on UI  Thread Id : "+Thread.currentThread().getId());
            }
        });

Handler 源码:

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

在handler 中会将Runnable 对象赋值给 message.callback 属性,封装成Message,调用 sendMessageDelaye() 将消息发送出去。

sendMessageDelaye() 方法最后在辗转几次后最终会调用sendMessageAtTime() 将消息放到消息队列中。

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

而 Handler.sendMessage()的源码为:

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

所以本质上,无论是利用Handler.sendMessage(),还是 Handler.post() 都是将消息添加到消息队列之中。

那么为什么在子线程中需要传入 MainLooper , 而主线程却不需要呢?

首先我们是要在子线程中通知主线程,那么我们便需要代码执行在UI 线程中。

如果在子线程中直接:

Handler handler = new Handler();

会抛出异常:

Can’t create handler inside thread that has not called Looper.prepare()

我们可以看一下源码:

Handler 源码:

    public Handler() {
        this(null, false);
    }

    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can‘t create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

我们能够看到,无参的构造方法,会调用public Handler(Callback callback, boolean async) 。

在这个方法中,调用 Looper.myLooper(); 获取 Looper 对象,之所以抛出异常,一定是其为null了。

那么为什么没有获取到Looper对象呢?

接下来看Looper源码:

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

从 ThreadLocal 变量中获取当前线程的值,那么这个值是在哪里设置的呢?

    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

在Looper.prepare() 中设置的。

也就是说在子线程中直接 new Handler() 对象,需要先调用Looper.prepare() 方法。

而在主线程中是不需要的,因为在应用初始化时,已经调用 Looper.prepare() 了。

而Looper 中还有一个方法:Looper.loop() 方法

Looper.loop() 内包含一个死循环,不断的从队列中获取消息,如果没有消息时,会阻塞。

Looper.loop() 调用了 Handler.dispatchMessage() 方法:

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

    private static void handleCallback(Message message) {
        message.callback.run();
    }

在dispatchMessage() 方法中会调用 handleMessage() 方法,或者调用 handleCallback() 方法处理我们利用Handler post Runnable

所封装的消息。

3 、所以通过以上总结

我们知道如果Looper.loop()是在主线程中调用的,那么我们重写的 handlerMessage() 方法

和封装在消息中的 Runnable 都会在主线程中执行。

反过来说,如果Looper.prepare() 以及 Looper.loop() 是在子线程中调用的,那么基于子线程的Looper,所创建的Handler

所发送的消息都将会执行在子线程中,HandlerThread 便是利用了这个原理。

二 、HandlerThread

1 、我们首先看一下 HandlerThread 如何使用:

    private void requestWithHandlerThread() {
    //初始化一个 HandlerThread 对象
        HandlerThread handlerThread = new HandlerThread("HandlerThread");
    //调用start() 方法
        handlerThread.start();
    Log.d(TAG, "Main : "+Thread.currentThread().getId());
        Log.d(TAG, "HandlerThread : "+handlerThread.getId());
    //初始化一个Handler 对象,利用 HandlerThread 中的 Looper 对象
        Handler handler = new Handler(handlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //执行在子线程中
                Log.d(TAG,"Thread : "+Thread.currentThread().getId() +"   "+msg.obj);

            }
        };
        Message message = Message.obtain();
        message.obj = "From Message";
        handler.sendMessage(message);
        handler.post(new Runnable() {
            @Override
            public void run() {
        //执行在子线程中
                Log.d(TAG, "post : "+Thread.currentThread().getId());
            }
        });
    }

2 、结果:

11-15 17:20:15.634 12297-12297/com.loader.demo D/Demo: Main : 1
11-15 17:20:15.634 12297-12297/com.loader.demo D/Demo: HandlerThread : 26599
11-15 17:20:15.640 12297-12416/com.loader.demo D/Demo: Thread : 26599   From Message
11-15 17:20:15.640 12297-12416/com.loader.demo D/Demo: post : 26599

在这里 HandlerThread 需要和 Handler 一起配合使用,HandlerThread 提供一个在子线程中创建的 Looper 。

按照之前的推论,Looper.prepare(), 以及 Looper.loop() 都是执行在子线程中,那么在处理消息时也必然执行在子线程中。

所以其实现了异步的效果。

3 、接下来看一下 HandlerThread 的源码:

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

        // If the thread has been started, wait until the looper has been created.
        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;
    }

    public int getThreadId() {
        return mTid;
    }
}

4 、总结

可以看到 HandlerThread 本身是一个 Thread 所以其 run() 方法会执行在子线程中。在 run() 方法中首先调用了Looper.prepare(),

用于初始化消息队列以及Looper对象,紧接着调用 Looper.loop() 开始从消息队列中轮询,一旦有消息便将消息取出处理。

因为整个过程都执行在子线程中,所以当我们用在子线程中创建的Looper作为参数传给Handler时,其处理消息的代码就会执行在子线程中了。

以上便是 HandlerThread 的原理,主要还是利用 Handler,Message, Looper 之间的关系。

三 、自定义 HandlerThread:

当我们了解了其原理之后,其实我们也可以自定义自己 HandlerThread , 在线程之中处理消息。

现在我们自定义一个MyHandlerThread 同样继承 Thread。

1 、代码如下:

public class MyHandlerThread extends Thread {

    private Handler asyncHandler;
    private Looper mLooper;
    public MyHandlerThread() {

    }

    @Override
    public void run() {

        Looper.prepare();
        mLooper = Looper.myLooper();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        asyncHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                handlerThreadMessage(msg);
            }
        };
        Looper.loop();

    }

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    //退出
    public void quit() {
        Looper looper = getLooper();
        if(looper != null) {
            looper.quit();
        }
    }

    /**
     * 发送消息
     * @param message
     */
    public void sendMessage(Message message) {
        if(asyncHandler != null) {
            Log.d("test","sendMessage");
            asyncHandler.sendMessage(message);
        }
    }

    /**
     * 处理消息
     * @param message
     */
    private void handlerThreadMessage(Message message) {
        Log.d("test","Message : "+message.obj+" Thread " +Thread.currentThread().getId());
    }
}

2、用法也很简单:

定义变量:

private MyHandlerThread handlerThread;

在onCreate() 中初始化:

handlerThread = new MyHandlerThread();
handlerThread.start();

在需要异步时调用:

    private void updateData() {
        Message message = Message.obtain();
        message.obj = "更新数据";
        handlerThread.sendMessage(message);
    }

这样我们便实现自定义 HandlerThread ,其中我们还可以根据需求封装不同发送消息的方法。

并且我们还将提交任务的代码和在子线程中处理任务的代码分开了,两块代码利用 MessageQueue 相连接,

那么这是不是也算是一种生产者消费者模式呢? 因为Handler 机制本身也算是一种生产者消费者模式啊。

四、

下一篇会讲解 Android 中另外一个可以实现异步的类: IntentService 。

IntentService 本身当然是一个 Service , 但是它可以做到完成任务后自动退出,下一篇一起看看其是怎么做到的。

时间: 2024-08-26 05:12:40

Android 并发二三事之 Handler 机制的妙用 HandlerThread的相关文章

Android Handler机制

本博客是笔者在阅读<Android 内核剥析>后,有感而写.强烈建议读者去阅读这本书. 要说handler机制,handler并不是猪脚.真正的猪脚是Android异步消息处理现程. 异步消息处理线程: 一般线程在处执行完run()后就会结束.而异步消息处理线程则一个 while(true)循环.不断的从当前线程中取出消息,又不断的处理消息.如果没有消息,就阻塞住,等待队列有新的消息在继续处理. 使用异步消息处理线程的场景: 1.任务需要常驻,比如处理用户交互的事件.Android中典型的就是

Android中的Handler机制

直接在UI线程中开启子线程来更新TextView显示的内容,运行程序我们会发现,如下错 误:android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.翻译过来就是:只有创建这个控件的线程才能去更新该控件的内容. 所有的UI线程要去负责View的创建并且维护它,例如更新冒个TextView的显示,都必

android之handler机制深入解析

一.android中需要另开线程处理耗时.网络的任务,但是有必须要在UI线程中修改组件.这样做是为了: ①只能在UI线程中修改组件,避免了多线程造成组件显示混乱 ②不使用加锁策略是为了提高性能,因为android中经常使用多线程. handler就是为了解决在多个线程之间的通信问题. 二.基本使用: 1 package com.dqxst.first.multithread; 2 3 import android.app.Activity; 4 import android.os.Bundle;

Android Handler 异步消息处理机制的妙用 创建强大的图片加载类

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38476887 ,本文出自[张鸿洋的博客] 最近创建了一个群,方便大家交流,群号:55032675 上一篇博客介绍了Android异步消息处理机制,如果你还不了解,可以看:Android 异步消息处理机制 让你深入理解 Looper.Handler.Message三者关系 .那篇博客的最后,提出可以把异步消息处理机制不仅仅是在MainActivity中更新UI,可以用到别的地方,

带你深入理解Android Handler机制

带你深入理解Android Handler机制 欢迎转载请注明来源 说到消息机制,我们一定会想到Handler,由于Android系统规定主线程不能阻塞超过5s,否则会出现"Application Not Responding".也就是说,你不能在主线程中进行耗时操作(网络请求,数据库操作等),只能在子线程中进行.下面先来看一下在子线程中访问UI会出现什么情况. public void click(View v){ new Thread(new Runnable() { @Overri

【转载】Android 的 Handler 机制实现原理分析

handler在安卓开发中是必须掌握的技术,但是很多人都是停留在使用阶段.使用起来很简单,就两个步骤,在主线程重写handler的handleMessage( )方法,在工作线程发送消息.但是,有没有人想过这种技术是怎么实现的呢?下面我们一起探讨下. 先上图,让大家好理解下handler机制: handler机制示例图 上面一共出现了几种类,ActivityThread,Handler,MessageQueue,Looper,msg(Message),对这些类作简要介绍: ActivityThr

Android的Handler机制

Handler机制的原理 Android 的 Handler 机制(也有人叫消息机制)目的是为了跨线程通信,也就是多线程通信.之所以需 要跨线程通信是因为在 Android 中主线程通常只负责 UI 的创建和修改,子线程负责网络访问和耗时操作, 因此,主线程和子线程需要经常配合使用才能完成整个 Android 功能. Handler 机制可以近似用图 1 展示.MainThread 代表主线程,newThread 代表子线程. MainThread 是 Android 系统创建并维护的,创建的时

Android Handler机制 (一个Thead中可以建立多个Hander,通过msg.target保证MessageQueue中的每个msg交由发送message的handler进行处理 ,但是 每个线程中最多只有一个Looper,肯定也就一个MessageQuque)

转载自http://blog.csdn.net/stonecao/article/details/6417364 在android中提供了一种异步回调机制Handler,使用它,我们可以在完成一个很长时间的任务后做出相应的通知 handler基本使用: 在主线程中,使用handler很简单,new一个Handler对象实现其handleMessage方法,在handleMessage中 提供收到消息后相应的处理方法即可,这里不对handler使用进行详细说明,在看本博文前,读者应该先掌握hand

Android中使用Handler机制更新UI的三种解决方案

最近想把学习Android过程中的Handler使用经验写下来,供自己以后查看,也与大家一起分享. 使用Handler其实不得不与Android中的线程或者说Java中的多线程扯上关系.本篇文章只会用到最基本的线程使用,不会涉及太难的,关于Android的线程我们以后再讨论.在Android中每新建一个Activity,该Activity(理解为界面)就是一个线程,是一个主线程,也称之为UI线程.主线程可以更新界面元素,不会出现问题.每当新建一个线程new Thread,该线程就是一个子线程,A