Handler 使用方法详解
Handler是 Android中用来更新UI 的一套消息处理机制。Handler 允许线程间发送Message或Runnable对象进行通信。在Android中UI修改只能通过UI Thread,子线程不能更新UI。如果子线程想更新UI,需要通过 Handler 发送消息给主线程,进而达到更新UI的目的。
欢迎关注微信公众号:程序员Android
公众号ID:ProgramAndroid
获取更多信息
微信公众号:ProgramAndroid
我们不是牛逼的程序员,我们只是程序开发中的垫脚石。
我们不发送红包,我们只是红包的搬运工。
通过本章学习你将掌握以下知识点
- Handler 消息处理机制原理
- Handler 机制处理的4个关键对象
- Handler常用方法
- 子线程更新UI 异常处理
- 主线程给子线程发送消息的方法
- 子线程给主线程发送消息的方法
- 主、子 线程 互发消息方法
- 子线程方法中调用主线程更新UI的方法
继承关系如下:
java.lang.Object ↳ android.os.Handler
1. Handler 消息处理机制原理
当Android 应用程序创建的时候,系统会给每一个进程提供一个Looper ,Looper 是一个死循环,它内部维护一个消息队列,Looper 不停的从消息队列中取Message,取到的消息就发送给handler,最后Handler 根据接收的消息去修改UI等。
2. Handler 机制处理的4个关键对象
1.Message
线程之间传递的消息,可以携带一些简单的数据供子线程与主线程进行交换数据。
2.Message Queue
存放通过Handler 发送的 Message 的消息队列,每一个线程只有一个消息队列。
3.Handler
消息处理者,主要用于发送跟处理消息。
主要功能:
发送消息SendMessage()
处理消息 HandleMessage()
4.Looper
内部包含一个死循环的MessageQueue,用于存储handler 发送的Message,Looper则是不断的从消息队列中取消,如果有消息就取出发送给Handler 处理,没有则阻塞。
总结:
Handler 负责发送Message到Message Queue,Looper负责从Message Queue 遍历Message ,然后直接把遍历的消息回传给Handler 自己,通过Handler 自身的handleMessage处理更新UI等操作。
主线程、子线程间通信简单流程
3. Handler常用方法
1.Runnable对象
- post(Runnable)
使用方法举例:
private Handler mRunnableHandler = new Handler(); public void RunnableHandlderMethod() { new Thread() { @Override public void run() { try { Thread.sleep(1000); mRunnableHandler.post(new Runnable() { @Override public void run() { ((Button) findViewById(R.id.btn_runnable)).setText("Runnable"); } }); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); }
- postAtTime(Runnable, long)
- postDelayed(Runnable, long)
- Message 对象
- sendEmptyMessage(int)
使用方法举例:
private int mCount = 0; private Handler mMessageHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); ((Button) findViewById(R.id.btn_thread)).setText("" + mCount); } }; class MessageHandlerThreadMethod extends Thread { String mString; public MessageHandlerThreadMethod(String str) { mString = str; } @Override public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(1000); } catch (Exception e) { } mCount++; mMessageHandler.sendEmptyMessage(0); } } } //1.Message 对象 new MessageHandlerThreadMethod("子线程不能更新UI").start();
- sendMessage(Message),
使用方法举例:
- sendMessageAtTime(Message, long),
- sendMessageDelayed(Message, long)
3.接收、处理Message
- handleMessage(Message)
使用方法举例:
private Handler mMessageHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); ((Button) findViewById(R.id.btn_thread)).setText("" + mCount); } };
4. 子线程更新UI 异常处理
子线程不能更新UI,如果在子线程中更新UI,会出现CalledFromWrongThreadException 异常。
- CalledFromWrongThreadException
CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
解决方法:
子线程通过Handler 发送消息给主线程,让主线程处理消息,进而更新UI。
5. 主线程给子线程发送消息的方法
此例子中子线程通过Looper不断遍历主线程发送的消息,Looper 使用方法如下:
-
- 准备Looper 轮询器
Looper.prepare();
- 准备Looper 轮询器
-
- Handler 处理遍历消息
Handler mHandler = new Handler()
- Handler 处理遍历消息
-
- 遍历消息队列
Looper.loop();
- 遍历消息队列
Looper 使用方法如下:
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(); } }
主线程发送消息的使用例子如下:
public void BtnMainMessageMethod(View view) { Message msg = new Message(); msg.obj = "主线程:这是我携带的信息"; if (mMainHandler != null) { mMainHandler.sendMessage(msg); } else { Toast.makeText(getApplicationContext(), "开启子线程轮询消息,请再次点击发送消息", Toast.LENGTH_SHORT).show(); //开启轮询线程,不断等待接收主线成消息 new ChildLooperThread().start(); } } private Handler mMainHandler; String mMainMessage; // 自定义 Loop 线程 ---> 不停的处理主线程发的消息 class ChildLooperThread extends Thread { @Override public void run() { // 1.准备成为loop线程 Looper.prepare(); // 2.处理消息 mMainHandler = new Handler() { // 处理消息 public void handleMessage(Message msg) { super.handleMessage(msg); mMainMessage = (String) msg.obj; Log.i("TAG", "子线程:从主线程中接受的消息为:\n" + mMainMessage); // 使用 runOnUiThread 在主线程中更新UI runOnUiThread(new Runnable() { @Override public void run() { ((Button) findViewById(R.id.btn_main_message)).setText(mMainMessage); } }); } }; // 3.Loop循环方法 Looper.loop(); } }
6. 子线程给主线程发送消息的方法
子线程发送消息给主线程方法
public void BtnChildMessageMethod(View view) { new Thread() { public void run() { while (mCount < 100) { mCount++; try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } /** * 利用handler 对象发送消息 * Message msg=Message.obtain(); * Message msg=new Message(); 获取一个消息对象message * */ Message msg = Message.obtain(); // 消息标记 msg.what = 1; // 传递整型值msg.obj="传递object数据" msg.arg1 = mCount; Log.i("TAG", "count 值=" + mCount); if (mhandler != null) { mhandler.sendMessage(msg); } } } ; }.start(); } //定义一个handler 主线程 接收子线程发来的信息 private Handler mhandler = new Handler() { // 處理消息的方法 public void handleMessage(android.os.Message msg) { switch (msg.what) { case 1: int value = msg.arg1; Log.i("TAG", "value值=" + value); ((Button) findViewById(R.id.btn_child_message)).setText("当前值=" + value); break; default: break; } } };
7. 主、子 线程 互发消息方法
主要实现主、子线程每隔1s中通信一次
- 实现打印Log如下:
主、子线程通信log信息
- 实现方法如下:
public void BtnChildMessageMethod(View view) { new Thread() { public void run() { while (mCount < 100) { mCount++; try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } /** * 利用handler 对象发送消息 * Message msg=Message.obtain(); * Message msg=new Message(); 获取一个消息对象message * */ Message msg = Message.obtain(); // 消息标记 msg.what = 1; // 传递整型值msg.obj="传递object数据" msg.arg1 = mCount; Log.i("TAG", "count 值=" + mCount); if (mhandler != null) { mhandler.sendMessage(msg); } } } ; }.start(); } //定义一个handler 主线程 接收子线程发来的信息 private Handler mhandler = new Handler() { // 處理消息的方法 public void handleMessage(android.os.Message msg) { switch (msg.what) { case 1: int value = msg.arg1; Log.i("TAG", "value值=" + value); ((Button) findViewById(R.id.btn_child_message)).setText("当前值=" + value); break; default: break; } } }; private Handler mChildHandler; //创建主线程 private Handler mainhandler = new Handler() { @Override public void handleMessage(Message msg) { Log.i("TAG", "子线程对我说:" + msg.obj);// 主线成携带的消息内容 Message message = new Message(); message.obj = Thread.currentThread() + "我是主线程:小子你得听我的。";// 向子线程发送消息 mChildHandler.sendMessageDelayed(message, 1000); } }; public void BtnMainChildMessageMethod(View view) {// 创建 名称为currentThread 子线程 HandlerThread mChildThread = new HandlerThread("ChildThread"); mChildThread.start(); mChildHandler = new Handler(mChildThread.getLooper()) { @Override public void handleMessage(Message msg) { Log.i("TAG", "主线程对我说:" + msg.obj); // 子线程携带的消息 Message message = new Message(); message.obj = Thread.currentThread() + "我是子线程,小样,让我听你的没门";// 向主线程发送消息 mainhandler.sendMessageDelayed(message, 1000); } }; // 主线成发送空消息,开启通信 mainhandler.sendEmptyMessage(1); }
8.子线程方法中调用主线程更新UI的方法
- Activity 中 可以使用runOnUiThread(Runnable)
// 使用 runOnUiThread 在主线程中更新UI runOnUiThread(new Runnable() { @Override public void run() { ((Button) findViewById(R.id.btn_main_message)).setText(mMainMessage); } });
- Handler.post(Runnable)
mRunnableHandler.post(new Runnable() { @Override public void run() { ((Button) findViewById(R.id.btn_runnable)).setText("Runnable"); } });
- View.post()
((Button) findViewById(R.id.btn_runnable)).post(new Runnable() { @Override public void run() { ((Button) findViewById(R.id.btn_runnable)).setText("Runnable"); } });
- Handler.sendMessage(Message)
至此,本篇已结束,如有不对的地方,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!
如有侵权,请联系小编,小编对此深感抱歉,届时小编会删除文章,立即停止侵权行为,请您多多包涵。
既然都看到这里,领两个红包在走吧!
以下两个红包每天都可以领取
1.支付宝搜索 522398497,或扫码支付宝红包海报。
支付宝扫一扫,每天领取大红包
2.微信红包,微信扫一扫即可领取红包
微信扫一扫,每天领取微信红包
小礼物走一走,来简书关注我
更多相关文章
- android异步任务完成后再执行主线程任务
- Android应用程序键盘(Keyboard)消息处理机制分析(27)
- Android消息推送接收后,通知栏的显示
- Android应用程序键盘(Keyboard)消息处理机制分析(17)
- Android使用线程获取网络图片的方法
- android中使用多线程——HandlerThread举例
- android 多线程断点下载,listview 模式 开始 暂停等功能