在安卓中,在不同的线程间通信,可是使用Handler来轻松实现,本文就详细的介绍Handler处理消息的原理。

1,Message

讲Handler机制,我们就不得不涉及到一个很重要的对象Message,Handler使用Message对象作为传输对象,所以Message对象中,可以携带一些对象,直接在另一个线程(handler对象所绑定的线程)中去处理。一个典型的案例就是我们在获取网络图片时必须单开线程,而后通过Handler发送一个Message通知UI线程去更新图片,这里就可以将得到的bitmap作为对象放在Message中,在UI线程中去得到这个bitmap,然后显示在ImageView控件上。

public class MainActivity extends Activity {String m_image_url = "https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superplus/img/logo_white_ee663702.png";private Handler m_handler;private ImageView m_imageView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);m_imageView = (ImageView) findViewById(R.id.imageview_temp);//在UI线程中new Handler()这样,处理消息时,也会在主线程中m_handler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {if (msg.what == 0x11) {//获得非UI线程传递过来的BitmapBitmap bitmap = (Bitmap) msg.obj;if (m_image_url != null) {m_imageView.setImageBitmap(bitmap);}}return true;//表示拦截消息事件,不会下发到handler的handerMessage()了}});}//按钮的触发事件public void click_fetch_image(View view) {new Thread(new Runnable() {@Overridepublic void run() {try {
//                    请求网络,注意添加网络权限URL url = new URL(m_image_url);HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.setDoInput(true);connection.connect();InputStream inputStream = connection.getInputStream();Bitmap bitmap = BitmapFactory.decodeStream(inputStream);if (bitmap != null) {
//                        得到一个Message对象的方法,官方推荐1,2方法
//                        1 new Message();2 Message.obtain();3 handler.obtainMessage();
//                        Message message = Message.obtain();Message message = m_handler.obtainMessage();
//                        设置message传递的信息
//                        2个int类型 message.arg1 .arg2 一个obj类型 message.objmessage.what = 0x11;message.obj = bitmap;
//                        发送消息给指定的handler
//                        1 通过Message自己发送message.setTarget(m_handler);message.sendToTarget();
//                        2 通过Handler发送
//                        m_handler.sendMessage(message);Log.d("", "子线程消息发送成功!");}else {Log.d("", "网络图片返回为null");}} catch (Exception e) {e.printStackTrace();}}}).start();}
}

代码中已经讲得很清楚怎么去获取一个Message对象,如果给利用message传递参数,如何将一个通过handler发送message.当然messge还有一些其他知识点,这里就先不讲。

2,Looper

第二个重要的类,当然是Looper了。
Looper其实就是一个在线程的处理MessageQueue的一个类,注意这里的线程是任意线程,不要被UI线程中存在looper就以为它只能在UI线程中存在,只是当运行应用时,系统会默认的在UI线程中去创建Looper而已。一个线程中只能有一个looper去处理消息(其实是分发消息给handler去处理)。想要在子线程中使用handler机制,就必须在子线程中创建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();}}

跟进Looper源码中看到Lopper.prapare()最终调用了

    private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}

它的私有构造方法,去创建一个MessageQueue,并绑定当前线程,此时我们线程中就有了looper对象了(或许应该反过来)。

看下looper.loop()做了哪些事情:

    /*** Run the message queue in this thread. Be sure to call* {@link #quit()} to end the loop.*/public static void loop() {final Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}final MessageQueue queue = me.mQueue;// Make sure the identity of this thread is that of the local process,// and keep track of what that identity token actually is.Binder.clearCallingIdentity();final long ident = Binder.clearCallingIdentity();for (;;) {Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;}// This must be in a local variable, in case a UI event sets the loggerPrinter logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}msg.target.dispatchMessage(msg);if (logging != null) {logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);}// Make sure that during the course of dispatching the// identity of the thread wasn't corrupted.final long newIdent = Binder.clearCallingIdentity();if (ident != newIdent) {Log.wtf(TAG, "Thread identity changed from 0x"+ Long.toHexString(ident) + " to 0x"+ Long.toHexString(newIdent) + " while dispatching to "+ msg.target.getClass().getName() + " "+ msg.callback + " what=" + msg.what);}msg.recycleUnchecked();}}

在第6行,得到当前线程的looper
第10行,得到消息队列,
从第17行开始是一个执行死循环,
18行从消息队列里获得消息,
31行调用handler(message.target就是message绑定的handler)的dispatchMessage()方法,对msg处理message消息

3,Handler

终于言归正传了,因为一般我们都是在UI线程中使用Handler所以这里只讲UI线程中的Handler的用法。

创建一个Handler

先创建一个Handler,我们一般声明的时候就创建,或者在Activity的onCreate()中创建:

Handler handler = new Handler();//这个handler就不具有处理消息的能力
Handler handler= new Handler(new Handler.Callback() {//这个handler可以处理消息,callback方式@Overridepublic boolean handleMessage(Message msg) {//use msg codereturn true;}});
Handler handler= new Handler(){//这个handler可以处理消息,匿名内部类方式@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);}};

Handler的两个最大的用处就是1,在另一个线程中发送message,或者runnable将他们入队(MessageQueue);2,处理message或者runnable。

使用handler发送消息

1,Message方式
前面讲Message时,讲过了,就是调用m_handler.sendMessage()方法发送消息
2,Runnable方式
如果不需要传递数据给另一个线程,只需要通知handler线程执行某个方法,比如切换一个Button的状态,这时候,就应该用Runnable方式:

        new Handler().post(new Runnable() {@Overridepublic void run() {//handler所在线程执行的代码//code}});

前面去面试一家公司,面试官问我:handler发送消息的Message和Runnable方式有什么区别?苦逼的我当时没研究过handler源码,没答上来。
现在分析一下,就应该去看一下这个post()方法怎么去处理这个Runnable的,先看一下handler的post()方法

    public final boolean post(Runnable r){return  sendMessageDelayed(getPostMessage(r), 0);}

看到这个方法名,就有点懂了,其实还是去发送了个Message消息,因为这个sendMessageDelayed()的第一个参数是一个Message对象,下面跟进getPostMessage()

    private static Message getPostMessage(Runnable r) {Message m = Message.obtain();m.callback = r;return m;}

看到这里就明白了,当使用Runnable形式发送消息时,也是发送一个Message对象,只是记录在了message.callback中。

使用Handler处理消息

在处理之前,先看一下,handler把是如何把消息发送出去,然后消息又回到handler中去处理的

    Class Handlerpublic final boolean sendMessage(Message msg){return sendMessageDelayed(msg, 0);}public final boolean sendMessageDelayed(Message msg, long delayMillis){if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}public boolean sendMessageAtTime(Message msg, long uptimeMillis) {/** mQueue为Looper中的MessageQueue */MessageQueue queue = mQueue;if (queue == null) {RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");Log.w("Looper", e.getMessage(), e);return false;}return enqueueMessage(queue, msg, uptimeMillis);}private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {/** 设置当前消息的handler对象 */msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}/** messageQueue将msg加入队列 */return queue.enqueueMessage(msg, uptimeMillis);}
Class MessageQueueboolean enqueueMessage(Message msg, long when) {/** 没有找到发送者(handler) */if (msg.target == null) {throw new IllegalArgumentException("Message must have a target.");}if (msg.isInUse()) {throw new IllegalStateException(msg + " This message is already in use.");}synchronized (this) {if (mQuitting) {IllegalStateException e = new IllegalStateException(msg.target + " sending message to a Handler on a dead thread");Log.w("MessageQueue", e.getMessage(), e);msg.recycle();return false;}msg.markInUse();msg.when = when;Message p = mMessages;boolean needWake;if (p == null || when == 0 || when < p.when) {// New head, wake up the event queue if blocked./** 当前没有消息处理,将msg加入队列 */msg.next = p;mMessages = msg;needWake = mBlocked;} else {// Inserted within the middle of the queue.  Usually we don't have to wake// up the event queue unless there is a barrier at the head of the queue// and the message is the earliest asynchronous message in the queue.needWake = mBlocked && p.target == null && msg.isAsynchronous();Message prev;for (;;) {prev = p;p = p.next;if (p == null || when < p.when) {break;}if (needWake && p.isAsynchronous()) {needWake = false;}}msg.next = p; // invariant: p == prev.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;}

通过分析Handler和MessageQueue的源码,可以发现,Handler发送一条消息之后,是加入到Looper的MessageQueue中的,这样Looper.loop()就能实时的从MessageQueue中去获得消息了,通过调用msg.target.dispatchMessage(msg);将当前的msg消息发送给原来的发送者target来处理。
现在message又回到了Handler中,分析一下handler.dispatchMessage():

public void dispatchMessage(Message msg) {if (msg.callback != null) {/** 表明使用Runnable方式发送 */handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {/** 如果存在callback拦截消息 ,并返回true */return;}}/** 这就是我们常重写的handlerMessage() */handleMessage(msg);}}

到这里,就恍然大悟了,msg已经到我们的handlerMessage()碗里来了。不过还是要看一下handlerCallBack()是怎么处理Runnable的

    private static void handleCallback(Message message) {message.callback.run();}

很简单的一行代码,就是去跑一个run()方法,而不是start()。
这里分析一下,线程中绑定的是Looper对象,也就是说当前线程一直有一个looper在轮询消息,当looper轮询到一条消息时,就通知handler去处理,注意这是在同一个线程中,也就是looper所在的线程中。handler如果去调用一个实现runnable接口的start()方法岂不是又新开启了一个线程了,所以这里直接调用了run()方法,在当前线程就执行就行了。

4,使用Handler完成定时任务

Handler除了上面提到的基本用法外,还有一个常用功能就是在UI线程执行一个定时的任务,比如让一个TextView自增长。

    private Handler m_handler;public void click_start(View view) {m_handler = new Handler();//发送一个runnable,延迟一秒执行m_handler.postDelayed(m_runnable, 1000);}public void click_cancel(View view){//从消息队列中移除runnablem_handler.removeCallbacks(m_runnable);}Runnable m_runnable = new Runnable() {int i = 1;@Overridepublic void run() {m_increment_textview.setText("" + i++);//在run()中又去发送一个runnable,就是一个循环调用的过程m_handler.postDelayed(this, 1000);}};

Android_Handler机制相关推荐

  1. activity 生命周期_死磕Android_App 启动过程(含 Activity 启动过程)

    1. 前言 Activity是日常开发中最常用的组件,系统给我们做了很多很多的封装,让我们平时用起来特别简单,很顺畅.但是你有没有想过,系统内部是如何启动一个Activity的呢?Activity对象 ...

  2. 死磕Android_App 启动过程(含 Activity 启动过程)

    1. 前言 Activity是日常开发中最常用的组件,系统给我们做了很多很多的封装,让我们平时用起来特别简单,很顺畅.但是你有没有想过,系统内部是如何启动一个Activity的呢?Activity对象 ...

  3. 2019 Android开发工程师面经

    文章目录 1. Java基础 1.1 什么是乐观锁? 1.2 volatile关键字 1.3 hashmap 原理,红黑树是什么? 1.4 jvm内存分配 1.5 String,StringBuffe ...

  4. 有人说这是2021字节跳动-初级Android工程师的面经?吓到我了!我还是去搬砖吧!

    我的情况 我在大三的时候,没有参加春招,也没有参加秋招,我大三 三月份的时候在实习僧上投了几个简历,发现面的都很水,原来我在大学的时候,成绩还算ok,编程能力在班里也是前几的,但是说实话,找工作经验真 ...

  5. 作者说这是初级Android工程师的面经?吓到我了!

    /   今日科技快讯   / 昨日,全国"携号转网"正式提供服务启动仪式在京举行.从今天开始,"携号转网"服务正式走出试点省份,与全国广大移动通信用户见面. / ...

  6. 我理解的Hanlder--android消息传递机制

    每一个学习Android的同学都会觉得Handler是一个神奇的东西,我也一样,开始我以为我懂了Handler的机制,后来发现自己是一知半解,昨天想想,我能否自己实现一个Handler,让子线程与Ac ...

  7. Go语言的错误异常处理机制及其应用

    一.背景 在日常编写golang程序或阅读别人的golang代码时,我们总会看到如下的一堆代码块: xx, err = func(xx) if err != nil {//do sth. to tac ...

  8. 2022-2028年中国机制砂石行业投资分析及前景预测报告

    [报告类型]产业研究 [报告价格]¥4500起 [出版时间]即时更新(交付时间约3个工作日) [发布机构]智研瞻产业研究院 [报告格式]PDF版 本报告介绍了中国机制砂石行业市场行业相关概述.中国机制 ...

  9. c#打开数据库连接池的工作机制_数据库连接池-tomcat-jdbc使用笔记

    现在 主流的数据库连接池有:Proxool.C3P0.DBCP.tomcat-jdbc.Druid.其中tomcat-jdbc是tomcat服务器比较可靠的 数据库连接池. Tomcat 在 7.0 ...

最新文章

  1. python使用matplotlib可视化、使用xcorr函数可视化两个变量的互相关图、使用acorr函数可视化自相关图像
  2. ssh升级后+sftp+java_java中使用JSCH包,SFTP及SSH2文件操作及远程命令执行(改进)...
  3. IE 下 href 的 BUG
  4. HTTP 方法:GET 对比 POST
  5. Linux的Nginx六:源码|安装
  6. [Qt教程] 第17篇 2D绘图(七)涂鸦板
  7. 第43讲:灵活好用的 Spider 的用法
  8. adf开发_了解ADF生命周期中的ADF绑定
  9. .net 笔记尝试(二)
  10. ipad怎么连接电脑_苹果连接电脑没反应怎么办
  11. python之路day2_Python之路,day2
  12. tomcat端口被占用了怎么办
  13. 实训41 2018.6.2
  14. Django JWT认证实现
  15. 【二叉树】剑指offer:二叉树中序遍历的下一个节点
  16. python如何测试rabbit_Python如何检测到我的RabbitMQ密码失败?
  17. Linkedin领英如何避免封号
  18. Oracle JDK 最新和旧版本下载地址
  19. webpack 图片压缩
  20. 怎样组织一次攻防演练比赛- 前期准备阶段

热门文章

  1. 查看gcc/g++版本以及对应默认的的C++标准
  2. 随心所欲----食堂点餐系统分析设计
  3. FLASH整站技术点分析和实践-第一期建站集体学习之一loadding
  4. Flex实现 WebQQ那白云草地主题,云朵飘!
  5. 各种消息下wParam及lParam值的含义
  6. css动画相关属性详解
  7. cvCvtColor函数
  8. Java通过JDBC连接MySQL
  9. 如何选择补丁管理工具
  10. 【华为认证5G工程师 HCIA-5G V2.0(中文版)发布通知】