对Handler的理解学习

一、Handler的作用

  • 官方解释
    Handler允许你发送和处理Message和与线程的MessageQueue关联的Runnable对象。每个Handler实例与一个线程和它的MessageQueue相关联。当你创建一个新的Handler,它就被绑定到这个线程或者这个线程的消息队列上–从那时起,它会传递messages和runnables到那个消息队列,然后当他们离开消息队列时执行他们。
  • 个人理解
    Handler是一个线程消息处理者,是UI线程和子线程(工作线程)之间的信使。他可以将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现在工作线程对UI的异步更新。
    ps:其实Handler可以完成任何线程之间的消息传递,不只是主线程与子线程。

二、为啥用Handler

  • 在多个线程同时更新UI的时候,保证线程的安全。
  • 假如最初的设计没有Handler,而是所有地方都能更新UI,那么我有很多个线程同时在给一个文本控件setText,那肯定会引起界面混乱。如果这时候要解决怎么办?加锁?这样又会影响性能。
  • 所以Android在设计的时候就加入了这个Handler的机制让我们去遵守而不用去考虑多线程的问题。

三、Handler的机制(原理)

一句话解释:

Handler通过执行其绑定线程的消息队列(MessageQueue)中不断被Looper循环取出的消息(Message)来完成线程间的通信。

主要有几个类需要理解:
1. Looper

  • 官方解释
    用于运行线程的消息循环的类。默认情况下线程没有与之关联的消息循环;在要运行循环的线程中调用prepare来创建一个Looper,然后调用loop使其循环处理消息,直到循环结束为止。
  • 主要方法
    首先看prepare方法:
	/**
     * 将当前线程初始化为Looper,这使您有机会创建Handlers,然后在实际开始循环之前先引用这个Looper。
     * 确保在调用此方法后调用{@link #loop()},并通过调用{@link #quit()}结束该方法。
     */
    public static void prepare() {
        prepare(true);
    }

    /**
     *  这里sThreadLocal是一个ThreadLocal的对象,可以在一个线程中存储变量。
     *  这里可以看到储存的是一个Looper的实例,前三行判断如果sThreadLocal.get()为空,则抛出异常。
     *  说明此方法不能被调用两次,也保证了一个线程中只有一个Loopper实例。
     */
    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的构造方法:

	/**
     * 实例化了一个消息队列(MessageQueue)对象和将当前线程赋值给一个全局变量mThread 
     */
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

再看loop方法,只保留了重点代码:

 	/**
     *在此线程中运行消息队列。 确保调用{@link #quit()}以结束循环
     */
    public static void loop() {
    // 调用sThreadLocal.get()方法获取之前储存的Looper实例
        final Looper me = myLooper();
        if (me == null) {
        // 如果me为null则抛出异常,也就是说loop方法必须在prepare方法之后运行。
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        // 拿到该looper实例中的mQueue(消息队列)
        final MessageQueue queue = me.mQueue;
        // 开启无限循环
        for (;;) {
        	// 取出一条消息,如果没有消息则阻塞
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
        
            try {
            	// 使用调用 msg.target.dispatchMessage(msg);把消息交给msg的target的dispatchMessage方法去处理。
            	//Msg的target就是handler对象
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            //释放消息占据的资源
            msg.recycleUnchecked();
        }

接下来就走到了Handler的dispatchMessage方法了
2. Handler

  • 先看构造方法,通过as的structure可以看到Handler有7个构造方法,:Handler的构造方法
    这里我们平时使用最多的可能是两种,一个是无参的,一种是传入Looper对象的。但是不管哪种,最终都是走到了最后两个构造方法。来看倒数第二个方法:
public Handler(Callback callback, boolean async) {
        // 这个FIND_POTENTIAL_LEAKS变量恒为false,看不懂
        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());
            }
        }
        // 获取当前线程保存的Looper实例
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        // 获取当前线程保存的Looper实例中实例化时保存的MessageQueue(消息队列)
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

这个构造方法保证了handler的实例与我们Looper实例中MessageQueue关联上了。
最后一种更粗暴,直接把需要的变量都传进去了,全部赋值:

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

总之,不管以哪种方式实例化Handler,目的都是要将其与MessageQueue关联上。

  • sendMessage,这个方法平时是用的最多的了
	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);
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
	}
	
	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);
    }

	private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

经过一连串的调用以后到了sendMessageAtTime方法,在这里获取到MessageQueue然后调用enqueueMessage方法将其传入。
在enqueueMessage方法中把Message.target 赋值为this(在上面Looper的loop方法里面就是利用了这个target.dispatchMessage(msg)来传递的消息),也就是将当前的Handler实例传入Message赋值给其target属性;最后调用queue.enqueueMessage方法把消息保存到消息队列中。
说到这里,倒是可以看看这个dispatchMessage方法了:

	/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
        // 这里mCallback是否为空取决于调用哪个构造函数,但是目的都是为了让我们执行handleMessage方法,不管是Handler的还是Callback
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

最后来看handleMessage方法:

public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        public boolean handleMessage(Message msg);
    }
	/**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

可以看到不管是不管是Handler的还是Callback的handleMessage方法,都是需要我们自己去重写然后根据回调的msg进行消息处理。

  • 总结Handler机制
    • 1.Looper.prepare()在当前线程使用ThreadLocal保存一个通过构造方法生成的Looper实例,并保存一个MessageQueue实例。该方法在同一个线程只能调用一次,所以一个线程只会存在一个Looper和一个MessageQueue。
    • 2.Looper.loop()开启无限循环,每次从MessageQueue实例取出一条消息,再通过该消息的 msg.target.dispatchMessage(msg) 方法将消息进行传递。
    • 3.通过Handler的构造方法获取当前线程中的Looper实例及获取当该Looper对象中实例化时保存的MessageQueue(消息队列)。
    • 4.通过handler.sendMessage(Message msg)给msg的target赋值为handler自身,然后加入MessageQueue中。
    • 5.msg.target.dispatchMessage(msg) 调用到最后实际是回调了Handler的handleMessage方法,由我们自行重写处理消息。

四、Handler的用法

常用有两种

  • 直接new Handler(),通过handler.sendMessage()发消息,然后重写handleMessage()方法等待回调处理消息。
Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            logic();
        }
    };
  • 传入Looper,此处为封装主线程,传入Looper.getMainLooper(),保证了不管在什么线程中实例化,都是在主线程回调。然后调用handler.post(Runnable r)方法或postDelayed。

ps:在主线程中创建Handler时,主线程会自动创建Looper的,而自己在子线程中自定义handler时,需要手动创建Looper,并调用Looper的prepare方法,或者传Looper.getMainLooper()

public class MainThread {

    private Handler mHandler;

    public MainThread() {
        mHandler = new Handler(Looper.getMainLooper());
    }

    public void post(Runnable runnable) {
        postDelayed(runnable, 0);
    }

    public void postDelayed(Runnable runnable, long delayMillis) {
        mHandler.postDelayed(runnable, delayMillis);
    }

    public void removeCallbacks(Runnable runnable) {
        mHandler.removeCallbacks(runnable);
    }
}
  • 子线程创建Handler,如果不给Handler指定一个Looper就会出现异常
class MyThread extends Thread{
    public  Handler handler;
    @Override
    public void run() {
          Looper.prepare(); //创建一个Looper对象
          handler = new Handler(){
               //此方法运行在子线程中
               @Override
               public void handleMessage(Message msg) {
                   System.out.println("当前的线程-->" + Thread.currentThread());
               }
          };
      Looper.loop(); //开始轮询
    }
}

MyThread thread = new MyThread();
thread.start(); //启动线程
//发送消息就会执行该handler的handleMessage方法(在子线程中)
thread.handler.sendEmptyMessage(1);

五、用到Handler的地方

  • HandlerThread
    本身是一个子线程,已经在run()方法中指定了Looper,可以直接进行异步操作。

HandlerThread

  • Activity的runOnUiThread(Runnable action)方法,实际上是用了handler.post(action)方式。内部先判断当前线程是不是主线程,如果不是就通过handler发送一个消息。
 public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }
  • Activity的启动流程中(在ActivityThread的main方法里面调用Looper.prepareMainLooper())
    在这里插入图片描述
    在这里插入图片描述
    给sMainLooper赋值当前线程(即主线程)的Looper。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值