Android Handler、Message完全解析,带你从源码的角度彻底理解

之前也是由于周末通宵看TI3比赛,一直没找到时间写博客,导致已经有好久没更新了。惭愧!后面还会 恢复进度,尽量保证每周都写吧。这里也是先恭喜一下来自瑞典的Alliance战队夺得了TI3的冠军,希望明年 中国战队能够虎起!

开始进入正题,我们都知道,Android UI是线程不安全的,如果在子线程中尝试 进行UI操作,程序就有可能会崩溃。相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也 是早已烂熟于心,即创建一个Message对象,然后借助Handler发送出去,之后在Handler的handleMessage() 方法中获得刚才发送的Message对象,然后在这里进行UI操作就不会再出现崩溃了。

这种处理方式被 称为异步消息处理线程,虽然我相信大家都会用,可是你知道它背后的原理是什么样的吗?今天我们就来一 起深入探究一下Handler和Message背后的秘密。

首先来看一下如何创建Handler对象。你可能 会觉得挺纳闷的,创建Handler有什么好看的呢,直接new一下不就行了?确实,不过即使只是简单new一下, 还是有不少地方需要注意的,我们尝试在程序中创建两个Handler对象,一个在主线程中创建,一个在子线程 中创建,代码如下所示:

public class MainActivity extends Activity {  

    private Handler handler1;  

    private Handler handler2;  

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        handler1 = new Handler();
        new Thread(new Runnable() {
            @Override
            public void run() {
                handler2 = new Handler();
            }
        }).start();
    }  

}

如果现在运行一下程序,你会发现,在子线程中创建的Handler是会导致程序崩溃的,提示的错误 信息为 Can't create handler inside thread that has not called Looper.prepare() 。说是不能在 没有调用Looper.prepare() 的线程中创建Handler,那我们尝试在子线程中先调用一下Looper.prepare()呢 ,代码如下所示:

new Thread(new Runnable() {
    @Override
    public void run() {
        Looper.prepare();
        handler2 = new Handler();
    }
}).start();

果然这样就不会崩溃了,不过只满足于此显然是不够的,我们来看下Handler的源码, 搞清楚为什么不调用Looper.prepare()就不行呢。Handler的无参构造函数如下所示:

public 

Handler() {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) 

&&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = null;
}

可以看到,在第10行调用了Looper.myLooper()方法获取了一个Looper对象,如果Looper对象为空 ,则会抛出一个运行时异常,提示的错误正是 Can't create handler inside thread that has not called Looper.prepare()!那什么时候Looper对象才可能为空呢?这就要看看Looper.myLooper()中的代码 了,如下所示:

public static final Looper myLooper() {
    return (Looper)sThreadLocal.get();
}

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索线程
, new
, handler
, 中创
, prepare
, Looper
, looper.prepare
Handler+线程
handler message、messagehandler、handler message 传值、handlermessage、安卓 handler message,以便于您获取更多的相关知识。

时间: 2024-09-08 11:28:20

Android Handler、Message完全解析,带你从源码的角度彻底理解的相关文章

Android AsyncTask完全解析 带你从源码的角度彻底理解_Android

我们都知道,Android UI是线程不安全的,如果想要在子线程里进行UI操作,就需要借助Android的异步消息处理机制.之前我也写过了一篇文章从源码层面分析了Android的异步消息处理机制. 不过为了更加方便我们在子线程中更新UI元素,Android从1.5版本就引入了一个AsyncTask类,使用它就可以非常灵活方便地从子线程切换到UI线程,我们本篇文章的主角也就正是它了. AsyncTask很早就出现在Android的API里了,所以我相信大多数朋友对它的用法都已经非常熟悉.不过今天我

Android AsyncTask完全解析 带你从源码的角度彻底理解

我们都知道,Android UI是线程不安全的,如果想要在子线程里进行UI操作,就需要借助Android的异步消息处理机制.之前我也写过了一篇文章从源码层面分析了Android的异步消息处理机制. 不过为了更加方便我们在子线程中更新UI元素,Android从1.5版本就引入了一个AsyncTask类,使用它就可以非常灵活方便地从子线程切换到UI线程,我们本篇文章的主角也就正是它了. AsyncTask很早就出现在Android的API里了,所以我相信大多数朋友对它的用法都已经非常熟悉.不过今天我

Android AsyncTask完全解析,带你从源码的角度彻底理解

我们都知道,Android UI是线程不安全的,如果想要在子线程里进行UI操作,就需要借助Android的异步消息处理机制.之前我也写过了一篇文章从源码层面分析了Android的异步消息处理机制,感兴趣的朋友可以参考 Android Handler.Message完全解析,带你从源码的角度彻底理解 . 不过为了更加方便我们在子线程中更新UI元素,Android从1.5版本就引入了一个AsyncTask类,使用它就可以非常灵活方便地从子线程切换到UI线程,我们本篇文章的主角也就正是它了. Asyn

Android事件分发机制完全解析,带你从源码的角度彻底理解(下)

记得在前面的文章中,我带大家一起从源码的角度分析了Android中View的事件分发机制,相信阅读过的 朋友对View的事件分发已经有比较深刻的理解了. 还未阅读过的朋友,请先参考 Android事件分发机 制完全解析,带你从源码的角度彻底理解(上) . 那么今天我们将继续上次未完成的话题,从源码的 角度分析ViewGruop的事件分发. 首先我们来探讨一下,什么是ViewGroup?它和普通的View有什么区 别? 顾名思义,ViewGroup就是一组View的集合,它包含很多的子View和子

Android 带你从源码的角度解析Scroller的滚动实现原理

今天给大家讲解的是Scroller类的滚动实现原理,可能很多朋友不太了解该类是用来干嘛的,但是研究Launcher的朋友应该对他很熟悉,Scroller类是滚动的一个封装类,可以实现View的平滑滚动效果,什么是实现View的平滑滚动效果呢,举个简单的例子,一个View从在我们指定的时间内从一个位置滚动到另外一个位置,我们利用Scroller类可以实现匀速滚动,可以先加速后减速,可以先减速后加速等等效果,而不是瞬间的移动的效果,所以Scroller可以帮我们实现很多滑动的效果. 在介绍Scrol

Google Protocol Buffers快速入门(带生成C#源码的方法)

Google Protocol Buffers是google出品的一个协议生成工具,特点就是跨平台,效率高,速度快,对我们自己的程序定义和使用私有协议很有帮助. Protocol Buffers入门: 1.去 http://code.google.com/p/protobuf/downloads/list 下载一个源代码包和一个已编译好的二进制包 2.找一个Proto示例代码,使用命令 protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/address

艾伟:WCF从理论到实践(17):OO大背离(带视频+ppt+源码)

本系列文章导航 WCF从理论到实践(1):揭开神秘面纱 WCF从理论到实践(2):决战紫禁之巅 WCF从理论到实践(3):八号当铺之黑色契约 WCF从理论到实践(4):路在何方 WCF从理论到实践(5):Binding细解 WCF从理论到实践(6):WCF架构 WCF从理论到实践(7):消息交换模式 WCF从理论到实践(8):事件广播 WCF从理论到实践(9):实例模式和对象生命周期 WCF从理论到实践(10):异常处理 WCF从理论到实践(11)-异步 WCF从理论到实践(12):事务 WCF

android 手势密码 画图 有可以实现的源码

问题描述 android 手势密码 画图 有可以实现的源码 10C android 手势密码 还要用红色的线画出错误的图片,这个效果没有实现. 解决方案 Android招财进宝手势密码的实现Android招财进宝手势密码的实现Android招财进宝手势密码的实现 解决方案二: http://blog.csdn.net/wulianghuan/article/details/40536635 解决方案三: http://www.android100.org/html/201502/13/11838

android 自定义控件 使用declare-styleable进行配置属性(源码角度)

http://blog.csdn.net/vipzjyno1/article/details/23696537 最近在模仿今日头条,发现它的很多属性都是通过自定义控件并设定相关的配置属性进行配置,于是便查询了解了下declare-styleable,下面我把自己的使用感受和如何使用进行说明下. declare-styleable:declare-styleable是给自定义控件添加自定义属性用的. 官方的相关内部控件的配置属性文档:http://developer.android.com/ref