Android 面试题之Handler

10 篇文章 0 订阅
4 篇文章 0 订阅

Handler 老东西了,但是每次面试基本都会问,个人理解handler就是跨线程通讯的一个工具类

Handler流程主要分为4个类

1.Message 就是消息的实体类(承载体)

2.Handler 用于处理消息的类

3.MessageQueue 消息队列

4.Looper 通过无线循环取出要用的消息

Handler 的使用也很简单

    /**
     * 子线程初始化handler
     */
    private void testHandler1() {
        //获取当前线程的Looper对象
        Looper looper = Looper.myLooper();
        if (looper == null) {
            //如果当前线程没有Looper(looper == null)
            //调用 Looper.prepare() 初始化Looper对象 并且初始化对应的MessageQueue
            Looper.prepare();
            //重新将刚创建的Looper赋值
            looper = Looper.myLooper();
        }
        //初始化的时候是需要传入Looper的,并且不能为空,新的api都需要传的
        handler = new Handler(looper) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                handler.getLooper().quit();
            }
        };
        //这里开启Looper的死循环 保证当前所在的子线程不会结束 如果是主线程就不必调用loop()
        Looper.loop();
    }
    /**
     * 子线程发送消息
     * handler.sendMessage()
     * 默认Handler已经初始化成功
     */
    private void testHandler2() {
        //循环 知到handler初始化完毕为止
        while (handler == null) {

        }
        Message message = Message.obtain();
        message.what = 10;
        handler.sendMessage(message);
    }
        new Thread(new Runnable() {
            @Override
            public void run() {
                testHandler1();
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                testHandler2();
            }
        }).start();

开启两个线程实现子线程之间的通讯

Handler 的建立

1. Handler的构造方法

    public Handler(@NonNull Looper looper) {
        this(looper, null, false);
    }
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
很明显这里的Looper是不能传空的,不然 looper.mQueue 回报空指针异常。而 mQueue 就是消息队列 MessageQueue.
这里涉及到一个知识点:线程刚创建的时候并没有Looper,所以我们需要通过如下方法实例化一个Looper的对象
//调用 Looper.prepare() 初始化Looper对象 并且初始化对应的MessageQueue
            Looper.prepare();
如果是在子线程,根据需求可以使用Looper.loop() 方法来保持线程的存活
        //这里开启Looper的死循环 保证当前所在的子线程不会结束 如果是主线程就不必调用loop()
        Looper.loop();

有关于消息队列 MessageQueue 在Looper初始化的时候就已经创建了

有关于Looper.loop()方法里面有着很明显的无线循环,但是对于Handler流程来说我们只需要注意关键的代码即可

msg.target.dispatchMessage(msg);

在loop()方法中有这样一段代码 msg就是Message Message.target是一个Handler对象,调用的其实就是Handler.dispatchMessage方法

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

里面有 handleMessage 方法 就是我们实例化Handler时的handleMessage

        handler = new Handler(looper) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                //该方法用来接收消息Message
            }
        };

有关于消息怎么插入消息队列

        Message message = Message.obtain();
        message.what = 10;
        handler.sendMessage(message);
        //sendMessage 点击去查看我们可以定位到这里
        private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
        }
        //我们只看return的部分 MessageQueue 执行的时消息队列插入消息的方法
        //在MessageQueue里面
        boolean enqueueMessage(Message msg, long when) {
        	synchronized (this) {
           	 msg.markInUse();
          	  msg.when = when;
          	  Message p = mMessages;
          	  boolean needWake;
          	  if (p == null || when == 0 || when < p.when) {
          	      // New head, wake up the event queue if blocked.
          	      msg.next = p;
          	      mMessages = msg;
          	      needWake = mBlocked;
           	 } else {
             	   // Inserted within the middle of the queue.  Usually we don't have to wake
             	   // up the event queue unless there is a barrier at the head of the queue
            	    // and the message is the earliest asynchronous message in the queue.
            	    needWake = mBlocked && p.target == null && msg.isAsynchronous();
            	    Message prev;
             	   for (;;) {
                	    prev = p;
                 	   p = p.next;
                 	   if (p == null || when < p.when) {
                 	       break;
                  	  }
                 	   if (needWake && p.isAsynchronous()) {
                 	       needWake = false;
                	    }
            	    }
              	  msg.next = p; // invariant: p == prev.next
             	   prev.next = msg;
            }
           	 // We can assume mPtr != 0 because mQuitting is false.
          	  if (needWake) {
          	      nativeWake(mPtr);
          	  }
        }
        return true;
    }
nativeWake 是C++实现的方法 我将其直接翻译了叫做本地唤醒

过程如下:

用户建立好Message;

调用Handler的sendMessage方法把Message送入队列MessageQueue里;

这时候Looper通过无线循环来取出需要处理的Message;

最后Message就会被送给Handler处理。

一般情况下Handler都会在主线程建立,所以这玩意常用来更新UI(如果handler在子线程建立就不能更新UI了)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值