(1) Looper类别用来为一个线程开启一个消息循环。默认情况下Android中新诞生的线程是没有开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环)
Looper对象通过MessageQueue来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。
在创建Handler的时候可以通过Context.getMainLooper()获取主线程的Loooper,如:
this.mThirdHandler = new Handler(mContext.getMainLooper()){}
(2) 通常是通过Handler对象来与Looper交互的。Handler可看做是Looper的一个接口,用来向指定的Looper发送消息及定义处理方法。
默认情况下Handler会与其被定义时所在线程的Looper绑定,比如,在主线程中定义,其是与主线程的Looper绑定。
mainHandler = new Handler() 等价于new Handler(Looper.myLooper()).
Looper.myLooper():Return the Looper object associated with the current thread 获取当前进程的looper对象。
还有一个类似的 Looper.getMainLooper() 用于获取主线程的Looper对象。
(3) 在非主线程中直接new Handler() 会报如下的错误:
E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception
E/AndroidRuntime( 6173): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
原因是非主线程中默认没有创建Looper对象,需要先调用Looper.prepare()启用Looper。
(4) Looper.loop(); 让Looper开始工作,从消息队列里取消息,处理消息。
注意:写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。
(5) 基于以上知识,可实现主线程给子线程(非主线程)发送消息。
Toast或者Dialog中都有一个Handler的成员变量,在初始化时都会跟着初始化,而Toast或者Dialog中的Handler都需要一个Looper,所以需要在包含该Toast或者Dialog的线程中(如下面的Timer线程)初始化Looper。Looper.prepare();
问题代码:
private Handler myHandler = new Handler() { public void handleMessage(Message msg) { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { InputMethodManager m = (InputMethodManager) editText .getContext().getSystemService( Context.INPUT_METHOD_SERVICE); m.showSoftInput(editText, 0); // Looper.prepare(); Toast.makeText(Main.this, "show", Toast.LENGTH_LONG).show(); Looper.loop(); } }, 1000); } }
Toast 和 Looper,一个属于 android.widget,一个属于 android.os,两个貌似联系不怎么紧密的类,却通过下面这个异常联系到了一起:
E/AndroidRuntime( 1819): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() E/AndroidRuntime( 1819): at android.os.Handler.<init>(Handler.java:121) E/AndroidRuntime( 1819): at android.widget.Toast.<init>(Toast.java:68) E/AndroidRuntime( 1819): at android.widget.Toast.makeText(Toast.java:231)
Handler.java:121
119 mLooper = Looper.myLooper(); 120 if (mLooper == null) { 121 throw new RuntimeException( 122 "Can't create handler inside thread that has not called Looper.prepare()");}
成员变量,在初始化时会跟着初始化
68 final Handler mHandler = new Handler();
由以上的错误信息可以看出:程序要创建 handler,但是发现Looper.prepare还没有被调用。通过 Android SDK 中的Reference可以看到,Looper、Handler 的调用是非常有讲究的,如下面示例代码:
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
言归正题,继续寻找 Toast、Looper 和 Handler 三者之间的联系,也许谜底就能解开了。欲揭谜底,从源码入手是一条捷径。
Toast.java 的第231行的代码是创建一个新的Toast实例,而实例化的过程中,就需要执行第68行,也就是声明并创建Handler(成员变量)的实例。那么来看Handler.java的第121行到底做了什么,如下所示:
119 mLooper = Looper.myLooper(); 120 if (mLooper == null) { 121 throw new RuntimeException( 122 "Can't create handler inside thread that has not called Looper.prepare()");}
到此,距离真相的解开近了一大步,既然抛出了 RuntimeException,那么 mLooper 肯定是 null,但是为什么 Looper.myLooper() 会返回 null?继续进入到 Looper.java 中寻根究底。
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static final Looper myLooper() { return (Looper)sThreadLocal.get(); }
以上就是 myLooper() 方法的真实面貌,通过注释可以看出问题的真正原因在于当前线程并没有绑定 Looper,返回为 null 是正确但非正常的结果。
相关推荐
### Android之Looper、MessageQueue、Handler与消息循环详解 #### 一、概述 在Android开发过程中,消息处理机制是至关重要的部分,它涉及到应用程序如何管理、传递和响应各种事件。本篇文章将深入探讨Android中...
在Android系统中,Handler、Message和Looper构成了一个关键的异步通信机制,即Handler消息处理机制。这个机制允许Android应用程序在不同的线程间传递消息,处理UI更新等操作,是多线程编程中的重要组成部分。下面...
2. **`Looper`的作用**:`Looper`的主要作用是为线程创建一个消息循环,这个消息循环将不断地从`MessageQueue`中取出消息并交给对应的`Handler`进行处理。需要注意的是,`Looper.prepare()`方法在一个线程中只能被...
Handler消息机制是Android系统中处理线程间通信的重要方式,它与Looper和Message紧密关联,构建了一套完整的异步处理模型。在这个模型中,Handler主要负责发送和处理Message,Looper负责在一个线程中循环地读取...
在Android开发中,`Handler`消息传递机制是实现线程间通信的重要手段,尤其是在更新UI时。本项目中,我们看到一个简单的计数器应用,它利用`Handler`来每秒增加文本框中的数字,直到达到100为止。下面我们将深入探讨...
【Android的Handler消息传递机制详解】 在Android应用开发中,多线程的使用是必不可少的,尤其是当涉及到耗时操作如网络请求、大数据处理时。然而,Android系统规定,所有与用户界面(UI)交互的操作必须在主线程...
在Android开发中,Handler、Looper和Message是实现异步消息处理的关键组件,它们共同构成了Android的消息传递机制。这个机制使得应用程序能够优雅地处理UI线程与后台线程之间的通信,避免了因直接操作UI线程而导致的...
**消息循环机制:** - 应用程序运行时,`Application`类会启动一个全局的消息循环,不断从操作系统获取消息并分发给相应的窗口。 - 对于每个消息,`Application`会根据消息类型调用不同的处理方法。例如,对于鼠标...
调用`Looper.loop()`方法后,会进入一个无限循环,不断从MessageQueue中取出待处理的消息并交给对应的Handler处理。 #### 五、异步消息处理流程 1. **初始化**:在主线程中创建Handler对象,并重写`handleMessage...
4. **Looper(循环器)**:Looper是消息队列和Handler之间的协调者,它在一个线程中循环读取Message Queue,取出Message并交给对应的Handler进行处理。主线程默认已经有一个Looper,而其他线程需要手动调用`Looper....
在Android开发中,Handler回调机制是用于解决线程间通信的核心技术之一,它与Looper和Message紧密相连,共同构建了Android的消息处理系统。本篇将深入解析Handler回调机制,并通过提供的"handler的回调机制"DEMO来...
Handler消息机制是Android系统中处理多线程通信的核心组件,它在应用程序开发中扮演着重要角色。通过Handler、Looper和Message三者之间的协同工作,开发者可以在主线程与子线程之间进行安全的数据传递和任务调度。 ...
在Android开发中,Handler消息机制是一种关键的异步通信方式,尤其在处理UI更新和线程间通信时显得尤为重要。标题“handler消息机制实现控制进度条”涉及到的是如何利用Handler来实时更新进度条(ProgressBar)的...
本教程将详细讲解如何使用Java语言来实现Android的Handler消息机制,包括其基本原理、创建步骤以及如何实现跨线程通信和发送延迟消息。 一、Handler基本原理 Handler是Android系统提供的一个用于处理线程间通信的...
3. **Looper(循环器)**:Looper是消息队列的管理者,它在一个线程中不断循环地从消息队列中取出待处理的消息并分发给相应的Handler。在主线程中,Looper已经默认初始化并运行;而在工作线程中,如果需要使用...
Handler使用简单功能强大,常被用作线程间传递消息的组件,而且还可以用于跨进程。 消息机制背后有包括Looper ,MessageQueue管理和分发消息的实现,...这里从Java层开始深入探究下Handler和消息机制背后实现的原理
Looper 相当于一个 MessageQueue 的管理人员,它会不停的循环的遍历队列,然后将符合条件的 Message 一个个的拿出来交给 Handler 进行处理。 为什么需要多线程?在日常应用中,我们通常需要处理一些“后台,用户不...
`Looper`维持着消息循环,不断地从队列中取出消息并分发,而`Handler`则负责创建、发送和处理这些消息,实现了线程间的通信和同步。这种机制使得开发者能够在不阻塞主线程的情况下执行耗时操作,保证了UI的流畅性。...
Android 应用程序中,消息处理机制是其异步编程的核心组成部分,主要涉及三个关键组件:Handler、Message和Looper。这种机制使得应用可以有效地管理线程间的通信,避免阻塞主线程,提升用户体验。 首先,我们需要...
loop()方法是个死循环,将会不停的从MessageQueue对象中获取Message对象,如果MessageQueue 对象中不存在Message对象,则结束本次循环;最终将会执行handleMessage()方法,也就是我们ServiceHandler类中复写的方法。...