Android에서 Handler의 사용 및 소스 코드에 대한 상세 분석

[본 글은 '신인인재창출식' 이벤트에 참여하였으며, 함께 너겟창업의 길을 함께 시작하겠습니다.]

1. 안드로이드에서 핸들러의 역할

Android에서 Android 시스템은 다중 스레드 간의 메시지 전송 및 UI 작업 업데이트에 주로 사용되는 핸들 메커니즘인 다중 스레드 메시지 통신 메커니즘 세트를 캡슐화합니다.

2. Handler 구현 메커니즘

핸들의 구현 메커니즘에 대해 이야기하기 전에 먼저 몇 가지 기본 개념을 이해하십시오.

  • 핸들러 메시지 발신자 및 핸들러
  • 메시지 메시지 캐리어
  • 루프 폴러는 메시지 큐에서 메시지를 가져와 핸들에 배포하는 역할을 합니다.
  • MessageQueue는 저장소 핸들에서 보낸 메시지 대기열을 유지 관리합니다.

여기에 이미지 설명 삽입

3. Handle의 구체적인 용도

3.1 자식 스레드가 UI 스레드에 메시지를 보냅니다.

(1) 메시지를 보낼 Handler를 만들고 메시지를 처리하기 위해 handleMessage를 다시 작성합니다.비정적 내부 클래스는 기본적으로 외부 클래스의 참조를 보유하기 때문에 내부 정적 클래스를 사용하여 Handle을 정의합니다.액티비티가 소멸되면 메모리 누수가 발생하기 쉽지만 정적 클래스가 사용됩니다. 예, 핸들에 활동 참조가 필요하고 외부 활동에 대한 모든 참조는 약한 참조에 의해 유지됩니다. 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);
            }
        });
复制代码

전체 프로세스는 자식 스레드에서 핸들 개체를 만들고 메인 스레드에서 메시지를 보냅니다.

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

이때 Handle이 생성될 때 2개의 메소드가 호출되었는데, Looper.prepare(); Looper.loop();이 ?

핸들이 메시지 큐에 메시지를 보낸 다음 루퍼가 폴링하여 MessaeQueue 큐에서 메시지를 꺼내고 dispatchMessage 메서드를 통해 메시지를 배포하고 마지막으로 핸들 메시지가 핸들에 있는 메시지를 처리한다고 전에 말했습니다. 그런 다음 Looper对象和MessageQueue消息队列핸들 멀티스레딩에서 전달되는 메시지와 협력하기 위해 반드시 하나가 필요합니다. 다음으로 메인 스레드가 자식 스레드에 메시지를 보내는 예제를 통해 소스 코드가 구체적으로 무엇을 하는지 알아보겠습니다.

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이 비어 있지 않은 한 핸들의 메시지 처리를 가로챌 수 있습니다.

요약 : 위의 내용은 다중 스레드에서 핸들 통신에 대한 전체 분석 과정으로, 현재 스레드에서 Looper 및 MessageQueue 객체를 생성하고 여기에 Handle을 바인딩하고 현재 스레드를 Looper 스레드로 전환하여 메시지 큐를 모니터링할 수 있도록 합니다. 통과 MessageQueue에 메시지를 보낸 다음 Looper를 통해 메시지를 꺼내 핸들에 넘겨 처리합니다.또한 핸들을 통해 메시지를 보내 핸들의 handleMessage를 가로챌 수도 있습니다.

5. UI 스레드가 Looper와 MessageQueue를 생성하지 않은 이유는 무엇입니까?

프로그램이 시작될 때 시스템이 이미 ActivityThread의 기본 메서드를 볼 수 있도록 특정 코드를 생성했기 때문입니다.

  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");
    }
复制代码

위의 코드는 시스템이 시작될 때 우리를 위해 생성된 것으로, 구체적인 생성은 위의 분석과 유사하므로 더 이상 언급하지 않겠습니다.

추천

출처juejin.im/post/7084169943503077383