Android Handler简介及其用法
来源:互联网 发布:永恒之塔女性捏脸数据 编辑:程序博客网 时间:2024/05/29 12:42
Handler简介及其用法
前言
Handler类主要用处
1.在新启动的线程中发送消息。
2.在主线程中获取并处理消息。
Handler的运行机制
在启动了一个应用后,会有一个主线程,也称作UI线程,只有这个线程才能改变UI界面,伴随着这个线程产生的还有一个Looper,一个Looper可以和多个Handler绑定,一个UI线程就对应着它自己的Looper,这个Looper对象会一直从主线程的MessageQueue(消息队列)中取出消息并传递给Handler进行处理。因此我们在主线程中可以直接创建Handler然后使用即可。一个线程可以拥有多个Handler.
若想在子线程中控制UI界面,我们需要做的就是将主线程的Handler传入,然后向主线程的Handler发送消息,然后在主线程中的handleMessage()中去处理消息。
由于Handler是需要和线程绑定在一起的,因此在初始化Handler的时候需要注意了一般有两种方法创建:
1.给Handler指定Looper对象,那么这个Handler便绑定到了Looper对象所在的线程中,Handler的消息处理回调会在那个线程中执行。可以通过Loop.myLooper()得到当前线程的Looper对象,通过Loop.getMainLooper()可以获得当前进程的主线程的Looper对象。
2.不指定Looper对象,那么这个Handler绑定到了创建这个线程的线程上,消息处理回调也就在创建线程中执行.
private class MyThread extends Thread { public void run() { Looper.prepare(); private Handler myHandler = new Handler() { public void handleMessage(Message msg) { ... }
Looper.loop();
} } }
这样定义了之后,如果在主线程中new MyThread(), MyThread中的Handler就会和主线程中的looper绑定在一起了,理所当然的handlerMessage()方法也会在主线程中执行,再来控制UI界面就没问题了。
下面有个小例子,用定时任务创建的线程定时任务(其中有子线程)向主线程的Handler发送消息,主线程来进行处理。
public class MainActivity extends Activity { int[] imageIds = new int[]{ R.drawable.ic_launcher,R.drawable.e1,R.drawable.e2, R.drawable.e3,R.drawable.e4,R.drawable.e5, R.drawable.e6 }; int currentImageId = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final ImageView imageView = (ImageView)findViewById(R.id.imageview); final Handler myHandler = new Handler(){ @Override public void handleMessage(Message msg) {
//接受消息并更换图片 if (msg.what == 0x1233) { imageView.setBackgroundResource(imageIds[currentImageId++ % imageIds.length]); } } }; new Timer().schedule(new TimerTask() { @Override public void run() {
//向主线程中Handler实例发送消息 myHandler.sendEmptyMessage(0x1233); } },0,1200); }}
Handler及Loop、MessageQueue之间的合作关系
上面简单的介绍了Handler及其用法之后,下面来介绍下和Handler一起工作的几个组件:
1.Message: Handler接受和处理的消息对象。
2.Looper: 每个线程中只能拥有一个Looper,但是一个Looper可以和多个线程的Handler绑定起来,也就是说很多个线程可以往一个Looper所持有的MessageQueue中发送消息。这就给我们提供了线程之间通信的可能。
3.MessageQueue : 消息队列,采用着先进先出的管理方式,管理Message。
为什么说Looper持有MessageQueue呢?让我们来看看内部的实现:
<span style="font-size:14px;">private Looper () {mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread();}</span>
在初始化Looper的时候,里面会关联着一个MessageQueue实例,这个Messagequeue也就是在线程发送的消息的容器。仔细观察上面的代码,发现Looper是private的,这就说明了我们不能用构造方法来创建Looper对象,那该怎么弄呢?由于和线程扯上了关系,我们不得部分下面两种情况来考虑了:
1.在主线程(UI线程)中,系统在一个应用开始的时候就已经帮我们初始化好了属于主线程的Looper,我们直接可以在主线程中实例化一个Handler对象,使用即可:
<span style="font-size:14px;">Handler myHandler = new Handler(){ @Override public void handleMessage(Message msg) { if (msg.what == 0x1233) { System.out.println("hello world~~~"); } } };</span>
2.第二种情况就是在我们自己启动的子线程中了,自己创建的东西由于不是亲生的(系统自动生成并启动的),所以你懂的,只能靠自己来弄Looper对象了,好在Looper类留了一个静态的方法prepare()供我们使用,可以调用这个方法来创建Looper对象:
<span style="font-size:14px;">public static final void prepare() {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper());}</span>
那么还没完呢,有了这个Looper对象之后,怎么让它不断地从自己的MessageQueue中取出消息并传递给绑定了的Handler上面呢?Looper中还有一个死循环的函数,loop(),一旦开始,就会不停的从MessageQueue中取出消息并交给绑定了的Handler处理。网上找了找,下面贴上实现的代码:
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static final void loop() { Looper me = myLooper(); // 线程中存储的Looper对象 MessageQueue queue = me.mQueue; // Looper类中的消息队列 while (true) { Message msg = queue.next(); // might block 获取消息 //if (!me.mRun) { // break; //} if (msg != null) { if (msg.target == null) { // No target is a magic identifier for the quit message. return; } if (me.mLogging!= null) me.mLogging.println( ">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what ); msg.target.dispatchMessage(msg); // 利用 Target 注册的方法处理消息 if (me.mLogging!= null) me.mLogging.println( "<<<<< Finished to " + msg.target + " " + msg.callback); msg.recycle(); } } }
到这里差不多基础的东西就介绍完了,其是我知道的也就这么多了,,全部都呈上来了,下面总结下关于Handler的一些说法:
1.一个线程只能有一个looper,或者没有looper,这个looper可以是自己通过Looper.prepare()创建出来的,也可以是通过Looper.getMainLooper()得到的.
2.一个Handler只能和一个Looper绑定起来,但是一个Looper可以被多个Handler来绑定,共同使用里面的消息队列.并不会出现冲突的情况,谁发的消息,到时候就该谁拿走.并且处理掉.
3.一个Thread能够有多个Handler,这些Handler如果在创建的时候没有指定Looper,那么就是用的该Thread的Looper,如果指定了Looper,发送的消息将送到指定的looper里面.
4.Handler它用的Looper之间没有任何的瓜葛,只是借用了一下Looper而已,在用mHandler.sendEmptyMessage()的时候,发送到哪个Looper的MessageQueue,到时候就从那个MessageQueue中取出.
5.子线程中想控制UI的话,如果子线程和主线程在同一个文件下(一般都是这样子),直接对主线程的Handler发送消息,如果不在同一个文件夹下,那就将主线程的Handler作为参数传入.好像没有其它的办法,因为即使用同一个Looper,也不能处理其他Handler发送过来的消息,所以在子线程中用Handler发送消息,主线程上的Handler是无法处理的.关于这方面的知识,我还会整理之后和大家分享的,其实在handler向Looper发送消息的时候,传过去了一个target,用来指名这条消息归哪个handler处理的.
今天的博客就到这里咯,如果有什么不对的地方还望各位指出,今天捋Handler的知识把自己都弄晕了......23333
- Android Handler简介及其用法
- Android 系统Handler用法简介
- Android 系统Handler用法简介
- Android 系统Handler用法简介
- Handler及其用法
- Handler-简介和用法
- Handler用法简介
- Android - Handler的简介
- android Handler简介
- Android中Handler简介
- Android Handler的用法
- Android handler 用法总结
- android之Handler用法
- android Handler的用法
- Android Handler用法
- android handler用法
- android Handler用法
- android Handler用法
- 维也纳大学LTE链路仿真工具使用方法
- 9.6、Libgdx之罗盘
- 重定向(redirect)和转发(forward)区别
- 或者字段修改符
- 实例分析ELF文件静态链接
- Android Handler简介及其用法
- UML用例图总结
- yii2 console的使用
- 孩子与软件
- Java事务处理全解析(五)—— Template模式
- JavaScript
- java 日期处理工具类 DateUtil 备忘笔记
- java 文件读写
- 国家科技支撑计划项目申请书思路总结