Handler 使用详解
极力推荐文章:欢迎收藏
Android 干货分享
阅读五分钟,每日十点,和您一起终身学习,这里是程序员Android
本篇文章主要介绍 Android
开发中的部分知识点,通过阅读本篇文章,您将收获以下内容:
- Handler 消息处理机制原理
- Handler 机制处理的4个关键对象
- Handler常用方法
- 子线程更新UI 异常处理
- 主线程给子线程发送消息的方法
- 子线程给主线程发送消息的方法
- 主、子 线程 互发消息方法
- 子线程方法中调用主线程更新UI的方法
Handler
是 Android
中用来更新UI 的一套消息处理机制。Handler
允许线程间发送Message
或Runnable
对象进行通信。在Android
中UI修改只能通过UI Thread
,子线程不能更新UI
。如果子线程想更新UI
,需要通过 Handler
发送消息给主线程,进而达到更新UI
的目的。
Handler 简介
继承关系如下:
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)
使用方法举例:
public void BtnRunnableMethod(View view) {
// 1.Runnable 对象
RunnableHandlderMethod();
}
/**
* Runnable 对象更新 UI
* **/
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)
2. Message 对象
- sendEmptyMessage(int)
使用方法举例:
public void BtnMessageThreadMethod(View view) {
// 2.Message 对象
new MessageHandlerThreadMethod("子线程不能更新UI").start();
}
/**
* Message 对象举例
* ***/
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);
}
}
}
- sendMessage(Message)
使用方法举例:
public void BtnMessageObjMethod(View view) {
HandlerMessageObjMethods();
}
/***
* handler sendmessage 处理方法
* **/
private Handler mHandlerMessageObj = new Handler() {
@Override
public void handleMessage(Message msg) {
((Button) findViewById(R.id.btn_message)).setText("arg1:"
+ msg.arg1 + "\n" + msg.obj);
}
};
private void HandlerMessageObjMethods() {
new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
// Message message = new Message();
Message message = mHandlerMessageObj.obtainMessage();
message.arg1 = 100;
Person person = new Person();
person.name = "Lucy";
person.age = 12;
message.obj = person;
mHandlerMessageObj.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
class Person {
public int age;
public String name;
public String toString() {
return "Name=" + name + "\n Age=" + age;
}
}
- 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
解决方法:
子线程通过Handler
发送消息给主线程,让主线程处理消息,进而更新UI
。
5. 主线程给子线程发送消息的方法
此例子中子线程通过Looper
不断遍历主线程发送的消息,Looper
使用方法如下:
- 准备Looper 轮询器
Looper.prepare();
- Handler 处理遍历消息
Handler mHandler = new Handler()
- 遍历消息队列
Looper.loop();
Looper
使用方法如下:
// 自定义 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);
... ...
}
});
}
};
// 3.Loop循环方法
Looper.loop();
}
}
主线程发送消息给子线程 的使用例子如下:
- 启动 子线程,并再启动后发送消息
public void BtnMainMessageMethod(View view) {
// 点击主线程 按钮,启动子线程,并在子线程启动后发送消息
Message msg = new Message();
msg.obj = "主线程:这是我携带的信息";
if (mMainHandler != null) {
// 2.主线程发送消息
mMainHandler.sendMessage(msg);
} else {
Toast.makeText(getApplicationContext(), "开启子线程轮询消息,请再次点击发送消息",
Toast.LENGTH_SHORT).show();
// 1.开启轮询线程,不断等待接收主线成消息
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. 子线程给主线程发送消息的方法
1.子线程发送消息给主线程方法
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();
}
2.主线程接收并处理消息的方法
// 定义一个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
如下:
- 实现方法如下:
- 启动子线程并发送给主线程消息
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);
}
2.主线程接收并处理子线程发送的消息
// 创建主线程
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);
}
};
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() {
// TODO Auto-generated method stub
((Button) findViewById(R.id.btn_runnable)).setText("View.post()方法使用");
}
});
Handler.sendMessage(Message)
public void BtnMainMessageMethod(View view) {
// 点击主线程 按钮,启动子线程,并在子线程启动后发送消息
Message msg = new Message();
msg.obj = "主线程:这是我携带的信息";
if (mMainHandler != null) {
// 2.主线程发送消息
mMainHandler.sendMessage(msg);
}
}
9.移除Handler 发送的消息方法
1.移除 handler 发送的所有消息
private Handler mChildHandler;
mChildHandler.removeCallbacksAndMessages(null);
2.移除 指定消息
private Handler mainhandler;
mainhandler.removeMessages(what);
至此,本篇已结束,如有不对的地方,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!
Handler 使用详解的更多相关文章
- android Handler机制详解
简单运行图: 名词解析: Message(消息):定义了一个包含描述以及随意的数据对象可以被发送到Hanlder的消息,获得消息的最好方法是Message.obtain或者Handler.o ...
- Handler知识点详解
Handler是在多线程之间使用的,用于线程之间进行通信. 要想知道为什么需要Handler就首先说明android的主线程和工作线程. 主线程又称为UI线程.正是因为在android中,所有与UI有 ...
- 【Android】Android实现Handler异步详解
方式不止一种,这里使用的是Timer类,创建一个定时器.我们经常需要获得移动设备端口的显示屏信息,但是onCreate()方法执行的时候,OnShow()方法不一定执行了,也就是说,在执行Oncrea ...
- Android学习总结(3)——Handler深入详解
什么是Handler Handler是Android消息机制的上层接口,它为我们封装了许多底层的细节,让我们能够很方便的使用底层的消息机制.Handler的最常见应用场景之一便是通过Handler在子 ...
- android Handler机制之ThreadLocal详解
概述 我们在谈Handler机制的时候,其实也就是谈Handler.Message.Looper.MessageQueue之间的关系,对于其工作原理我们不做详解(Handler机制详解). Messa ...
- Android多线程----异步消息处理机制之Handler详解
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...
- Message,MessageQueue,Looper,Handler详解+实例
Message,MessageQueue,Looper,Handler详解+实例 原文地址 Android的Handler使用(这篇简单介绍Handler的使用) 一.几个关键概念 1.Message ...
- Message,MessageQueue,Looper,Handler详解
Message,MessageQueue,Looper,Handler详解 一.几个关键概念 1.MessageQueue:是一种数据结构,见名知义,就是一个消息队列,存放消息的地方.每一个线程最 ...
- Handler Looper 原理 详解
演示代码 public class MainActivity extends ListActivity { private TextView tv_info; private CalT ...
随机推荐
- 数据预处理之独热编码(One-Hot):为什么要使用one-hot编码?
一.问题由来 最近在做ctr预估的实验时,还没思考过为何数据处理的时候要先进行one-hot编码,于是整理学习如下: 在很多机器学习任务如ctr预估任务中,特征不全是连续值,而有可能是分类值.如下: ...
- React躬行记(7)——表单
表单元素是一类拥有内部状态的元素,这些状态由其自身维护,通过这类元素可让用户与Web应用进行交互.HTML中的表单元素(例如<input>.<select>和<radio ...
- C# 收集几条ToString()格式
.ToString("C");//¥1,200.00 .ToString("D3");//025 string r3 = 2500.1231.ToString( ...
- 数据结构丨N叉树
遍历 N叉树的遍历 树的遍历 一棵二叉树可以按照前序.中序.后序或者层序来进行遍历.在这些遍历方法中,前序遍历.后序遍历和层序遍历同样可以运用到N叉树中. 回顾 - 二叉树的遍历 前序遍历 - 首先访 ...
- 9.22考试 crf的军训 题解
做这道题时由于第一道题太水了,第一反应是NOIP T2级别的题,需要拿上70~100的分,然后就开始分析,当然最后事实证明我错了…… 这道题当时首先联想到了 NOIP2016愤怒的小鸟 当然,数据范围 ...
- ~~函数基础(一):函数&参数~~
进击のpython 函数基础(一):参数 ==说明一点奥,如果你前置文章没有看懂,那么不建议你直接看这个== ==因为看了也是浪费时间,没有意义== 好,接下来就是Python的灵魂部分了--函数 在 ...
- ~~Py2&Py3~~
进击のpython python2 整型 int -- long(长整型) /获取的是整数 python3 整型 int /获取的是浮点数(小数) python2 print(range(1,10)) ...
- C程序疑问解答 ——可怕的野指针
本篇为原创,禁止任何形式的他用! 一.疑问点 指针是C语言一个很强大的功能,同时也是很容易让人犯错的一个功能,用错了指针,轻者只是报个错,重者可能整个系统都崩溃了.下面是大家在编写C程 ...
- markdown浅谈
markdown是啥? markdown就是一种修饰网页/博客的方法,他能使网页变得更美观. 我们先解释一下代码框: 这个没法保留,就是把键盘左上角的⋅·⋅ 切换成英文变成`. 然后``` 在隔一行` ...
- (转)Vix_API 操作 VMware
对虚拟机(VMware Workstation)进行程序控制,查询了VMware官方网站的一些内容,但调试的时候还是出现很多问题. 刚开始想通过命令行的方式控制虚拟机,但总是存在一些问题,到现在也没搞 ...