安卓源码学习之Handler执行流程

学安卓也有一段时间了,一直都是做项目或者看别人的博客,自己从来没有写一片像样的文章来记录自己所学到的知识,现在发现如果把所学到得知识记录下来对知识也是一种温习,并且会掌握得更加牢固。


今天记录我所掌握的Handler消息机制执行流程,平时我们都会使用到Handler对象来进行延迟消息的处理或者结合线程来更新UI控件,但是对里面的原理也不是很了解,所以我今天看了一下源码,把看到的东西记录下来。
首先我们使用Handler肯定的new一个它的对象,那new的过程当中它都做了些什么事呢?于是乎我打开了源码:

//这里是它的午餐构造方法,它总共有几个构造方法,最终都是调用的这个构造
//方法下面的构造方法
public Handler() {
        this(null, false);
    }
    //最终调用的是这个构造方法
public Handler(Callback callback, boolean async) {
        ...
        mLooper = Looper.myLooper();//这里可以看到获取了Looper对象
        if (mLooper == null) {
        //如果looper对象为空,则抛出异常,这里先给出一点疑问,就是,为甚
        //么Looper.myLooper()方法可能会为空呢
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;//获取消息队列
        mCallback = callback;
        mAsynchronous = async;
    }

当然,获取到Handler对象以后肯定是要使用它的,我们通过调用它的sendMessage(msg)方法或者其他sendXXX方法来发送消息,于是乎我们得有一个消息对象啊,我们可以这么做:
Message msg=new Message()或者Message msg=handler.obtainMessage();大家都知道下面这个方法效率更加高,这是为什么呢请看源码:

public final Message obtainMessage(){
    return Message.obtain(this);
}
//最终handler.obtinMessage()会调用Message对象的这个方法:
public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                sPoolSize--;
                return m;
            }
        }
        return new Message();
}
//从上面可以看到,如果使用obtinMessage()方法会先往消息池里面取出,如果去不到则实例化一个返回,这就是为什么obtinMessage比newMessage效率高的原因!

好了,获取到Message对象以后肯定得发送消息啊,于是我们跟踪一下handler.sendMessage(Message msg)方法源码:

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);
}
//最终调用了这个方法
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
     ...
     //可以看到这里会对消息进行排序
    return enqueueMessage(queue, msg, uptimeMillis);
 }

可以看到发送消息最终会调用enqueueMessage(queue, msg, uptimeMillis)方法,我们进入这个方法一看究竟:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        ...
        //这里调用了MessageQueue的enqueueMessage方法
        return queue.enqueueMessage(msg, uptimeMillis);
}
根据MessageQueue的enqueueMessage方法:
boolean enqueueMessage(Message msg, long when) {
        ...
        synchronized (this) {
            ...
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //这里判断是否发送消息的延迟时间when 是否为0,如果不是0的话
            //则将其加入到队列的前面
            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 {
                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;
    }
    //从上面的代码可以分析出如果出入的延迟时间为0则优先级最高
//这里是从消息队列中取出消息以后底层调用的方法,好像从这里以后这个方法是系
//底层调用的,查了一下资料貌似是一个linux管道设置的方法
 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //这里调用了handleMessage方法
            handleMessage(msg);
        }
    }

//于是调用了发送消息以后就会调用handlerMessage(Message msg)方法进行我们自己的代码处理了。
具体的流程图可以用如下图来表示:

引入上面我提出的问题,就是在Handler的无参构造方法里:

public Handler(Callback callback, boolean async) {
        ...
        mLooper = Looper.myLooper();//这里可以看到获取了Looper对象
        if (mLooper == null) {
        //如果looper对象为空,则抛出异常,这里先给出一点疑问,就是,为甚
        //么Looper.myLooper()方法可能会为空呢
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;//获取消息队列
        mCallback = callback;
        mAsynchronous = async;
    }

我提出的为什么Looper.myLooper();可能获取的Looper对象为空?首先想一下我们有时候在子线程中创建Handler对象的时候会发生什么情况呢?当然是报错了,这个报错的信息就是上面源码中的提示信息:”Can’t create handler inside thread that has not called Looper.prepare()”,那为什么主线程中new 一个Handler对象就不会报错呢?那是因为在主线程创建的时候系统就会跟着创建一个Looper对象,而子线程中不会主动创建Looper对象,当我们在子线程中先这样写:

Looper.prepare();
....
Looper.loop();

就不会报错了,这里贴出主线程创建Looper对象的源码:

 public static void main(String[] args) {
        ...

        Looper.prepareMainLooper();//创建主线程的Looper对象

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

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

        AsyncTask.init();

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        Looper.loop();//循环取出消息

所以我们在主线程中new Handler不会报错,在子线程中需要加上Looper.prepare()和Looper.loop()方法的原因了。

具体的流程就分析完毕,如果不对的地方麻烦看的朋友指出一下,我写博客是为了把自己学习的东西记录下来,同时锻炼一下自己的表达能力,希望如果有不对的地方麻烦老师指点一下,谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值