Android----Handler消息机制

2018-10-13  本文已影响8人  海盗的帽子

csdn
个人博客

Android----Handler消息机制(进阶篇)

一.简单介绍

Android 中的消息机制主要是指 Handler 的运行机制和在 MessageQueue 和 Looper 的配合下 将一个线程中的任务切换到 Hanlder 所在的线程去执行。在 Android 中常常用来在子线程切换到 UI 线程从而在 UI 线程更新 UI 。

Android 的 UI 控件不是线程安全的,如果多个线程并发访问控件会导致控件的状态不可预期,这和多线程访问同一的变量是一个道理,但是控件却不能像简单的变量一样加上锁机制就可以实现线程同步,因为这会降低 UI 的访问效率。所以采用单线程的模式通过 Handler 切换可以实现 UI 的更新。

二.涉及的几个类

1.MessageQueue 和 Message

MessageQueue 是一个消息队列,内部是以一个单链表的数据结构维护数据。Message 就是消息队列中存储的对象。

2.Looper

在消息机制中 Looper 会不断地从 MessageQueue 中查看是否有新的 Message 需要处理,没有就阻塞等待,这是一个无限循环的模式。MessageQueue 会在 Looper 的构造器中初始化,也就是说一个 Looper 对应一个 MessageQueue .

3.Handler

消息的发送者和处理者,内部有一个 Looper 和一个 MessageQueue 。通过 dispatchMessage 方法对消息进行分派,决定最后的回调方式。有一个抽象方法 handleMessage 用与处理消息。在 dispatchMessage 方法中,如果消息是 handler.post 方式发送则会调用 post 里的 Runnable 接口,如果是 sendMessage 方式发送就会调用 handleMessage 方法。

4.ThreadLocal

这是 Thread Local Variable 线程局部变量的意思,支持泛型,这个类的功能就是为每个线程提供一个变量( 用泛型指定 类型)的副本,使得每个线程都可以独立地改变自己的副本而不会和其他线程产生冲突。可以隔离多个线程对共享资源的冲突。在 Handler 中 ThreadLocal 存放着 Looper ,因此只要在 UI 线程中都可以实例化 Handler 就可以得到主线程的 Looper。

三.工作过程

在实际开发中,Handler 的使用通常在主线程中,然后在子线程中发送消息,最后在运行在主线程的 handleMessage 处理消息,或者以 Handler.post 方法中处理消息。

1.主线程的消息循环

主线程的消息循环的开始是在 ActivityThread 的 main 方法中

 public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();
        ...
        ActivityThread thread = new ActivityThread();
        ...
        Looper.loop();

    }

这里先看第一个方法 prepareMainLooper ,这是独有的。

 public static void prepareMainLooper() {
        prepare(false);
        ...
  private static void prepare(boolean quitAllowed) {
          if (sThreadLocal.get() != null) {
          //一个线程只能有一个 Looper
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //创建主线程的 Looper
        sThreadLocal.set(new Looper(quitAllowed));
    }
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

在 ActivityThread 的 main 方法中会创建主线程的 Looper ,并且在实例化 Looper 时候就创建消息队列 MessageQueue ,并将当前线程和 Looper 关联起来。
接着创建完 UI 线程后就会执行 loop 方法。

    public static void loop() {
        final Looper me = myLooper();
        ...
        //无限循环
        for (;;) {
            Message msg = queue.next(); // might block
            ...
            try {
                ...
                //这里的 target就是 Handler 
                msg.target.dispatchMessage(msg);
                ...
            } 
            // 释放资源
            msg.recycleUnchecked();
        }
    }

在 loop 方法中会无限循环对消息队列进行查看,如果有消息就取出消息,并找到消息对应的执行者进行分派,最后释放资源。

2.Handler 的创建

 public Handler(Callback callback, boolean async) {
        ...
        mLooper = Looper.myLooper();
        ...
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

通常如果使用 Handler 更新 UI 控件的话,那么 Hanlder 的实例化就要在主线程中,因为这样才能在构造方法中获取主线程的 looper 和对应的 MessageQueue 。

3.sendMessage 和 post

(1).sendMessage( Message )

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        ...
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //在这里 msg 的 target 就被赋值,这里指向就是 handler 
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

从上面的方法一步步执行下来,在 enqueueMessage 方法中将 msg 的 target 指向 调用的 handler ,最后就会将消息添加到消息队列中。

(2)post( Runnable )

  public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
  private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        //将 Runnable 赋给 Message 的 callback 
        m.callback = r;
        return m;
    }

post 方式中参数是一个 Runnable 对象,在 Hanlder 会被封装为一个 Message ,最后同样调用 sendMessageDelayed 方法,后续的操作就是一样的。

4.处理消息

通过 3 中两种方式都可以发送一个消息,往消息队列中添加 Message ,而接着 looper 循环就会取出这个消息,对其进行分配也就是在之前讲过的 msg.target.dispatchMessage 方法。这里的 msg.target 在 3 中可以看到其实就是 Handler ,接着就看 dispatchMessage .

 public void dispatchMessage(Message msg) {
        //这对应 post 方式
        if (msg.callback != null) {
            handleCallback(msg);
        //这对应 sendMessage 方式
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

可以看到最后就会根据之前的发送消息的方式从而选择不同的方法,如果是 post 方式就会执行 post 里面的 run 方法,如果是 sendMessage 方式就会执行 handleMessage .大致流程如图:


Handler机制.png

四.HandlerThread 和 IntentService

在上面的讲述过程中,都是以主线程的存在情况,因为主线程在程序运行开始阶段系统就会创建被初始化 looper 和 MessageQueue ,不用自己手动创建。这是 Handler 消息机制的在主线程的运用情况,下面就介绍如何在子线程运用 Handler 。

1.HandlerThread

HandlerThread 继承了 Thread ,内部有自己的 Looper 。这个类是对在子线程使用 Handler 和 Looper 的一个简单的封装,从而实现子线程有自己的消息循环。

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;
    ....
    @Override
    public void run() {
        mTid = Process.myTid();
        //创建自己的 Looper
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        //消息循环
        Looper.loop();
        mTid = -1;
    }
}
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() 方法,后续的步骤也和之前在主线程创建一样,只不过这里的 Looper 是属于 HanlderThread 这个线程的。

2.IntentService

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) {
            //因为这里的 handleMessage 是在 HanldeThread 中的
            //所以 onHandleIntent 也就运行在子线程中。
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

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

    @Override
    public void onCreate() {
      
        super.onCreate();
        //创建 HanlderThread 对象
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        
        //获取线程中的 Looper 
        mServiceLooper = thread.getLooper();
        //绑定自己的 Handler ,注意这里的 Looper 是子线程的,
        //所以 Handler 中的方法会运行在子线程中
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        //将 Intent 包装在 Message 中发送
        mServiceHandler.sendMessage(msg);
    }

上面方法是 IntentService 的源码,IntentService 有一个特殊的方法就是 onHandleIntent ,这个方法是一个运行在子线程中方法,所以可以进行一些耗时的操作。IntentService 实现的原理实际上就是运用了 HandlerThread 。在 onCreate 方法中会启动这个线程,并将 ServiceHandler 和 HandlerThread 线程的 Looper 进行绑定,因此 Hanlder 发送的消息就会到这个子线程中去处理。在 handleMessage 调用 onHandleIntent ,从而实现在子线程获取 Intent,并处理操作。

上一篇下一篇

猜你喜欢

热点阅读