最经面试中,技术面试中有一个是Handler的消息机制,细细想想,我经常用到的Handler无非是在主线程(或者说Activity)新建一个Handler对象,另外一个Thread是异步加载数据,同时当他加载完数据后就send到主线程中的那个Handler对象,接着Handler来处理,刚才发送的一些消息。

  1. public class HandlerTestActivity extends Activity {
  2. private TextView tv;
  3. private static final int UPDATE = 0;
  4. private Handler handler = new Handler() {
  5.  
  6. @Override
  7. public void handleMessage(Message msg) {
  8. // TODO 接收消息并且去更新UI线程上的控件内容
  9. if (msg.what == UPDATE) {
  10. // Bundle b = msg.getData();
  11. // tv.setText(b.getString("num"));
  12. tv.setText(String.valueOf(msg.obj));
  13. }
  14. super.handleMessage(msg);
  15. }
  16. };
  17.  
  18. /** Called when the activity is first created. */
  19. @Override
  20. public void onCreate(Bundle savedInstanceState) {
  21. super.onCreate(savedInstanceState);
  22. setContentView(R.layout.main);
  23. tv = (TextView) findViewById(R.id.tv);
  24.  
  25. new Thread() {
  26. @Override
  27. public void run() {
  28. // TODO 子线程中通过handler发送消息给handler接收,由handler去更新TextView的值
  29. try {
  30. for (int i = 0; i < 100; i++) {
  31. Thread.sleep(500);
  32. Message msg = new Message();
  33. msg.what = UPDATE;
  34. // Bundle b = new Bundle();
  35. // b.putString("num", "更新后的值:" + i);
  36. // msg.setData(b);
  37. msg.obj = "更新后的值:" + i;
  38. handler.sendMessage(msg);
  39. }
  40. } catch (InterruptedException e) {
  41. e.printStackTrace();
  42. }
  43. }
  44. }.start();
  45. }
  46.  
  47. }

如图所示,每个Thread都一个Looper,这个Looper类是用于管理其中的消息队列(MessageQueue)的,那Handler是干嘛的呢,他是用来传递消息队列的。

那下面就分析Looper、Hanlder方法吧。

Looper方法是用来处理消息队列的,注意了,它和线程是绑定的。

要是想在子线程中获取一个Looper该怎么做呢:

  1. Looper.prepare();
  2. Looper looper = Looper.myLooper();

那么这些都干了哪些工作呢???

来看下它的源码吧:

Looper:

  1. ……
  1. //准备Looper相关事宜
  2. public static void prepare() {
  3. //只能有一个对象哦
  4. if (sThreadLocal.get() != null) {
  5. throw new RuntimeException("Only one Looper may be created per thread");
  6. }
  7. sThreadLocal.set(new Looper());
  8. }
  9. //构造函数
  10. /*新建一个消息队列
  11. * 把当前运行的线程作为运行线程
  12. */
  13.  
  14. private Looper() {
  15. mQueue = new MessageQueue();
  16. mRun = true;
  17. mThread = Thread.currentThread();
  18. }
  1.  

public static final Looper myLooper() {

//这个方法是从当前线程的ThreadLocal中拿出设置的looper

return (Looper)sThreadLocal.get();

}

  1. /**
  2. * Run the message queue in this thread. Be sure to call
  3. * {@link #quit()} to end the loop.
  4. */
  5. public static void loop() {
  6. Looper me = myLooper();
  7. if (me == null) {
  8. throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  9. }
  10. MessageQueue queue = me.mQueue;
  11.  
  12. // Make sure the identity of this thread is that of the local process,
  13. // and keep track of what that identity token actually is.
  14. Binder.clearCallingIdentity();
  15. final long ident = Binder.clearCallingIdentity();
  16.  
  17. while (true) {
  18. Message msg = queue.next(); // might block
  19. if (msg != null) {
  20. if (msg.target == null) {
  21. // No target is a magic identifier for the quit message.
  22. return;
  23. }
  24.  
  25. long wallStart = 0;
  26. long threadStart = 0;
  27.  
  28. // This must be in a local variable, in case a UI event sets the logger
  29. Printer logging = me.mLogging;
  30. if (logging != null) {
  31. logging.println(">>>>> Dispatching to " + msg.target + " " +
  32. msg.callback + ": " + msg.what);
  33. wallStart = SystemClock.currentTimeMicro();
  34. threadStart = SystemClock.currentThreadTimeMicro();
  35. }
  36.  
  37. msg.target.dispatchMessage(msg);
  38.  
  39. if (logging != null) {
  40. long wallTime = SystemClock.currentTimeMicro() - wallStart;
  41. long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
  42.  
  43. logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
  44. if (logging instanceof Profiler) {
  45. ((Profiler) logging).profile(msg, wallStart, wallTime,
  46. threadStart, threadTime);
  47. }
  48. }
  49.  
  50. // Make sure that during the course of dispatching the
  51. // identity of the thread wasn't corrupted.
  52. final long newIdent = Binder.clearCallingIdentity();
  53. if (ident != newIdent) {
  54. Log.wtf(TAG, "Thread identity changed from 0x"
  55. + Long.toHexString(ident) + " to 0x"
  56. + Long.toHexString(newIdent) + " while dispatching to "
  57. + msg.target.getClass().getName() + " "
  58. + msg.callback + " what=" + msg.what);
  59. }
  60.  
  61. msg.recycle();
  62. }
  63. }
  64. }

下面就来看下Handler:

  1. public Handler() {
  2. if (FIND_POTENTIAL_LEAKS) {
  3. final Class<? extends Handler> klass = getClass();
  4. if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
  5. (klass.getModifiers() & Modifier.STATIC) == 0) {
  6. Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
  7. klass.getCanonicalName());
  8. }
  9. }
  10. //先获得一个Looper对象,这个要是在子线程里,是需要先prepare()的
  11.  
  12. mLooper = Looper.myLooper();
  13. if (mLooper == null) {
  14. throw new RuntimeException(
  15. "Can't create handler inside thread that has not called Looper.prepare()");
  16. }
  17. mQueue = mLooper.mQueue;
  18. mCallback = null;
  19. }
  20. /**
  21. * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
  22. * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
  23. * If you don't want that facility, just call Message.obtain() instead.
  24. 会从消息池里面取得消息队列
  25. */
  26. public final Message obtainMessage()
  27. {
  28. return Message.obtain(this);
  29. }

那我现在写个小例子,是在子线程实现的消息的传递。

  1. @Override
  2. public void onClick(View v) {
  3. int id = v.getId();
  4. if (id == R.id.btn1) {
  5. new Thread() {
  6.  
  7. public void run() {
  8.  
  9. Log.i("log", "run");
  10.  
  11. Looper.prepare();
  12. // Looper looper = Looper.myLooper();
  13. Toast.makeText(MainActivity.this, "toast", 1).show();
  14. Handler h = new Handler() {
  15.  
  16. @Override
  17. public void handleMessage(Message msg) {
  18. // TODO Auto-generated method stub
  19. super.handleMessage(msg);
  20. if (msg != null) {
  21. String strMsg = (String) msg.obj;
  22. System.out.println(strMsg);
  23. }
  24.  
  25. }
  26.  
  27. };
  28. //获取到Handler对象的消息
  29. Message msg = h.obtainMessage();
  30. msg.obj = "add";
  31. msg.sendToTarget();
  32.  
  33. Looper.loop();// 进入loop中的循环,查看消息队列
  34.  
  35. };
  36.  
  37. }.start();
  38.  
  39. }
  40. }

不知你是否理解,这个小Demo中,我们需要注意:

1  子线程也是可以有Handler的,其实Handler只是从当前的线程中获取到Looper来监听和操作MessageQueue的。

2 子线程需要先prepare()才能获取到Looper的,是因为在子线程只是一个普通的线程,其ThreadLoacl中没有设置过Looper,所以会抛出异常,而在Looper的prepare()方法中sThreadLocal.set(new Looper())是设置了Looper的。

而对于主线程里面的Handler,是没有以上的麻烦的,因为这个在Activity创建时,就已经初始化了Looper等其他工作了。

另外可以看下参考文章中的子线程中Toast

参考文章:

Android之Handler用法总结

Android Handler机制

子线程中Toast

[Android]Handler的消息机制的更多相关文章

  1. android handler传递消息机制

    当工作线程给主线程发送消息时,因为主线程是有looper的,所以不需要初始化looper,注意给谁发消息就关联谁的handler,此时用的就是主线程的handler handler会把消息发送到Mes ...

  2. 浅析Android中的消息机制(转)

    原博客地址:http://blog.csdn.net/liuhe688/article/details/6407225 在分析Android消息机制之前,我们先来看一段代码: public class ...

  3. 浅析Android中的消息机制(转)

    在分析Android消息机制之前,我们先来看一段代码: public class MainActivity extends Activity implements View.OnClickListen ...

  4. 浅析Android中的消息机制-解决:Only the original thread that created a view hierarchy can touch its views.

    在分析Android消息机制之前,我们先来看一段代码: public class MainActivity extends Activity implements View.OnClickListen ...

  5. 浅析Android中的消息机制

    在分析Android消息机制之前,我们先来看一段代码: public class MainActivity extends Activity implements View.OnClickListen ...

  6. android Handler及消息处理机制的简单介绍

    学习android线程时,直接在UI线程中使用子线程来更新TextView显示的内容,会有如下错误:android.view.ViewRoot$CalledFromWrongThreadExcepti ...

  7. 重温Android中的消息机制

    引入: 提到Android中的消息机制,大家应该都不陌生,我们在开发中不可避免的要和它打交道.从我们开发的角度来看,Handler是Android消息机制的上层接口.我们在平时的开发中只需要和Hand ...

  8. 谈谈对Android中的消息机制的理解

    Android中的消息机制主要由Handler.MessageQueue.Looper三个类组成,他们的主要作用是 Handler负责发送.处理Message MessageQueue负责维护Mess ...

  9. Android中的消息机制

    在分析Android消息机制之前.我们先来看一段代码: public class MainActivity extends Activity implements View.OnClickListen ...

随机推荐

  1. Codeforces 711 D. Directed Roads (DFS判环)

    题目链接:http://codeforces.com/problemset/problem/711/D 给你一个n个节点n条边的有向图,可以把一条边反向,现在问有多少种方式可以使这个图没有环. 每个连 ...

  2. test是否被执行?

    procedure TForm2.Button1Click(Sender: TObject);  function test(value:boolean):boolean;  begin    res ...

  3. websocket的php测试demo

    <?php class WS { var $master; var $sockets = array(); var $debug = false; var $handshake = false; ...

  4. Windows x86 x64使用SetThreadContext注入shellcode的方式加载DLL

    一.前言 注入DLL的方式有很多,在R3就有远程线程CreateRemoteThread.SetWindowsHookEx.QueueUserApc.SetThreadContext 在R0可以使用a ...

  5. Oracle中wm_concat()函数的使用

    Oracle中wm_concat()函数的使用 wm_concat()函数是oracle行列转换函数,该函数可以把列值以‘,’分割开来,并显示成一行. 1.原数据: 2.把结果分组以‘|’分隔,以一行 ...

  6. C# WinForm开发系列 - RDLC

    http://www.cnblogs.com/peterzb/archive/2009/07/08/1519489.html http://jingyan.baidu.com/article/ab69 ...

  7. strlen() 和 sizeof() 在字符串中的使用

    #include <string.h> int _tmain(int argc, _TCHAR* argv[]) { char *pMyChar = "I like coding ...

  8. yum命令的使用

    yum源的配置,请参考此文:RedHatLinux 本地yum源的配置.本文讲解如何使用yum命令. [root@serv01 ~]# yum --help Loaded plugins: produ ...

  9. Tomcat创建虚拟目录和程序热部署

    虚拟目录的设置 方法一:在${tomcat安装目录}/conf/Catalina/localhost目录下添加与web应用同名的xml配置文件,这里站点名称为test为例子. test.xml内容:& ...

  10. Educational Codeforces Round 4 B. HDD is Outdated Technology 暴力

    B. HDD is Outdated Technology 题目连接: http://www.codeforces.com/contest/612/problem/B Description HDD ...