欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Handler实现原理解析

程序员文章站 2024-03-01 12:12:40
...

目录:

Handler

Handler概述:

Handler并不是专门用来更新UI界面的,只不过日常开发中,开发者用来更新UI比较多.

Handler的主要作用是:将一个任务切换到某个指定线程进行执行.

Handler的主要目的是:为了解决在子线程中无法更新UI界面的矛盾问题


Handler原理图:

Handler实现原理解析

主线程中为什么可以直接创建Hanlder:

在非主线程中创建Handler的时候需要先调用Looper.prepare();创建Looper对象以及完成消息队列的初始化,然 后创建Handler对象,最后,调用Looper.loop();开启消息循环;

而在主线程中,App的入口,ActivityThread.main方法中:

public static void main(String[] args) {
       // 不相干代码
       ......
       // 1.调用Looper.prepareMainLooper,其实也就是调用的Looper.loop,初始化Looper、MessageQueue等
       Looper.prepareMainLooper();
       // 2.创建ActivityThread的同时,初始化了成员变量Handler mH
       ActivityThread thread = new ActivityThread();
       thread.attach(false);
       // 
       if (sMainThreadHandler == null) {
           // 把创建的Handler mH赋值给sMainThreadHandler
           sMainThreadHandler = thread.getHandler();
       }

       if (false) {
           Looper.myLooper().setMessageLogging(new
                   LogPrinter(Log.DEBUG, "ActivityThread"));
       }
       // 3.调用Looper.loop()方法,开启死循环,从MessageQueue中不断取出Message来处理
       Looper.loop();

       throw new RuntimeException("Main thread loop unexpectedly exited");
   }

主线程不需要手动调用,是因为系统在启动App时,就帮我们调用了


Handler使用的两种方式:

**一:post方式**
Handler mHandler = new Handler();  
mHandler.post(new Runnable() {  
    @Override  
    public void run() {  }  
}); 

二:Message方式

//主线程中创建Handler
mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                // 处理返回的消息
            }
        };
        
//---------------------------------------------        

//在子线程总调用Handler发送消息
Message msg = Message.obtain();
msg.what = MSG_SUB_TO_MAIN;
msg.obj = "这是一个来自子线程的消息";
// 2.发送消息
mHandler.sendMessage(msg);


Handler中构造函数

public Handler(Callback callback, boolean async) {
        ...
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        ...
    }
  • 在创建Handler对象的时候,在构造函数中首先会初始化Looper对象并获取其中的MessageQueue消息队列对象;
  • 当创建Handler对象的线程中没有初始化Looper对象的时候就会导致抛出异常

post,Message调用的原理

post方式调用首先会调用getPostMessage(r)方法;
private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

在该方法中,创建一个Message对象,将需要执行的Runable对象设定为callback

随后,post方法就和Message方法一样,都会调用sendMessageDelayed()方法

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

在该方法中对设定的延迟时长进行判断,随后都调用了sendMessageAtTime(方法);

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()获取了Looper中的MessageQueue对象
  • 在enqueueMessage()中将Hnadler对象赋值给了msg.target,并且将传入的Message对象进行了enqueueMessage入队操作

Looper

Looper的构造函数

private Looper(boolean quitAllowed) {
        ...
        mQueue = new MessageQueue(quitAllowed);
        ...
    }

在创建的Looper的时候在Looper的构造函数中会首先初始化好一个MessageQueue用来保存需要处理的meassage,所以消息队列在looper对象进行初始化创建的时候一并创建好了.


Looper的创建

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));
    }
    
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

Looper创建的时候调用prepare方法,每个线程中只允许存在一个Looper,所以在重复创建会导致异常,当首次创建完成后,会将该Looper对象存储到sThreadLocal中,并且在构造函数中完成队列MessageQueue的初始化


开启Looper循环的loop()

public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        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);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ...省略部分代码
            msg.recycleUnchecked();
        }
    }
  • 在Looper中执行循环的是loop方法,在方法中是一个无限循环,其中就调用到了MessageQueue中的next方法,而next方法也是一个无效循环的方法,在没有消息的时候就处于阻塞状态,在有消息的时候就对消息进行返回并将该条消息从消息队列中移除;所以综合来说,Looper中的无线循环通过MessageQueue的next方法获取到了消息.

  • 唯一退出循环的判断条件是next为null,在什么情况下next会返回null呢?当调用Looper的quit方法时,会调用MessageQueue中的quit或者quitSaffely,两个退出方法二者的区别是一个立即退出,一个是将当前队列状态修改为退出状态,等任务执行完成之后退出,当消息队列被标记为退出状态时,next会返回null;

  • 在获取消息队列中的message对象后,会通过msg.target获取到Handler对象,去调用Handler中的dispatchMessage(msg);方法

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

private static void handleCallback(Message message) {
        message.callback.run();
    }
    

在dispatchMessage方法中,首先会判断Runnable对象是否为空,假如我通过post方法封装了需要执行的Runnable,那么在此处就会通过handleCallback()进行执行;而不论有没有,最终都会再执行Handler中的handleMessage()方法

MessgaeQueue工作原理:

MeassgaeQueue翻译的为消息队列,但它实质上并不是一个消息队列,它的内部是一个单链表的数据结构用来存储数据,但其并不对数据进行处理.

MessgaeQueue主要用到的有两个方法enqueueMessage和next,
enqueueMessage主要用来存储数据,想单链表中插入一条数据,next则是向链表中取出一条数据,并从队列中删除,需要说明的一点是,在next内部是一个无线循环,当没有消息的时候就处于阻塞状态,在有消息的情况下就会进行返回并将该条消息从队列中移除.

相关标签: android开发