【Android 学习】深入理解Handler机制
来源:互联网 发布:java二级联动下拉列表 编辑:程序博客网 时间:2024/05/18 14:23
Android 提供了Handler和Looper来来满足线程间的通信,而前面我们所说的IPC指的是进程间的通信。这是两个完全不同的概念。大家不要搞混了。
Handler先进先出原则,Looper类用来管理特定线程内消息的交换(MessageExchange);
我们了解Handler之前首先要知晓一下几点:
- Looper:一个线程有一个Looper,由Looper来管理当前线程的MessageQueue(消息队列),就是无限将消息队列里的取出,一个Handler必须绑定一个Looper。
- Handler:Handler的主要作用是将某一任务切换到特定的线程来执行。为什么要有这个机制昵?接下来会为大家介绍:
- MessageQueueu:消息队列用来存放线程发出的消息。
- Tread:线程通常是指UI线程也就是主线程,每个线程创建时都会为其创建MessageQueue。
1、为什么会有Handler机制
我们刚说Handler机制的主要作用是将某一任务切换到特定的线程来执行,我们做项目可能都遇到过ANR(Application Not Response),这就是因为执行某项任务的时间太长而导致程序无法响应。这种情况我们就需要将这项耗时较长的任务移到子线程来执行,从而消除ANR。而我们都知道Android规定访问UI只能在主线程中进行,如果在子线程中访问UI,那么程序就会抛出异常。而Android提供Handler就是为了解决在子线程中无法访问UI的矛盾。
2、demo
我们来先看一个简单的Demo吧!
MainActivity.java
package com.example.terry.hanlderdemo;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.widget.TextView;/** * Demo描述: * * 示例步骤如下: * 1 子线程给子线程本身发送消息 * 2 收到1的消息后,子线程给主线程发送消息 * 3 收到2的消息后,主线程给子线程发送消息 * * 为实现子线程给自己本身发送消息,关键还是在于构造Handler时传入的Looper. * 在此就传入该子线程自己的Looper即调用Looper.myLooper(),代码如下: * Looper.prepare(); * mHandlerTest1=new HandlerTest1(Looper.myLooper()); * Looper.loop(); * * 所以当mHandlerTest1.sendMessage(message);发送消息时 * 当然是发送到了它自己的消息队列. * * 当子线程中收到自己发送的消息后,可继续发送消息到主线程.此时只要注意构造 * Handler时传入的Handler是主线程的Handler即可,即getMainLooper(). * 其余没啥可说的. * * * 在主线程处理消息后再发消息到子线程 * * * 其实这些线程间发送消息,没有什么;关键还是在于构造Handler时传入谁的Looper. * */public class MainActivity extends Activity { private TextView mTextView; private HandlerTest1 mHandlerTest1; private HandlerTest2 mHandlerTest2; private int counter=0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } private void init() { mTextView = (TextView) findViewById(R.id.textView); //1 子线程发送消息给本身 new Thread() { public void run() { Looper.prepare(); mHandlerTest1=new HandlerTest1(Looper.myLooper()); Message message = new Message(); message.obj = "子线程发送的消息Hi~Hi"; mHandlerTest1.sendMessage(message); Looper.loop(); } }.start(); } private class HandlerTest1 extends Handler { private HandlerTest1(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); System.out.println("子线程收到:" + msg.obj); //2 收到消息后可再发消息到主线程 mHandlerTest2=new HandlerTest2(getMainLooper()); Message message = new Message(); message.obj = "O(∩_∩)O"; mHandlerTest2.sendMessage(message); } } private class HandlerTest2 extends Handler { private HandlerTest2(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); mTextView.setText("在主线程中,收到子线程发来消息:" + msg.obj); //3 收到消息后再发消息到子线程 if (counter==0) { Message message = new Message(); message.obj = "主线程发送的消息Xi~Xi"; mHandlerTest1.sendMessage(message); counter++; } } }}
3、Android 消息机制分析
3.1 TreadLocal的工作原理
- TreadLocal 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后只有在特定的线程中可以获取到存储的数据,对于其他线程来说则无法获取到。ThreadLocal用一句大白话来讲解,就是看上去只new了一份,但在每个不同的线程中却可以拥有不同数据副本的神奇类。其本质是ThreadLocal中的Values类维护了一个Object[],而每个Thread类中有一个ThreadLocal.Values成员,当调用ThreadLocal的set方法时,其实是根据一定规则把这个线程中对应的ThreadLocal值塞进了Values的Object[]数组中的某个index里。这个index总是为ThreadLocal的reference字段所标识的对象的下一个位置。
3.2 MessageQueue的工作原理
MessageQueue的工作原理:主要方法为enqueueMessage和next。
a. enqueueMessag主要就是一个单链表的插入操作,
b. next方法是一个无限循环,如果消息队列中没有消息,next方法就阻塞,有新消息到来时,next方法会返回这条消息并将其从单链表中删除。
3.3 Looper的工作原理:
a. prepare方法,为当前没有Looper的线程创建Looper。
b. prepareMainLooper和getMainLooper方法用于创建和获取ActivityThread的Looper。
c. quit和quitSafely方法,前者立即退出,后者只是设定一个标记,当消息队列中的所有消息处理完毕后会才安全退出。子线程中创建的Looper建议不需要的时候都要手动终止。
d. loop方法,死循环,阻塞获取msg并丢给msg.target.dispatchMessage方法去处理,这里的target就是handler。
3.4Handler的工作原理:
a. 无论sendMessage还是post最终都是调用的sendMessageAtTime方法。b. 发送消息其实就是把一条消息通过MessageQueue的enqueueMessage方法加入消息队列,Looper收到消息就会调用handler的dispatchMessage方法。它的处理过程参考流程图,一看就懂~
c. 这里我补充一个东西,当我们直接Handler h = new Handler()时,本质调用的是Handler(Callback callback, Boolean async)构造方法,这个方法里会调用Looper.myLooper()方法,这个方法其实就是返回的ThreadLocal里保存的当前线程的Looper,这也就解释了为什么我们在主线程中这样new没有问题,子线程中如果不先Looper.prepare会抛出异常的原因,前面多次说了,因为ActivityThread会在初始化的时候创建自己的Looper。
3.5主线程的消息循环:
- 【Android 学习】深入理解Handler机制
- 深入理解android Handler机制
- 深入理解Android中的Handler机制
- 带你深入理解Android Handler机制
- 带你深入理解Android Handler机制
- 深入理解Android中Handler机制
- 深入理解Android中的Handler机制
- 深入理解Android Handler 消息机制
- 深入理解Handler机制
- 深入理解handler机制
- 深入源码理解Handler机制
- Android消息机制——深入理解Handler
- Android 异步消息处理机制 深入理解 Looper、Handler、Message
- Android Handler机制理解
- Android学习札记26:深入理解Android中的消息处理机制——Thread、Looper、MessageQueue和Handler(1)
- Android学习札记28:深入理解Android中的消息处理机制——Thread、Looper、MessageQueue和Handler(2)
- 深入研究Android Handler机制
- 深入研究Android Handler机制
- 直观上理解PCA中特征值和特征向量
- 344. Reverse String #Easy
- Gson 过滤字段的几种方法
- adb命令大全
- ECharts 一个很好用的JS插件
- 【Android 学习】深入理解Handler机制
- C++变量的存储类别(动态存储、静态存储、自动变量、寄存器变量、外部变量)
- vi 查看文件 并 查找文字
- leetcode.229. Majority Element II
- 折线分割平面(图形递推题)
- openCV训练分类器是一些错误及解决办法
- 字符流中第一个不重复的字符
- 玩转ios友盟远程推送,16年5月图文防坑版
- IE浏览器因缓存问题未能成功向后端发送请求的几个解决办法