最近项目上用到Handler比较多,遇到不少麻烦,也体会到不少,Handler在Android开发经常会用到,但是很多人包括我都是对他的原理也是一知半解,这里总结一下自己对Handler的学习,欢迎补充和纠正。
Handler的作用:
- 发送和处理消息(Message)
- 发送和处理runnable对象
Handler涉及到几个概念:
1.Message:包含了消息id,数据,等信息,由MessageQueue队列控制。
2.MessageQueue:消息队列,用链表的方式存储Message,按照FIFO(队列先进先出规则)让Looper来 抽取Message,进行处理。
3.Looper:一个线程只有一个Looper对象,负责不断从MessageQueue 抽取Message进行处理。
发送和处理消息(Message)
//Message有两种获得方法;
Handler myHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
if(msg.what ==1){
}
super.handleMessage(msg);
}
};
// 第一种 调用myHandler.obtainMessage() 获得的Message已经跟myHandler绑定
Message msg1 = myHandler.obtainMessage();
// 直接调用 sendToTarget() 就可以把消息送入队列,等待Handler执行
msg1.sendToTarget();
// 第二种 直接用Message的构造函数
Message msg2 = new Message();
// 调用Handler 实例的 sendMessage()方法把消息送入队列,等待Handler执行
myHandler.sendMessage(msg2);//即时send
myHandler.sendEmptyMessageAtTime(1, uptimeMillis);//在uptimeMillis时间点 send属性what值为what的Message
myHandler.sendEmptyMessage(1);//send属性what值为what的Message
//其他以此类推
发送和处理消息 Runnable:
Runnable runnable = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
};
long time = System.currentTimeMillis();
myHandler.post(runnable);//即使post
myHandler.postAtFrontOfQueue(runnable);//插入队头
myHandler.postAtTime(runnable, time);//在time时间点 post
myHandler.postDelayed(runnable, 3000);//延迟3秒 post
//其他以此类推
关于线程问题:
Android,启动一个应用就会开启一个线程,这个线程是主线程,处理各种UI控件和消息的响应,所以,在这个线程上不要运行耗时的操作,这样会出现UI的停顿,超过5S系统,自动弹出强制关闭窗口,但是所有UI操作都必须在主线程里面进行操作,这时候在进行耗时任务时候,就可以用Handler在子线程里面,发送消息,然后在主线程里面用Handler去处理消息,来改变UI。
例子不自己写了 ,在网上找了一个
package com.blueeagle;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class SendMessage extends Activity {
private TextView textView;
private MyHandler myHandler;//定义一个自己的Handle类
private Button button;
private MyThread m=new MyThread(); //定义一个自己的线程类
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textView=(TextView)findViewById(R.id.text);
button=(Button)findViewById(R.id.startButton);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
myHandler=new MyHandler();
new Thread(m).start();
System.out.println("主线程运行ID:"+Thread.currentThread().getId());
}
});
}//在对UI进行更新时,执行时所在的线程为主UI线程
class MyHandler extends Handler{//继承Handler类时,必须重写handleMessage方法
public MyHandler(){
}
public MyHandler(Looper l){
super(l);
}
@Override
public void handleMessage(Message msg) {//执行接收到的通知,此时执行的顺序是按照队列进行,即先进先出
super.handleMessage(msg);
Bundle b=msg.getData();
String textStr1=b.getString("textStr");
SendMessage.this.textView.setText(textStr1);//更改TextView中的值
}
}//该线程将会在单独的线程中运行
class MyThread implements Runnable{
int i=1;
@Override
public void run() {
while(i<11){
System.out.println("当前运行线程ID:"+Thread.currentThread().getId());
try {
Thread.sleep(1000);
}
catch(InterruptedException e){
e.printStackTrace();
}
Message msg=new Message();
Bundle b=new Bundle();
b.putString("textStr", "线程运行"+i+"次");
i++;
msg.setData(b);
SendMessage.this.myHandler.sendMessage(msg);//通过sendMessage向Handler发送更新UI的消息
}
i=1;//下次启动线程时重新计数。
}
}
}
Handler与线程的关系:
Handler必须依附线程的Looper.
为什么在子线程创建Handler 会报错,因为子线程没有Looper给Handler使用,这时候有两种方法
- 在主线程创建Handler
handler = new Handler(Looper.getMainLooper()) ;//这样的Handler跟主线程 实例化的效果一样
2. 在子线程创建Handler
第一种是在主线程中调用Looper的静态方法Looper.prepare()方法创建Looper对象
Looper.prepare();// 创建该线程的Looper对象,用于接收消息,在非主线程中是没有looper的所以在创建handler前一定要使用prepare()创建一个Looper
Handler myThreadHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
Log.d(Constant.TAG, MessageFormat.format("Thread[{0}]--myThreadHandler handleMessage run...",
Thread .currentThread().getName()));
}
Looper.myLooper().loop();//建立一个消息循环,支持该线程将不会退出,不断进行循环
第二种 利用 HandlerThread 创建新线程 ,该线程初始化后就已经有了Looper对象
//生成一个HandlerThread对象
HandlerThread handlerThread = new HandlerThread("handler_thread");
//在使用HandlerThread的getLooper()方法之前,必须先调用该类的start(),同时开启一个新线程;
handlerThread.start();
//将由HandlerThread获取的Looper传递给Handler对象,即由处于另外线程的Looper代替handler初始化时默认绑定的消息队列来处理消息。
//自定义Handler
MyHandler myHandler = new MyHandler(handlerThread.getLooper());
关于Runnable的使用:
很多人会认为Handler post进去的就是一个线程,这里要纠正一下,Runnable只是个接口,并不是线程,是要配合Thread 的 start() 方法才能开启有一个心线程。
post进去队列的Runnable实例,当被Looper拿出来执行的时候,只是执行run()方法而已,这里并没有产生新线程,所以Handler是依附UI线程的时候,实现Runnable接口的run()方法的时候,不能进行耗时操作,不然会出现UI卡死。
还有一点队列是先进先出,如果要不断post多个Runnable进去队列,如果Ruannable post进去的时间间隔 delay区别太大,会出现一个总是被执行,而另一个被执行的次数大大减小。
相关推荐
Android Handler详细解析,讲解Handler之间的通讯,叫你如果用Handler完成异步线程对 UI的更新
Android Handler机制解析
NULL 博文链接:https://dingran.iteye.com/blog/1930178
android handler 机制源码 (带部分汉语注释)
在上一篇文章《Android应用程序消息处理机制(Looper、Handler)分析》中,我们分析了Android应用程序的消息处理机制,本文将结合这种消息处理机制来详细分析Android应用程序是如何获得键盘按键消息的。
Android Handler 原理分析 Handler一个让无数android开发者头疼的东西,希望我今天这边文章能为您彻底根治这个问题 今天就为大家详细剖析下Handler的原理 Handler使用的原因 1.多线程更新Ui会导致UI界面错乱 2.如果...
Android应用程序消息处理机制(Looper、Handler)分析
主要为大家详细介绍了Android Handler的原理,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
其中sMainLooper是个全局静态Looper,目前android系统特指为UI主Main线程对应的消息,这么设定,应该是大部分的消息处理只需要放到主Mai
主要介绍了Android Handler leak分析及解决办法详解的相关资料,需要的朋友可以参考下
先上图,让大家好理解下handler机制:handler机制示例图上面一共出现了几种类,ActivityThread,Handler,MessageQueue,Looper,msg(Message),对这些类作简要介绍:ActivityThread:程序的启动入口,为什么要介绍...
主要介绍了Android定时器和Handler用法,实例分析了Android中的定时器与Handler相关使用技巧,非常具有实用价值,需要的朋友可以参考下
Demo-实例讲解线程池里面的UI如何刷新,处理两个开发者头疼的问题: 1. 数据经常需要读取更新,并且比较耗时,需要分步刷新UI. 2. UI界面切换后,如何停止掉子线程里面正在读取的数据而不会将旧数据刷新到新UI界面上...
内存泄露的危害就是会使虚拟机占用内存过高,导致OOM(内存溢出),程序出错。接下来通过本文给大家分享Android使用Handler造成内存泄露问题及解决方法,一起看看吧
Android_Blog_Demos 存储CSDN博客的一些源码...Android IntentService完全解析 当Service遇到Handler 详细 Android 高清加载巨图方案 拒绝压缩图片 ViewDragHelper实战 自己打造Drawerlayout Android UI性能优化实
Android Handler Message源码解析和手写实现
Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系 Android消息循环分析 Android Activity developer 官网 (强烈推荐 dev guide) Android的启动模式(android:launchMode) ...
最近总结了一下handler的使用,handler是Android中要的消息机制之一,在面试和实际开发中尤为重要,所以总结了一下,传到这里,和大家交流学习