Android 中 Handler的使用和源码详细分析

·  阅读 426
Android 中 Handler的使用和源码详细分析

[本文已参与「新人创作礼」活动,一起开启掘金创作之路]

1、Handler在Android中的作用

在Android中Android系统为我们封装了一套多线程间消息通信的机制---handle机制,主要用来多线程间的消息传递、用来更新UI操作。

2、 Handler的实现机制

在说handle的实现机制之前,首先先了解几个基本概念:

  • Handler 消息的发送者和处理者
  • Message 消息传递的载体
  • Loop 轮询器 负责从消息队列中取出消息,分发给handle
  • MessageQueue 维护存储handle发送过来的消息队列 在这里插入图片描述

3、Handle的具体使用

3.1 子线程发消息给UI线程

(1) 创建Handler 发送消息和重写handleMessage处理消息,采用内部静态类的方式去定义Handle 是因为非静态内部类默认持有外部类的引用,Activity销毁的时候容易引起内存泄漏,但是使用静态类的,handle中需要一个activity的引用,所有采用弱引用的方式去持有外部Activity的引用,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。

  static class MyHandle extends Handler{
  
        WeakReference<Activity > mActivityReference;

        MyHandle(Activity activity) {
            mActivityReference= new WeakReference<>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            MyTheadActivity activity = (MyTheadActivity) mActivityReference.get();
            activity.textView.setText("接收到数据:"+msg.arg1);
        }
    }
    MyHandle myHandle=new MyHandle(this);
复制代码

创建子线程发送数据

 new Thread(){
            @Override
            public void run() {
                super.run();
                while (num>0){
                    try {
                        num--;
                        Thread.sleep(100);
                        Message message = Message.obtain();
                        message.arg1=num;
                        myHandle.sendMessage(message);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
复制代码
3.2 UI线程发消息给子线程

在子线程中创建Handle对象;

private void initSubThread() {
        new Thread(){
            @Override
            public void run() {
                super.run();
                Looper.prepare();
                myHandle1 =new MyHandle(MyTheadActivity.this);
                Looper.loop();

            }
        }.start();
    }
复制代码

主线程中发送消息到子线程

 textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Message msg = Message.obtain();
                msg.arg1=100;
                myHandle1.sendMessage(msg);
            }
        });
复制代码

整个流程在子线程中创建handle 对象,在主线程发送消息。

08-05 21:53:34.963 1782-1795/camera.test.com.networkdata E/TAG: 线程名称:Thread-77接收到消息:100
复制代码

这个时候在创建Handle 的时候调用了2个方法 Looper.prepare(); Looper.loop(); 这两个方法是弄啥类,

之前说过 handle 发送消息是发送到一个Message Queue中,然后Looper轮询从MessaeQueue队列中取出消息,然后通过dispatchMessage 方法分发消息,最终handle中handleMessage处理消息。 那么肯定需要一个Looper对象和MessageQueue消息队列 来配合Handle多线程中消息传递。 接下来通过主线程发送消息到子线程的例子来看下源码都具体干了啥。

4、handle 源码看看

从上边的主线程发消息到子线程开始,加入不调用Looper.prepare(); Looper.loop(); 方法会怎么样。

08-05 22:08:12.423 1897-1916/camera.test.com.networkdata E/AndroidRuntime: FATAL EXCEPTION: Thread-80
    Process: camera.test.com.networkdata, PID: 1897
    java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
        at android.os.Handler.<init>(Handler.java:200)
        at android.os.Handler.<init>(Handler.java:114)
        at camera.test.com.networkdata.MyTheadActivity$MyHandle.<init>(MyTheadActivity.java:29)
        at camera.test.com.networkdata.MyTheadActivity$2.run(MyTheadActivity.java:68)
复制代码

发现 程序直接崩溃了,意思就是mLooper不能为空。需要个Looper,这个错误我们也可以通过查看Handle的源码看到

源码分析一:
 public Handler(Callback callback, boolean async) {
          ....
          省略一些代码
        mLooper = Looper.myLooper();   //源码分析二
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
复制代码

可以看到 Looper.myLooper(); 获取的Looper对象为null的时候就抛出RuntimeException异常。

源码分析二: Looper.myLooper();

从ThreadLocal 中获取Looper对象


   static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
   
 /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
复制代码
源码分析三: Looper.prepare();

通过以上分析一和分析二知道,子线程中使用handle必须需要一个Looper否则就会报错,那接下在看下Looper.prepare(); 都做了些什么

 public static void prepare() {
        prepare(true);
    }
	
    private static void prepare(boolean quitAllowed) {
     //解释一: 一个线程中只能有一个Looper对象
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread"); 
        }
        解释二: 通过ThreadLocal 存储Looper对象
        
        sThreadLocal.set(new Looper(quitAllowed)); // 源码分析四
    }
复制代码

总结:一个线程中只能有一个Looper对象,创建的Looper对象通过ThreadLocal进行存储,和创建Handle时候的sThreadLocal.get(); 相对应。

源码分析四: new Looper(quitAllowed)
 private Looper(boolean quitAllowed) {
 		解释一 :创建消息队列
        mQueue = new MessageQueue(quitAllowed);
        解释二 : 绑定到当前线程
        mThread = Thread.currentThread();
    }
复制代码

总结:终于看到MessageQueue 了,再不见还以为他是个摆设呢,创建Looper对象的时候,自动创建MessageQueue 消息队列。

通过以上分析:知道Looper.prepare(); 创建了 Looper对象和MessageQueue消息队列对象,并绑定到当前线程。以上只是创建了Looper和MessageQueue,并没有见如何 轮询从MessageQueue中取出数据,接下来就看下Looper.loop();

源码分析五: Looper.loop();
 public static void loop() {
 //  解释一 :获取Looper对象
        final Looper me = myLooper();
  //解释二 :如果looper是null抛出异常
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
    //解释三 :获取MessageQueue  消息队列
        final MessageQueue queue = me.mQueue;
        
          省略一些代码
     
     // 解释四 : 开启一个无限循环从MessaQueue中取出消息
     
        for (;;) {
        消息队列中取出消息,消息为空return
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

          省略了一些代码
          
            try {
           // 解释五: 	取出的消息进行消息分发,msg.target 是啥呀?就是发送消息的Handle 对象,
           最终调用的是handle中 dispatchMessage方法/
                msg.target.dispatchMessage(msg);    源码分析 六
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
        
            msg.recycleUnchecked();
        }
    }
复制代码

总结: 通过以上代码知道,Looper.loop(); 就是looper 轮询器开始循环的从消息队列中取出数据,然后交给Handle去处理。

源码分析 六 dispatchMessage()
public void dispatchMessage(Message msg) {
	
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
          
            handleMessage(msg);
        }
    }
复制代码

总结:通过看handle中的dispatchMessage方法,发现如果mCallback 不为null的时候,最终调用handleMessage()方法,也就是我们经常重写处理消息的方法。

总结:通过以上分析,可以得出handle同过sendMessage方法发送消息,到消息队列然后looper从消息队列中取出消息然后最终还是交给handle去处理,那么handle是怎么和Looper、MessaheQueue进行关联的的嘞?接下来看下 创建Hanlde过程中都做了那些操作

源码分析 七 创建Handle过程

Handle创建有几种方式

第一类:不需要指定Looper对象,

 public Handler() {}
 
 public Handler(boolean async){}
 
 public  Handler(Callback callback) {}
  
 public Handler(Callback callback, boolean async) {}
 
 
第二类:自己传一个Looper 对象

 public Handler(Looper looper){}

 public Handler(Looper looper, Callback callback){}
 
 public Handler(Looper looper, Callback callback, boolean async) {} 

复制代码

先看下第一种创建方式,第一种最终都会调用 public Handler(Callback callback, boolean async) {} 方法

public Handler(Callback callback, boolean async) {
      
       省略一些代码
      //获取Looper对象,之前源码分析二中分析过了
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        // 初始化消息队列
        mQueue = mLooper.mQueue;
        // 初始化回调 ,这个回调是干什么的?之后再说
        mCallback = callback;
        // 是否异步
        mAsynchronous = async;
    }
复制代码

总结 :通过以上创建Handle的源码,发现初始化了Looper对象和mQueue 消息队列,callback回调,

然后再看下第二种创建方式,最后调用的是 public Handler(Looper looper, Callback callback, boolean async) {}

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

和第一种方式区别不是很大,只是Looper是自己创建的,传进来的,注意Lopoer在那个线程创建,这个线程就属于一个Looper线程。

总结:通过以上源码可以知道,在创建Handle的时候,handle和当前的线程的Looer、messageQueue进行关联的。然后handle发送消息到MessageQueue中,looper从MessageQueue取出消息,交给handle处理,整个流程就已经清晰可见。但是handle是怎么发送消息到消息队列的呢。

源码分析 八 消息发送
第一种 sendXXX 系列 最终都调用的是 sendMessageAtTime

public final boolean sendMessage(Message msg)

public final boolean sendEmptyMessage(int what){}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) 

public final boolean sendMessageDelayed(Message msg, long delayMillis)

public boolean sendMessageAtTime(Message msg, long uptimeMillis) 

第二种  postXX系列  其实最红调用的也是 sendMessageAtTime方法

 public final boolean post(Runnable r)

 public final boolean postAtTime(Runnable r, long uptimeMillis)

 public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)

 public final boolean postDelayed(Runnable r, long delayMillis)

复制代码

第一种和第二种方法不一样的地方就是,第二种方法需要一个Runnable 接口。很熟悉者不是创建线程的时候的Runnable 的么怎么这里也用到看下 public final boolean post(Runnable r)

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

getPostMessage( r ) 获取一个Message对象

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

Runnable 直接赋值给Messge的callback了。那么有什么用呢,这个看下面的handle的dispatchMessage方法。

接下来看下sendMessageAtTime

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
		获取到MessageQueue
        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);
    }
复制代码

enqueueMessage方法

  private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  // 当前handle对象赋值给 message的target 指定处理message对象
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        
        return queue.enqueueMessage(msg, uptimeMillis);
    }
复制代码

queue.enqueueMessage 存放消息到消息队列 (大概知道就行)

 boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
         省略一些代码
         // 整体意思就是 ,按照消息发送时间存放到对列当中 ,存放成功返回True 
            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;
    }
复制代码

总结:以上就是发送消息的整个过程。通过sendXXX或者postXXX方法最终都调用的是sendMessageAtTime()方法将消息发送到Message Queue消息队列中,并指定消息处理对象。

上边说到postXX的方式,传入一个Runnable接口,最终赋值给Message的callBack,那么到底有什么用呢再次看下dispatchMessage

 public void dispatchMessage(Message msg) {
 // 看到了把 如果msg.callback != null 调用  handleCallback(msg);
        if (msg.callback != null) {
            handleCallback(msg);
        } else {  
         //mCallback 又是什么 ,通过第一种创建 Handle方式,
            有一个参数为Callback ,这也是一个接口,如果不为空的话,就直接调用 他的 handleMessage 
            进行消息处理,   否则再交给 handle自己的handleMessage 处理
            if (mCallback != null) { 
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
复制代码

看下 handleCallback 都做了什么

 private static void handleCallback(Message message) {
    //  最终调用的是 接口run方法。 而且不会再走    handleMessage(msg); 方法了
        message.callback.run();
    }
复制代码

通过dispatchMessage 方法得知 Handle 的handleMessage 处理消息优先级最低,只要我们的 msg.callback 和mCallback 不为空,都可以拦截 handle的消息处理。

总结 : 以上是handle在多线程中通信的整个分析流程,在当前线程创建Looper和MessageQueue对象,并将Handle与之绑定,将当前线程变成Looper线程,并开启对消息队列的监听,handle通过发送消息到MessageQueue,然后通过Looper取出消息,并交给handle处理,还可以通过handle发送消息的方式,进行对handle的handleMessage 进行拦截处理。

5 、那为什么没见UI线程创建Looper和MessageQueue呢?

那是因为在程序启动的时候,系统已经帮我们创建好了具体代码看ActivityThread的main方法

  public static void main(String[] args) {
  
      //  省略一些代码

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

     //   省略一些代码
      
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
复制代码

以上代码就是系统启动的时候帮我们创建的,具体创建和上边分析的差不多,就不多说了

分类:
Android
标签:
收藏成功!
已添加到「」, 点击更改