Android消息机制Handler解析(源码+Demo)

转载请注明出处: https://blog.csdn.net/chen_lian_/article/details/51031331

新建了一个qq群 482543750,欢迎一起学习Android的小伙伴加入。

提供各种Android学习资料,面试资料,Android简历模板。


Handler是开发人员在面试过程中最常见的问题之一了,这篇文章将较为全面地对Handler进行解读,包括源码层,以及使用方法。

如果看完文章有疑问,欢迎在评论中一起探讨

基本内容包括:


看完文章之后,可以用这个图片进行复习。


一、什么是Handler
Handler是Android提供用来更新UI的一套机制,也是一套消息处理机制,可以用它来发送消息,也可以用它来接收消息。

二、为什么使用Handler
Android在设计之时,就封装了一套消息的创建、传递、处理机制,若不遵循这样的处理机制,就没办法更新UI信息,并且会抛出异常

三、Handler用法
1、postdelayed()延时发送执行子线程

文字轮询Demo(实现每隔一秒钟更新一次Textview的功能)
  1. public class MainActivity extends AppCompatActivity {  
  2.   
  3.   
  4.     private TextView mTextView;  
  5.   
  6.     private Handler mHandler = new Handler(){  
  7.         @Override  
  8.         public void handleMessage(Message msg) {  
  9.             super.handleMessage(msg);  
  10.         }  
  11.     };  
  12.   
  13.     private String[] str = new String[]{“傲慢”,“偏见”,“僵尸”};  
  14.     private int index = 0;  
  15.     MyRunnable myRunnable = new MyRunnable();  
  16.   
  17.     private class MyRunnable implements Runnable{  
  18.   
  19.         @Override  
  20.         public void run() {  
  21.             index = index % 3;  
  22.             mTextView.setText(str[index]);  
  23.             index ++;  
  24.             mHandler.postDelayed(myRunnable,1000);  
  25.         }  
  26.     }  
  27.   
  28.     @Override  
  29.     protected void onCreate(Bundle savedInstanceState) {  
  30.         super.onCreate(savedInstanceState);  
  31.         setContentView(R.layout.activity_main);  
  32.   
  33.         mTextView = (TextView) findViewById(R.id.tv);  
  34.   
  35.   
  36.         mHandler.postDelayed(myRunnable,1000);  
  37.     }  
  38. }  
2、sendMessage()回调handleMessage()传递消息
     Demo:在子线程中得到信息,发送至主线程,更新textview的内容

  1. public class MainActivity extends AppCompatActivity {  
  2.   
  3.   
  4.     private TextView mTextView;  
  5.   
  6.     Handler mHandler = new Handler(){  
  7.         @Override  
  8.         public void handleMessage(Message msg) {  
  9.             mTextView.setText(msg.obj+”““arg1=”+msg.arg1 + “ arg2=”+msg.arg2);  
  10.             super.handleMessage(msg);  
  11.         }  
  12.     };  
  13.   
  14.     private  class Person{  
  15.         String name;  
  16.         int age;  
  17.   
  18.         @Override  
  19.         public String toString() {  
  20.             return “name=”+name+“ age=”+age;  
  21.         }  
  22.     }  
  23.   
  24.     @Override  
  25.     protected void onCreate(Bundle savedInstanceState) {  
  26.         super.onCreate(savedInstanceState);  
  27.         setContentView(R.layout.activity_main);  
  28.   
  29.         mTextView = (TextView) findViewById(R.id.tv);  
  30.   
  31.         new Thread(){  
  32.             @Override  
  33.             public void run() {  
  34.                 Message msg = new Message();  
  35.                 msg.arg1 = 1;  
  36.                 msg.arg2 = 2;  
  37.                 Person person = new Person();  
  38.                 person.name = ”pig”;  
  39.                 person.age = 10;  
  40.                 msg.obj = person;  
  41.                 mHandler.sendMessage(msg);  
  42.             }  
  43.         }.start();  
  44.   
  45.     }  
  46. }  

3、sendToTarget()传递消息
     与第二种用法原理一致
  1. public class MainActivity extends AppCompatActivity {  
  2.   
  3.   
  4.     private TextView mTextView;  
  5.   
  6.     Handler mHandler = new Handler(){  
  7.         @Override  
  8.         public void handleMessage(Message msg) {  
  9.             mTextView.setText(msg.obj+”““arg1=”+msg.arg1 + “ arg2=”+msg.arg2);  
  10.             super.handleMessage(msg);  
  11.         }  
  12.     };  
  13.   
  14.     private  class Person{  
  15.         String name;  
  16.         int age;  
  17.   
  18.         @Override  
  19.         public String toString() {  
  20.             return “name=”+name+“ age=”+age;  
  21.         }  
  22.     }  
  23.   
  24.     @Override  
  25.     protected void onCreate(Bundle savedInstanceState) {  
  26.         super.onCreate(savedInstanceState);  
  27.         setContentView(R.layout.activity_main);  
  28.   
  29.         mTextView = (TextView) findViewById(R.id.tv);  
  30.   
  31.         new Thread(){  
  32.             @Override  
  33.             public void run() {  
  34.                 Message msg = mHandler.obtainMessage();//同样可以获取Message对象  
  35.                 msg.arg1 = 1;  
  36.                 msg.arg2 = 2;  
  37.                 Person person = new Person();  
  38.                 person.name = ”pig”;  
  39.                 person.age = 10;  
  40.                 msg.obj = person;  
  41.                 msg.sendToTarget();  
  42.             }  
  43.         }.start();  
  44.   
  45.     }  
  46. }  

4、使用CallBack截获Handler的消息
  1. public class MainActivity extends AppCompatActivity {  
  2.   
  3.   
  4.     private TextView mTextView;  
  5.   
  6.     Handler mHandler = new Handler(new Handler.Callback() {  
  7.         //传入CallBack对象,对于重载的返回值为bollean的handleMessage()  
  8.         //返回值为false,将先执行这个方法,再执行返回值为void的handleMessage()方法  
  9.         //返回值为true,只执行这个方法  
  10.         @Override  
  11.         public boolean handleMessage(Message msg) {  
  12.             Toast.makeText(MainActivity.this“截获消息”, Toast.LENGTH_SHORT).show();  
  13.             return false;  
  14.         }  
  15.     }){  
  16.         public void handleMessage(Message msg) {  
  17.             Toast.makeText(MainActivity.this“发出消息”, Toast.LENGTH_SHORT).show();  
  18.         }  
  19.     };  
  20.   
  21.     private  class Person{  
  22.         String name;  
  23.         int age;  
  24.   
  25.         @Override  
  26.         public String toString() {  
  27.             return “name=”+name+“ age=”+age;  
  28.         }  
  29.     }  
  30.   
  31.     @Override  
  32.     protected void onCreate(Bundle savedInstanceState) {  
  33.         super.onCreate(savedInstanceState);  
  34.         setContentView(R.layout.activity_main);  
  35.   
  36.         mTextView = (TextView) findViewById(R.id.tv);  
  37.         Button btn = (Button) findViewById(R.id.btn);  
  38.         btn.setOnClickListener(new View.OnClickListener() {  
  39.             @Override  
  40.             public void onClick(View v) {  
  41.                 mHandler.sendEmptyMessage(0);  
  42.             }  
  43.         });  
  44.   
  45.   
  46.     }  
  47. }  
四、为什么在Android中智能通过Handler机制在主线程中更新UI?

最根本的是 解决多线程并发问题。
假如在同一个Activity中,有多个线程同时更新UI,且没有加锁,那会导致什么问题呢?
UI更新混乱。
假如加锁呢?
会导致 性能下降。
使用Handler机制,我们不用去考虑多线程的问题,所有更新UI的操作,都是在 主线程消息队列中轮询去处理的。

五、Handler机制的原理

1、Handler封装了消息的发送(主要包括消息发送给谁)
Looper
(1)内部包含一个消息队列,即MessageQueue,所有Handler发送的消息都会进入这个队列
(2)Looper.loop方法,是一个死循环,不断从MessageQueue取出消息,如有消息就处理,没有就阻塞

2、MessageQueue,一个消息队列,可以添加消息,处理消息

3、Handler内部会跟Looper进行关联,也就是说,在Handler内部可以找到Looper,找到了Looper也就找到了MessageQueue,在Handler中发送消息,其实就是向Message发送消息,

总结:Handler负责发送消息,Looper负责接收消息,并把消息回传给Handler自己,而MessageQueue是一个存储消息的容器。


源码层:
Android的应用程序是通过ActivityThread进行创建,在ActivityThread默认创建一个Main线程,一个Looper,所有更新UI的线程都是通过Main线程进行创建的。
查看Looper.loop的源代码,发现确实是一个死循环


  1. public static void loop() {  
  2.     final Looper me = myLooper();  
  3.     if (me == null) {  
  4.         throw new RuntimeException(“No Looper; Looper.prepare() wasn’t called on this thread.”);  
  5.     }  
  6.     final MessageQueue queue = me.mQueue;  
  7.   
  8.     // Make sure the identity of this thread is that of the local process,  
  9.     // and keep track of what that identity token actually is.  
  10.     Binder.clearCallingIdentity();  
  11.     final long ident = Binder.clearCallingIdentity();  
  12.   
  13.     for (;;) {  
  14.         Message msg = queue.next(); // might block  
  15.         if (msg == null) {  
  16.             // No message indicates that the message queue is quitting.  
  17.             return;  
  18.         }  
  19.   
  20.         // This must be in a local variable, in case a UI event sets the logger  
  21.         Printer logging = me.mLogging;  
  22.         if (logging != null) {  
  23.             logging.println(”>>>>> Dispatching to ” + msg.target + “ ” +  
  24.                     msg.callback + ”: ” + msg.what);  
  25.         }  
  26.   
  27.         msg.target.dispatchMessage(msg);  
  28.   
  29.         if (logging != null) {  
  30.             logging.println(”<<<<< Finished to ” + msg.target + “ ” + msg.callback);  
  31.         }  
  32.   
  33.         // Make sure that during the course of dispatching the  
  34.         // identity of the thread wasn’t corrupted.  
  35.         final long newIdent = Binder.clearCallingIdentity();  
  36.         if (ident != newIdent) {  
  37.             Log.wtf(TAG, ”Thread identity changed from 0x”  
  38.                     + Long.toHexString(ident) + ” to 0x”  
  39.                     + Long.toHexString(newIdent) + ” while dispatching to ”  
  40.                     + msg.target.getClass().getName() + ” ”  
  41.                     + msg.callback + ” what=” + msg.what);  
  42.         }  
  43.   
  44.         msg.recycleUnchecked();  
  45.     }  
  46. }  

发现是通过msg.target.dispatchMessage()方法来处理消息,查看其源码

  1. public void dispatchMessage(Message msg) {  
  2.     if (msg.callback != null) {  
  3.         handleCallback(msg);  
  4.     } else {  
  5.         if (mCallback != null) {  
  6.             if (mCallback.handleMessage(msg)) {  
  7.                 return;  
  8.             }  
  9.         }  
  10.         handleMessage(msg);  
  11.     }  
  12. }  


从源码看出,当有CallBack的时候,会截获消息,没有的话会回调handleMessage()来处理消息

而对于SendMessage()系列的方法,这里不再做过多解析,但从其源码可以看出,确实是最终把消息传入了消息队列中。

六、创建与线程相关的Handler


在子线程中创建Handler,需要通过Looper.prepare()获取Looper,且调用Looper.loop()方法对消息队列中的Message进行轮询


  1. public class MainActivity extends AppCompatActivity {  
  2.   
  3.   
  4.     private TextView mTextView;  
  5.   
  6.     public Handler mHandler = new Handler(){//主线程中的Handler  
  7.         @Override  
  8.         public void handleMessage(Message msg) {  
  9.             Log.d(”CurrentThread”,Thread.currentThread()+“”);//打印Thread 的ID  
  10.         }  
  11.     };  
  12.   
  13.     class MyThread extends Thread{  
  14.         private Handler handler;//子线程中的Handler  
  15.         @Override  
  16.         public void run() {  
  17.             Looper.prepare();//获取Looper  
  18.             handler = new Handler(){  
  19.                 @Override  
  20.                 public void handleMessage(Message msg) {  
  21.                     Log.d(”CurrentThread”,Thread.currentThread()+“”);  
  22.                 }  
  23.             };  
  24.             Looper.loop();//轮询消息队列  
  25.         }  
  26.     }  
  27.   
  28.   
  29.   
  30.     @Override  
  31.     protected void onCreate(Bundle savedInstanceState) {  
  32.         super.onCreate(savedInstanceState);  
  33.         setContentView(R.layout.activity_main);  
  34.   
  35.         MyThread thread= new MyThread();  
  36.         thread.start();  
  37.         try {  
  38.             thread.sleep(500);  
  39.         } catch (InterruptedException e) {  
  40.             e.printStackTrace();  
  41.         }  
  42.         thread.handler.sendEmptyMessage(1);  
  43.         mHandler.sendEmptyMessage(1);  
  44.     }  
  45. }  

输出的结果
03-31 20:56:06.498 1804-1816/? D/CurrentThread: Thread[Thread-113,5,main]
03-31 20:56:06.578 1804-1804/com.lian.handlerdemo D/CurrentThread: Thread[main,5,main]

七、HandlerThread

HandlerThread本质是一个Thread,区别在于他在run()之后创建了一个含有消息队列的Looper,这样我们在子线程中创建Handler时候只需指定使用HandlerThread中的Looper,不用再调用Looper.prepare(),looper.loop()等,简化了操作。
Android系统提供的Handler使用的Looper默认绑定了UI线程的消息队列,所以我们在Handler中不能进行耗时操作,而对于非UI线程,若想使用消息机制,HandlerThread内部的Looper是最合适的,他不会阻塞UI线程。


  1. public class MainActivity extends AppCompatActivity {  
  2.   
  3.   
  4.     private TextView mTextView;  
  5.   
  6.     public HandlerThread mHandlerThread;  
  7.   
  8.   
  9.   
  10.     @Override  
  11.     protected void onCreate(Bundle savedInstanceState) {  
  12.         super.onCreate(savedInstanceState);  
  13.         setContentView(R.layout.activity_main);  
  14.   
  15.         mHandlerThread = new HandlerThread(“handler thread”);  
  16.         mHandlerThread.start();  
  17.         Handler handler = new Handler(mHandlerThread.getLooper()){//通过getLooper()获取Looper  
  18.             @Override  
  19.             public void handleMessage(Message msg) {  
  20.                 Log.d(”current thread”,“” + Thread.currentThread());  
  21.             }  
  22.         };  
  23.         handler.sendEmptyMessage(1);  
  24.     }  
  25. }  

结果:
03-31 21:36:42.770 7225-7237/? D/current thread: Thread[handler thread,5,main]


八、主线程与子线程信息交互

主线程中的Handler与子线程中的Handler互相发送消息,只要调用对方的sendMessage()就可以了


  1. public class MainActivity extends AppCompatActivity {  
  2.   
  3.   
  4.     public Handler mHandler = new Handler(){  
  5.         @Override  
  6.         public void handleMessage(Message msg) {  
  7.             Log.d(”current thread”“” + Thread.currentThread());  
  8.             Message message = new Message();  
  9.             message.what = 1;  
  10.             handler.sendMessageDelayed(message,1000);//向子线程的Handler发送消息  
  11.         }  
  12.     };  
  13.   
  14.     public HandlerThread mHandlerThread;  
  15.     public Handler handler;  
  16.     private Button btn1,btn2;  
  17.   
  18.   
  19.   
  20.     @Override  
  21.     protected void onCreate(Bundle savedInstanceState) {  
  22.         super.onCreate(savedInstanceState);  
  23.         setContentView(R.layout.activity_main);  
  24.   
  25.         btn1 = (Button) findViewById(R.id.btn);  
  26.         btn2 = (Button) findViewById(R.id.btn2);  
  27.   
  28.         mHandlerThread = new HandlerThread(“handler thread”);//指定HandlerThread的名字  
  29.         mHandlerThread.start();  
  30.         handler = new Handler(mHandlerThread.getLooper()){//通过getLooper()获取Looper  
  31.             @Override  
  32.             public void handleMessage(Message msg) {  
  33.                 Log.d(”current thread”“” + Thread.currentThread());  
  34.                 Message message = new Message();  
  35.                 mHandler.sendMessageDelayed(message,1000);//向主线程中的Handler发送消息  
  36.             }  
  37.         };  
  38.   
  39.         btn1.setOnClickListener(new View.OnClickListener() {  
  40.             @Override  
  41.             public void onClick(View v) {  
  42.                 handler.sendEmptyMessage(1);//开始发送消息  
  43.             }  
  44.         });  
  45.   
  46.         btn2.setOnClickListener(new View.OnClickListener() {  
  47.             @Override  
  48.             public void onClick(View v) {  
  49.                 handler.removeMessages(1);//停止发送消息  
  50.             }  
  51.         });  
  52.   
  53.     }  
  54. }  

结果:
03-31 22:21:11.422 16748-16760/com.lian.handlerdemo D/current thread: Thread[handler thread,5,main]
03-31 22:21:12.422 16748-16748/com.lian.handlerdemo D/current thread: Thread[main,5,main]
03-31 22:21:13.422 16748-16760/com.lian.handlerdemo D/current thread: Thread[handler thread,5,main]
03-31 22:21:14.422 16748-16748/com.lian.handlerdemo D/current thread: Thread[main,5,main]
03-31 22:21:15.426 16748-16760/com.lian.handlerdemo D/current thread: Thread[handler thread,5,main]
03-31 22:21:16.426 16748-16748/com.lian.handlerdemo D/current thread: Thread[main,5,main]
03-31 22:21:20.414 16748-16760/com.lian.handlerdemo D/current thread: Thread[handler thread,5,main]
03-31 22:21:21.414 16748-16748/com.lian.handlerdemo D/current thread: Thread[main,5,main]
03-31 22:21:22.414 16748-16760/com.lian.handlerdemo D/current thread: Thread[handler thread,5,main]
03-31 22:21:23.418 16748-16748/com.lian.handlerdemo D/current thread: Thread[main,5,main]


九、四种更新UI的方法

1、Handler.post();

2、Handler.sendMessage();
第一二种方法事实上没有本质的区别,都是通过发送消息,在UI线程中更新UI,前面已经做过演示,不再赘述

3、runOnUIThread()
使用方法:
  1. public class MainActivity extends AppCompatActivity {  
  2.     TextView mTextView;  
  3.   
  4.     @Override  
  5.     protected void onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.         setContentView(R.layout.activity_main);  
  8.   
  9.         mTextView = (TextView) findViewById(R.id.tv);  
  10.   
  11.         new Thread(){  
  12.   
  13.             @Override  
  14.             public void run() {  
  15.   
  16.                 try {  
  17.                     Thread.sleep(1000);  
  18.                 } catch (InterruptedException e) {  
  19.                     e.printStackTrace();  
  20.                 }  
  21.                 runOnUiThread(new Runnable() {  
  22.                     @Override  
  23.                     public void run() {  
  24.                         mTextView.setText(”更新UI”);  
  25.                     }  
  26.                 });  
  27.             }  
  28.         }.start();  
  29.   
  30.     }  
  31. }  



我们查看runOnUIThread()的源代码

  1. public final void runOnUiThread(Runnable action) {  
  2.     if (Thread.currentThread() != mUiThread) {  
  3.         mHandler.post(action);  
  4.     } else {  
  5.         action.run();  
  6.     }  
  7. }  

可以发现,其本质上仍然是通过Handler.post()方法再UI线程中更新UI


4、View.post()
使用方法
  1. public class MainActivity extends AppCompatActivity {  
  2.     TextView mTextView;  
  3.   
  4.     @Override  
  5.     protected void onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.         setContentView(R.layout.activity_main);  
  8.   
  9.         mTextView = (TextView) findViewById(R.id.tv);  
  10.   
  11.         new Thread(){  
  12.             @Override  
  13.             public void run() {  
  14.                 try {  
  15.                     Thread.sleep(1000);  
  16.                 } catch (InterruptedException e) {  
  17.                     e.printStackTrace();  
  18.                 }  
  19.                 mTextView.post(new Runnable() {  
  20.                     @Override  
  21.                     public void run() {  
  22.                         mTextView.setText(”更新UI”);  
  23.                     }  
  24.                 });  
  25.             }  
  26.         }.start();  
  27.   
  28.     }  
  29. }  

查看其源码,一样是采用Handler.post()方法更新UI


  1. public boolean post(Runnable action) {  
  2.     final AttachInfo attachInfo = mAttachInfo;  
  3.     if (attachInfo != null) {  
  4.         return attachInfo.mHandler.post(action);  
  5.     }  
  6.     // Assume that post will succeed later  
  7.     ViewRootImpl.getRunQueue().post(action);  
  8.     return true;  
  9. }  


十、在非UI线程中更新UI的方法

 先看一个Demo
  1. public class MainActivity extends AppCompatActivity {  
  2.     TextView mTextView;  
  3.   
  4.     @Override  
  5.     protected void onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.         setContentView(R.layout.activity_main);  
  8.   
  9.         mTextView = (TextView) findViewById(R.id.tv);  
  10.   
  11.         new Thread(){  
  12.             @Override  
  13.             public void run() {  
  14.                 mTextView.setText(”更新UI了”);  
  15.             }  
  16.         }.start();  
  17.   
  18.     }  
  19. }  
结果:



惊讶地发现,成功更新了UI,并没有抛出异常

然而当我们先让线程休眠2s,再更新


  1. public class MainActivity extends AppCompatActivity {  
  2.     TextView mTextView;  
  3.   
  4.     @Override  
  5.     protected void onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.         setContentView(R.layout.activity_main);  
  8.   
  9.         mTextView = (TextView) findViewById(R.id.tv);  
  10.   
  11.         new Thread(){  
  12.             @Override  
  13.             public void run() {  
  14.   
  15.                 try {  
  16.                     Thread.sleep(2000);  
  17.                 } catch (InterruptedException e) {  
  18.                     e.printStackTrace();  
  19.                 }  
  20.   
  21.                 mTextView.setText(”更新UI了”);  
  22.             }  
  23.         }.start();  
  24.   
  25.     }  
  26. }  

更新失败,抛出异常


这是什么原因呢?

在Activity中有一个ViewRootImpl类,这个类没有实例化的时候,系统不会检测当前线程是否UI线程,而这个类的实例化是在Activity的onResume()中实现,所以,当我们没有让子线程休眠时,直接更新UI,系统还来不及检测当前线程是否UI线程,于是我们成功更新了UI,
而休眠二秒中后,ViewRootImpl已经实例化,此时更新UI就会抛出异常。
当然,在实际开发中,这意义不大,我们还是要在UI线程中更新UI。

十一、常见的两个问题

使用Handler常遇到的两个异常:
1、非UI线程更新UI
也就是我们上面遇到的问题
抛出这个异常:
 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

2、子线程中创建Handler缺少Looper
抛出这个异常:
java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()

查看源码
  1. mLooper = Looper.myLooper();  
  2.     if (mLooper == null) {  
  3.         throw new RuntimeException(  
  4.             ”Can’t create handler inside thread that has not called Looper.prepare()”);  
  5.     }  
  6.     mQueue = mLooper.mQueue;  
  7.     mCallback = callback;  
  8.     mAsynchronous = async;  
  9. }  

发现,没有Looper,就会抛出这个运行时异常。





猜你喜欢

转载自blog.csdn.net/tgvincent/article/details/80800026