Android在子线程用handler取消发送消息发送的消息,主线程是怎么loop到的

MainActivity如下:
package cc.c;
import android.app.A
import android.os.B
import android.os.H
import android.os.L
import android.os.M
import android.widget.TextV
* Demo描述:
* 示例步骤如下:
* 1 子线程给子线程本身发送消息
* 2 收到1的消息后,子线程给主线程发送消息
* 3 收到2的消息后,主线程给子线程发送消息
* 为实现子线程给自己本身发送消息,关键还是在于构造Handler时传入的Looper.
* 在此就传入该子线程自己的Looper即调用Looper.myLooper(),代码如下:
* Looper.prepare();
* mHandlerTest1=new HandlerTest1(Looper.myLooper());
* Looper.loop();
* 所以当mHandlerTest1.sendMessage(message);发送消息时
* 当然是发送到了它自己的消息队列.
* 当子线程中收到自己发送的消息后,可继续发送消息到主线程.此时只要注意构造
* Handler时传入的Handler是主线程的Handler即可,即getMainLooper().
* 其余没啥可说的.
* 在主线程处理消息后再发消息到子线程
* 其实这些线程间发送消息,没有什么;关键还是在于构造Handler时传入谁的Looper.
public class MainActivity extends Activity {
private TextView mTextV
private HandlerTest1 mHandlerTest1;
private HandlerTest2 mHandlerTest2;
private int counter=0;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
private void init() {
mTextView = (TextView) findViewById(R.id.textView);
//1 子线程发送消息给本身
new Thread() {
public void run() {
Looper.prepare();
mHandlerTest1=new HandlerTest1(Looper.myLooper());
Message message = new Message();
message.obj = "子线程发送的消息Hi~Hi";
mHandlerTest1.sendMessage(message);
Looper.loop();
}.start();
private class HandlerTest1 extends Handler {
private HandlerTest1(Looper looper) {
super(looper);
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println("子线程收到:" + msg.obj);
收到消息后可再发消息到主线程
mHandlerTest2=new HandlerTest2(getMainLooper());
Message message = new Message();
message.obj = "O(&_&)O";
mHandlerTest2.sendMessage(message);
private class HandlerTest2 extends Handler {
private HandlerTest2(Looper looper) {
super(looper);
public void handleMessage(Message msg) {
super.handleMessage(msg);
mTextView.setText("在主线程中,收到子线程发来消息:" + msg.obj);
收到消息后再发消息到子线程
if (counter==0) {
Message message = new Message();
message.obj = "主线程发送的消息Xi~Xi";
mHandlerTest1.sendMessage(message);
counter++;
main.xml如下:
&RelativeLayout xmlns:android="/apk/res/android"
xmlns:tools="/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"
android:layout_centerInParent="true"
android:layout_marginTop="70dip" /&
&/RelativeLayout&
1.线程没有终止条件,会一直给主线程发消息,主线程不停的调用handleMessage代码,很容易ANR(应用程序不响应)2.handler.obtainMessage()得到message对象比new Message();更高效&
Handler对象与其调用者在同一线程中,如果在Handler中设置了延时操作,则调用线程也会堵塞。每个Handler对象都会绑定一个Looper对象,每个Looper对象对应一个消息队列(MessageQueue)。如果在创建Handler时不指定与其绑定的Looper对象,系统默认会将当前线程的Looper绑定到该Handler上。在主线程中,可以直接使用new Handler()创建Handler对象,其将自动与主线程的Looper对象绑定;在非主线程中直接这样创建Handler则会报错,因为Android系统默认情况下非主线程中没有开启Looper,而Handler对象必须绑定Looper对象。这种情况下,需先在该线程中手动开启Looper(Looper.prepare()--&Looper.loop()),然后将其绑定到Handler对象上;或者通过Looper.getMainLooper(),获得主线程的Looper,将其绑定到此Handler对象上。Handler发送的消息都会加入到Looper的MessageQueue中。一说Handler包含两个队列:线程队列和消息队列;使用Handler.post()可以将线程对象加入到线程队列中;使用Handler.sendMessage()可以将消息对象加入到消息队列中。通过源码分析证实,Handler只有一个消息队列,即MessageQueue。通过post()传进去的线程对象将会被封装成消息对象后传入MessageQueue。使用post()将线程对象放到消息队列中后,当Looper轮询到该线程执行时,实际上并不会单独开启一个新线程,而仍然在当前Looper绑定的线程中执行,Handler只是调用了该线程对象的run()而已。如,在子线程中定义了更新UI的指令,若直接开启将该线程执行,则会报错;而通过post()将其加入到主线程的Looper中并执行,就可以实现UI的更新。使用sendMessage()将消息对象加入到消息队列后,当Looper轮询到该消息时,就会调用Handler的handleMessage()来对其进行处理。再以更新UI为例,使用这种方法的话,就先将主线程的Looper绑定在Handler对象上,重载handleMessage()来处理UI更新,然后向其发送消息就可以了。
阅读(...) 评论()Android在子线程用handler发送的消息,主线程是怎么loop到的? - 知乎5被浏览1384分享邀请回答11 条评论分享收藏感谢收起1添加评论分享收藏感谢收起查看更多回答Android面试:主线程中的Looper.loop()一直无限循环为什么不会造成ANR? - 简书
Android面试:主线程中的Looper.loop()一直无限循环为什么不会造成ANR?
正如我们所知,在android中如果主线程中进行耗时操作会引发ANR(Application Not Responding)异常。
造成ANR的原因一般有两种:
当前的事件没有机会得到处理(即主线程正在处理前一个事件,没有及时的完成或者looper被某种原因阻塞住了)
当前的事件正在处理,但没有及时完成
为了避免ANR异常,android使用了Handler消息处理机制。让耗时操作在子线程运行。
因此产生了一个问题,主线程中的Looper.loop()一直无限循环检测消息队列中是否有新消息为什么不会造成ANR?
本人面试网易的时候就被问到了T_T
ActivityThread.java
是主线程入口的类,这里你可以看到写Java程序中司空见惯的main方法,而main方法正是整个Java程序的入口。
ActivityThread源码
public static final void main(String[] args) {
//创建Looper和MessageQueue
Looper.prepareMainLooper();
//轮询器开始轮询
Looper.loop();
Looper.loop()方法
while (true) {
//取出消息队列的消息,可能会阻塞
Message msg = queue.next(); // might block
//解析消息,分发消息
msg.target.dispatchMessage(msg);
显而易见的,如果main方法中没有looper进行循环,那么主线程一运行完毕就会退出。这还玩个蛋啊!
总结:ActivityThread的main方法主要就是做消息循环,一旦退出消息循环,那么你的应用也就退出了。
我们知道了消息循环的必要性,那为什么这个死循环不会造成ANR异常呢?
因为Android 的是由事件驱动的,looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者说Activity的生命周期都是运行在 Looper.loop() 的控制之下,如果它停止了,应用也就停止了。只能是某一个消息或者说对消息的处理阻塞了 Looper.loop(),而不是 Looper.loop() 阻塞它。
也就说我们的代码其实就是在这个循环里面去执行的,当然不会阻塞了。
handleMessage方法部分源码
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, "&&& handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.
r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
case RELAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
ActivityClientRecord r = (ActivityClientRecord) msg.
handleRelaunchActivity(r);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
case PAUSE_ACTIVITY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
handlePauseActivity((IBinder) msg.obj, false, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 2) != 0);
maybeSnapshot();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
case PAUSE_ACTIVITY_FINISHING:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
handlePauseActivity((IBinder) msg.obj, true, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 1) != 0);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
...........
可以看见Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时则采用相应措施。
如果某个消息处理时间过长,比如你在onCreate(),onResume()里面处理耗时操作,那么下一次的消息比如用户的点击事件不能处理了,整个循环就会产生卡顿,时间一长就成了ANR。
让我们再看一遍造成ANR的原因,你可能就懂了。
造成ANR的原因一般有两种:
当前的事件没有机会得到处理(即主线程正在处理前一个事件,没有及时的完成或者looper被某种原因阻塞住了)
当前的事件正在处理,但没有及时完成
而且主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。
总结:Looer.loop()方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理事件就不会产生ANR异常。
为认识世界,认识自己而活着!Android是基于Java的,所以也分主线程,子线程!&主线程:实现业务逻辑、UI绘制更新、各子线程串连,类似于将军;&子线程:完成耗时(联网取数据、SD卡数据加载、后台长时间运行)操作,类似于小兵;&一、子线程向主线程发消息(Thread+handler):&1、主线程中定义Handler:&
Handler mHandler = new Handler(){
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
//do something,refresh UI;
2、子线程处理完耗时操作之后发消息给主线程,更新UI:&
mHandler.sendEmptyMessage(0);
这样在子线程与主线程任务分工的条件下完成了消息交互;&二、主线程向子线程发送消息(Thread+handler):&主线程碰到耗时操作要子线程完成,此时发通知给子线程,操作步骤如下:&1、子线程中定义Handler,Handler定义在哪个线程中,就跟那个线程绑定,在线程中绑定Handler需要调用Looper.prepare();方法,主线程中不调用是因为主线程默认帮你调用了;&
public class LoopThread implements Runnable {
public Handler mHandler = null;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
String result = NetUtil.getJsonContent("北京");
//完成了获取北京天气的操作;
Log.i("test", "handler"+result);
Looper.loop();
其中Looper.prepare();和Looper.loop();维护了一个消息队列,等待消息注入并在子线程中执行;&2、主线程中这样调用:&
lThread.mHandler.sendEmptyMessage(0);
主线程向子线程发消息,让子线程执行指定的操作,在Android中还有一种方法,即:HandlerThread,看下面的例子:&
HandlerThread handlerThread = new HandlerThread("jerome");
handlerThread.start();
* 这里要将HandlerThread创建的looper传递给threadHandler,即完成绑定;
threadHandler = new Handler(handlerThread.getLooper()) {
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
//这儿可以做耗时的操作;
Log.i("jerome", "hello,I am sub thread");
阅读(...) 评论()

我要回帖

更多关于 handler循环发送消息 的文章

 

随机推荐