文章目录

  • 引言
  • Java层
  • 永动机跑起来
  • 示例
  • Looper
  • Handler
  • MessageQueue
  • 永动机停下
  • Native层
  • nativeInit()
  • nativePollOnce()
  • nativeWake()
  • native层总结
  • 创建Hanler线程



Android HandlerThread实现多线程 handler多线程通信机制_Java

引言

Binder/Socket用于进程间通信,而Handler消息机制用于同进程的线程间通信,Handler消息机制是由一组MessageQueue、Message、Looper、Handler共同组成的,为了方便且称之为Handler消息机制。

Android HandlerThread实现多线程 handler多线程通信机制_Java_02

Handler实例对象mHandler位于线程间共享的内存堆上,工作线程与主线程都能直接使用该对象,只需要注意多线程的同步问题。工作线程通过mHandler向其成员变量MessageQueue中添加新Message,主线程一直处于loop()方法内,当收到新的Message时按照一定规则分发给相应的handleMessage()方法来处理。所以说,Handler消息机制用于同进程的线程间通信,其核心是线程间共享内存空间,而不同进程拥有不同的地址空间,也就不能用handler来实现进程间通信。

Java层

永动机跑起来

Android HandlerThread实现多线程 handler多线程通信机制_Android_03

  • Message:消息分为硬件产生的消息(如按钮、触摸)和软件生成的消息;
  • MessageQueue:消息队列(按时间的优先级队列)的主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);
  • Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);
  • Looper:不断循环执行(Looper.loop),按分发机制将消息分发给目标处理者。

    enqueueMessage()中msg.target = this; 导致msg持有handler,而handler持有activity(内部类持有外部类的对象),而msg又在外部类的生命周期内。导致内存泄漏

示例

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();   //【见 2.1】

        mHandler = new Handler() {  //【见 3.1】
            public void handleMessage(Message msg) {
                //TODO 定义消息处理逻辑. 【见 3.2】
            }
        };

        Looper.loop();  //【见 2.2】
    }
}

Looper

Looper.prepare()

Android HandlerThread实现多线程 handler多线程通信机制_Java_04

保证looper线程唯一:ThreadLocal

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
   final MessageQueue mQueue; //优先级消息队列

Handler

(1)消息发送

Android HandlerThread实现多线程 handler多线程通信机制_消息队列_05

所有的发消息方式,最终都是调用MessageQueue.enqueueMessage();(2)消息分发机制

在Looper.loop()中,当发现有消息时,调用消息的目标handler,执行dispatchMessage()方法来分发消息。

Android HandlerThread实现多线程 handler多线程通信机制_消息队列_06

消息分发优先级:

Android HandlerThread实现多线程 handler多线程通信机制_消息队列_07

MessageQueue

MessageQueue是消息机制的Java层和C++层的连接纽带,大部分核心方法都交给native层来处理

  • next()方法中,nativePollOnce是阻塞操作,其中nextPollTimeoutMillis代表下一个消息到来前,还需要等待的时长;当nextPollTimeoutMillis = -1时,表示消息队列中无消息,会一直等待下去。

永动机停下

loop死循环的退出

Message msg = me.mQueue.next(); // might block
if (msg == null) {
            // No message indicates that the message queue is quitting.
            return false;
        }
// Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

MessageQueue的quit()方法将mQuitting=true

Message msg = me.mQueue.next(); // might block 有两种情况
时间没到:计算时间后等待自动唤醒 nativePollOnce(ptr, nextPollTimeoutMillis);
消息队列为空:加入消息时enqueueMessage()->唤醒 nativeWake(mPtr);

要是子线程消息队列没有消息,则手动调用quite():唤醒线程 next()返回null-》退出loop

Native层

在整个消息机制中,而MessageQueue是连接Java层和Native层的纽带,换言之,Java层可以向MessageQueue消息队列中添加消息,Native层也可以向MessageQueue消息队列中添加消息

Android HandlerThread实现多线程 handler多线程通信机制_Java_08

MessageQueue中的native方法如下

Android HandlerThread实现多线程 handler多线程通信机制_Java_09

nativeInit()

初始化

Android HandlerThread实现多线程 handler多线程通信机制_Android_10

nativePollOnce()

提取消息队列中的消息

Android HandlerThread实现多线程 handler多线程通信机制_消息队列_11


调用Looper::pollOnce()来完成,空闲时停留在epoll_wait()方法,用于等待事件发生或者超时

nativeWake()

nativeWake用于唤醒功能,在添加消息到消息队列enqueueMessage(), 或者把消息从消息队列中全部移除quit(),再有需要时都会调用 nativeWake方法

Android HandlerThread实现多线程 handler多线程通信机制_消息队列_12

native层总结

MessageQueue通过mPtr变量保存NativeMessageQueue对象,从而使得MessageQueue成为Java层和Native层的枢纽,既能处理上层消息,也能处理native层消息

Android HandlerThread实现多线程 handler多线程通信机制_Android_13


Android HandlerThread实现多线程 handler多线程通信机制_消息队列_14

创建Hanler线程

1.利用HandlerThread创建
既然涉及多个线程的通信,会有同步的问题,Android为了简化Handler的创建过程,提供了HandlerThread类, 很多时候,在HandlerThread线程中运行Loop()方法,在其他线程中通过Handler发送消息到HandlerThread线程。通过wait/notifyAll的方式,有效地解决了多线程的同步问题。

// Step 1: 创建并启动HandlerThread线程,内部包含Looper
HandlerThread handlerThread = new HandlerThread("gityuan.com");
handlerThread.start();

// Step 2: 创建Handler
Handler handler = new Handler(handlerThread.getLooper());

// Step 3: 发送消息
handler.post(new Runnable() {

        @Override
        public void run() {
            System.out.println("thread id="+Thread.currentThread().getId());
        }
    });

2.直接创建线程

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();
        // Step 1: 创建Handler
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                //处理即将发送过来的消息
                System.out.println("thread id="+Thread.currentThread().getId());
            }
        };

        Looper.loop();
    }
}

// Step 2: 创建并启动LooperThread线程,内部包含Looper
LooperThread looperThread = new LooperThread("gityuan.com");
looperThread.start();

// Step 3: 发送消息
LooperThread.mHandler.sendEmptyMessage(10);