我理解的Hanlder--android消息传递机制

  每一个学习Android的同学都会觉得Handler是一个神奇的东西,我也一样,开始我以为我懂了Handler的机制,后来发现自己是一知半解,昨天想想,我能否自己实现一个Handler,让子线程与ActivityUI线程通信,如果能够自己实现一个Handler,那必然是对Handler的消息传递机制理解渗透了。

一、引入

  Android的UI是单线程控制的,实际上,成功的UI框架都是基于单线程的,多线程的UI框架往往因为解决并发和死锁的艰难而胎死腹中。只有UI线程能控制界面控件,但我们总是希望子线程的数据能更新到UI上,于是就有了Handler,它帮助我们实现了非UI线程向UI线程之间的通信,Handler的使用非常简单。

  一个简单的示例,让一个TextView动态显示秒数:  


 1 public class MainActivity extends Activity {
2 private static final int UPDATE_TAG = 0x01;
3 private TextView textView;
4
5 @Override
6 protected void onCreate(Bundle savedInstanceState) {
7 super.onCreate(savedInstanceState);
8 setContentView(R.layout.activity_main);
9 textView = (TextView) findViewById(R.id.textview);
10 // 启动一个线程
11 new Thread(new Runnable() {
12
13 @Override
14 public void run() {
15 int count = 1;
16 while (true) {
17 // 发送计数值到handler
18 mHandler.obtainMessage(UPDATE_TAG, String.valueOf(count))
19 .sendToTarget();
20 try {
21 TimeUnit.SECONDS.sleep(1);
22 } catch (InterruptedException e) {
23 e.printStackTrace();
24 }
25 }
26 }
27
28 }).start();
29 }
30
31 public Handler mHandler = new Handler() {
32 public void handleMessage(Message msg) {
33 switch (msg.what) {
34 case UPDATE_TAG:
35 // 显示计数值
36 textView.setText((String)msg.obj);
37 break;
38 }
39 }
40 };
41 }

  通过开启一个线程,线程每隔一秒发送一次计数给handler,让handler来更新TextView的内容,新开的子线程里面不能直接操控TextView,因为这违反了UI线程了单线程控制规范,如果你真要这么做,一运行就会得到CalledFromWrongThreadException异常。

二、这不就是一个观察着模式么?

  最初自己理解的Handler就是一个典型的观察着模式。sendToTarget()这个方法一定以某种方式调用了Handler的 handleMessage(Message
msg)方法,从而完成了我们指定的任务,于是我写了下面这样一个Handler和Message


/***
* 模拟Android的Handler,申明一个供子类覆盖的方法和多个生成消息的方法
*/
public class Handler {

/***
* 子类覆盖本方法,实现想要的操作
*
* @param msg
*/
protected void handleMessage(Message msg) {

}

public Message obtainMessage(int what) {
Message msg = new Message(this, what, -1, -1, null);
return msg;
}

public Message obtainMessage(int what, Object obj) {
Message msg = new Message(this, what, -1, -1, obj);
return msg;
}

public Message obtainMessage(int what, int arg1, int arg2, Object obj) {
Message msg = new Message(this, what, arg1, arg2, obj);
return msg;
}
}

  下来就是Message类:  


/**
*
* 模拟Android的Message,这里为方便演示,只写了一个构造方法。构造方法中绑定了一个Handler
*/
public class Message {
private final Handler target;
public final int what;
public final int arg1;
public final int arg2;
public final Object obj;

public Message(Handler target, int what, int arg1, int arg2, Object obj) {
this.target = target;
this.arg1 = arg1;
this.arg2 = arg2;
this.what = what;
this.obj = obj;
}
/***
* 利用OOP的多态,调用子类的覆盖方法
*/
public void sendToTarget() {
target.handleMessage(this);
}
}

  测试代码如下:  

public class HandlerTest {

public static void main(String[] args) {
new Thread(new MyTask(Myhandler)).start();
}

static class MyTask implements Runnable {
private Handler handler;

public MyTask(Handler handler) {
this.handler = handler;
}

@Override
public void run() {
while (true) {
handler.obtainMessage(0, this.toString()).sendToTarget();
}
}

}

static Handler Myhandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
System.out.println("I am zero " + (String) msg.obj);
break;
}
}
};

}

  真想说上面的代码体现了一个简单的观察着模式,上面的测试代码能运行。但是,但是这样的Handler能代替Android的Handler在Activity中使用吗,用上面的Handler去替代Android的handler,结果得到是CalledFromWrongThreadException。

  这很显然的,上面的Handler可以只模仿到Android
Handler的形,没有模仿到其神,它只是通过一个回调模仿了Handler的工作外表,利用多态的伎俩去调用子类的handleMessage方法,想法是好的,但这最终相当于让子线程直接调用了handleMessage,从而让子线程对界面控件进行了操控,违背了UI界面的单线程控制原则,必须会报CalledFromWrongThreadException的异常。

三、子线程是如何把消息传递给UI线程

  前面仿造的Handler让子线程通过Message操纵了UI控件,因此会报错,那Android是如何将子进程的消息发送给UI进程。实际上Handler的工作离不开以下几个组件:

  • Message: Handler接收和处理的对象

  • MessageQueue:消息队列,以FIFO的方式管理Message对象

  • Looper:管理MessageQueue的对象,不断地从MessageQueue中取消息,并发送该消息对应的Handler进行处理。每个线程只有一个Looper,UI线程在创建时就已经默认创建了一个Looper,因此在Activity中可以直接创建Handler即可发送和处理消息;如果在子线程中创建Handler,必须在创建前调用Looper.prepare();这样Looper在才能做好准备,并为当前线程创建MessageQueue,以便接受Handler的Message。可以看一下Looper.prepare()的源码


    public static void prepare() {
    prepare(true);
    }

    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);
    mRun = true;
    mThread = Thread.currentThread();
    }

    在子进程创建好Handler后,调用Looper.loop()处理消息。Looper.loop()是一个死循环,如果不执行quit(),其后的代码将不会执行。现在看一个例子,我们通过点击Button给子线程的Hanlder传递消息。


    public class MainActivity extends Activity {
    private Mythread thread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    thread = new Mythread();
    thread .start();
      
    }

    class Mythread extends Thread {
    public Handler handler;

    @Override
    public void run() {
    Looper.prepare();
    // 在子线程中定义Handler
    handler = new Handler() {
    public void handleMessage(Message msg) {
    Log.i("handleMessage", "子线程handler.handleMessage(...)");
    // 接到消息让Looper退出死循环
    handler.getLooper().quit();
    }
    };
    Toast.makeText(MainActivity.this, "before Looper.loop()",
    Toast.LENGTH_LONG).show();
    // 死循环处理消息
    Looper.loop();
    Toast.makeText(MainActivity.this, "after Looper.loop()",
    Toast.LENGTH_LONG).show();

    }
    };

    public void onClickButton(View v) {
    thread.handler.obtainMessage().sendToTarget();
    }
    }


  可以看到线程启动后,会Toast"before
Looper.loop()",但不会立刻Toast"after Looper.loop()",只有当我们按下Button后才会显示"after Looper.loop()",因为按下Button,会给子线程的Handler发送一个空消息,而我们定义Handler对消息的处理是调用looper的quit(),从而结束Looper.loop()。注意quit()并不是简单的终止Looper.loop(),而是设置MessageQueue的一个标志,让MessageQueue的next()方法返回null,从而结束Looper.loop(),详情请看MessageQueue源码,这里我们来看看loop()的源码:


  • public static void loop() {
    ...
    for (;;) {
    Message msg = queue.next(); // might block
    if (msg == null) {
    // No message indicates that the message queue is quitting.
    return;
    }
    ...

    msg.target.dispatchMessage(msg);
    ...
    msg.recycle();
    }
    }


  另外跟踪sendToTarget()发现最终调用的方法是MessageQueue的enqueueMessage(Message
msg, long
when),这个方法只是把message放入队列中,并没有调用消息消息方法,消息的分发处理是在Looper的loop()中进行的。

四、是什么东西再帮UI线程跑Looper.loop()

  现在理清了前面的问题又来了新的问题,是谁在帮UI线程跑Looper.loop():

  • 如果是其他线程帮它Looper.loop(),那么这明显违背了UI线程的单线程控制规范;

  • 如果是UI线程自己在跑,那它的死循环在那里跑着,怎么用工夫去执行onCreate(),onResume()...以及我们绑定的各种listener。

  如果既要满足UI线程的单线程控制规范,又要让死循环Looper.loop()跑着不漏掉一个消息,还要响应onCreate()、listener,只有一种办法,那就是对界面的所有的操作都是由loop来驱动分发的,onCreate(),onResume()...各种listener都是Looper.loop()来调用。

  实际上Android就是这么干的,要理清onCreate等各种方法绑定的Message的过程不容易,但是我们可以很简单地验证我们的上面的假设,那就是在onCreate(),onResume(),以及listener的响应方法里面引发异常,然后看一下栈信息,我们在onCreate发放中加一句int
a = 1/0,看看异常的栈信息:

  当发现神奇的Handler原来就是靠一个死循环来维持的,一切就变得豁然开朗了。

  如果再深入,理解Activity的启动机制,我们最终找到UI线程的Looper.loop()是在ActivityThread中的main方法中执行的,让我们再看看源码:  


public final class ActivityThread {

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

// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);

Environment.initForCurrentUser();

// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());

Security.addProvider(new AndroidKeyStoreProvider());

Process.setArgV0("<pre-initialized>");
//为主线程开启Looper
Looper.prepareMainLooper();

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"));
}
//UI线程的死循环在这里
Looper.loop();

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

五、真的能自己写一个Handler供Activity调用吗

  现在回到我最初的问题,我们可以自己写一个Handler供Activity调用吗?答案已经很明显了,如果你足够强,你真实现了一个功能一样的Handler,那么恭喜你,你是在重写了整个Activity组件。

  

  以上是我对Handler的理解,希望对你有所帮助,如果有总结得不恰当的地方,欢迎指正。

感谢阅读,转载请注明出处:http://www.cnblogs.com/fengfenggirl/

时间: 2024-08-25 07:44:17

我理解的Hanlder--android消息传递机制的相关文章

Android消息传递机制

背景需求 在Android中,当遇到子线程需要刷新UI时,最常的做法就是handler,当然还有其他方便的方法如Android给我们提供的runOnUiThread(runnable)方法,但归根结底都是使用handler来刷新UI的. Android消息传递原理 简单的讲:handler发送(post或send)一条消息:MessageQueue(队,实际上是一个用单链表实现的队列)接受并存储该消息:looper无限循环的从MessageQueue中取出一条消息,例如msg,然后调用msg.t

android 消息传递机制EventBus的深入探究

以前,对于activity和fragment之间的通信可以使用接口的方式,定义起来比较麻烦费事,偶然间发现可以使用EventBus,发现很好用,查看了一下官方说明:EventBus是针一款对Android的发布/订阅事件总线.它可以让我们很轻松的实现在Android各个组件之间传递消息,并且代码的可读性更好,耦合度更低.但是在用的过程中总会出现一些问题,下面就将出现的问题详细记录一下,顺便具体学习EventBus(GreenRobot)这个第三方开源库,了解它内部的实现原理,以至于出了问题可以快

android线程消息传递机制——Looper,Handler,Message

android线程消息传递机制——Looper,Handler,Message 在引入这些概念之前,我们先了解一下引入这些机制的背景. 出于性能优化的考虑,Android的UI操作并不是线程安全的(如果你不懂什么是线程安全,可以阅读一下<一起探究多进程与多线程>里的数据安全与可重入),这意味着如果有多个线程同时操作某个UI组件,可能导致线程安全问题.为了解决这个问题,Android制定了一条简单的规则:只允许UI线程修改Activity里的UI组件.这个UI线程也通常被我们称为主线程. 在此引

解析Android的 消息传递机制Handler

1. 什么是Handler: Handler 网络释义"操纵者,管理者的"意思,在Android里面用于管理多线程对UI的操作: 2. 为什么会出现Handler: 在Android的设计机制里面,只允许主线程(一个程序第一次启动时所移动的线程,因为此线程主要是完成对UI相关事件的处理,所以也称UI线程) 对UI进行修改等操作,这是一种规则的简化,之所以这样简化是因为Android的UI操作时线程不安全的,为了避免多个线程同时操作UI造成线程安全 问题,才出现了这个简化的规则. 由此以

安卓开发_深入理解Handler消息传递机制

一.概述 因为子线程的run()方法无法修改UI线程(主线程)的UI界面,所以Android引入了Handler消息传递机制,实现在新创建的线程中操作UI界面 二.消息类(Message) 消息类是存放在MessageQueue中的,而一个MessageQueue中可以包含多个Message对象 每一个Message对象可以通过Message.obtain()或者Handler.obtainMessage()方法获得 一个Message具有的属性: 属性 类型 介绍 arg1 int 存放整型数

Android Handler消息传递机制详解

1.为什么要用Handler 出于性能优化的考虑,Android UI操作并不是线程安全,如果有多个线程并发操作UI组件,可能导致线程安全问题.可以设想下,如果在一个Activity中有多个线程去更新UI,并且都没有加锁机制,可能会导致什么问题? 界面混乱,如果加锁的话可以避免该问题但又会导致性能下降.因此,Android规定只允许UI线程修改Activity的UI组件.当程序第一次启动时,Android会同时启动一条主线程(Main Thread),主线程主要负责处理与UI相关的事件,比如用户

android触碰消息传递机制

前阵子要的工作是给桌面(Launcher启动器,其实也是一个activity)添加一个触摸特效(一个View),而这个特效是每次触碰都会有,不管你在桌面上做什么操作都会显示特效!之前一直摸索着不知道如何入手,后来慢慢的实验之后才知道有个android触碰消息传递机制.自己摸索的确很慢,要是早点知道这个机制那将会事半功倍. 用户的每次触碰(onClick,onLongClick,onScroll,etc.)都是由一个ACTION_DOWN+n个ACTION_MOVE+1个ACTION_UP组成的,

Android异步消息传递机制源码分析&amp;&amp;相关知识常被问的面试题

1.Android异步消息传递机制有以下两个方式:(异步消息传递来解决线程通信问题) handler 和 AsyncTask 2.handler官方解释的用途: 1).定时任务:通过handler.postDelay(Runnable r, time)来在指定时间执行msg. 2).线程间通信:在执行较为耗时操作的时候,在子线程中执行耗时任务,然后handler(主线程的)把执行的结果通过sendmessage的方式发送给UI线程去执行用于更新UI. 3.handler源码分析 一.在Activ

Android笔记二十五.Android事件Handler消息传递机制

因为Android平台不同意Activity新启动的线程訪问该Activity里的界面控件.这样就会导致新启动的线程无法动态改变界面控件的属性值.但在实际Android应用开发中,尤其是涉及动画的游戏开发中,须要让新启动的线程周期性地改变界面控件的属性值,这就须要借助Handler的消息传递机制实现. 一.Handler类简单介绍 1.功能 Handler类主要有两个作用 (1)在新启动的线程中发送消息; (2)在主线程中获取消息.处理消息.即当须要界面发生变化的时候.在子线程中调用Handle