Handler和HandlerThread

1.什么是Handler?

SDK中关于Handler的说明例如以下:

A Handler allows you to sendand process Messageand Runnable objects associated with a thread's MessageQueue.Each Handler instance is associated with a single thread and that thread'smessage queue. When you create a new Handler, it is bound to the thread /message queue of the thread that is creating it -- from that point on, it willdeliver messages and runnables to that message queue and execute them as theycome out of the message queue.

1.1Handler的作用

There are two main uses for aHandler: (1) to schedule messages and runnables to be executed as some point inthe future; and (2) to enqueue an action to be performed on a different threadthan your own.

1.1.1发送和处理消息

以下的代码对于我们来说又是多么的常见:

首先在Activity中创建一个继承自Handler的匿名内部类以及这个类的一个对象

Private MainHandler mMainHandler = new MainHandler();

private class MainHandler extends Handler {

    public voidhandleMessage(android.os.Message msg) {

        switch (msg.what) {

        case MSG_MAIN_HANDLER_TEST:

            Log.d(TAG, "MainHandler-->handleMessage-->thread id =" +     Thread.currentThread().getId());

            break;

        }

    }

 };

这样在Activity的其它地方就能够通过mMainHandler对象发送消息给Handler处理了

Message msg = mMainHandler.obtainMessage(MSG_MAIN_HANDLER_TEST);

        mMainHandler.sendMessage(msg);

【疑问】这里的消息是如何交给handler处理的,另外当有多个消息的时候处理的先后顺序呢?(以下会进行解答)

1.1.2处理runnables方法中的代码

除了上述的使用Handler发送以及处理消息外,handler另一个作用就是处理传递给它的action对象,详细使用步骤演示样例:

1、在主线程中定义Handler对象

2、构造一个runnable对象,为该对象实现runnable方法。

3、在子线程中使用Handler对象post(runnable)对象.

 

handler.post这个函数的作用是把Runnable里面的run方法里面的这段代码发送到消息队列中,等待执行。
假设handler是以UI线程消息队列为參数构造的,那么是把run里面的代码发送到UI线程中,等待UI线程执行这段代码。
假设handler是以子线程线程消息队列为參数构造的,那么是把run里面的代码发送到子线程中,等待子线程执行这段代码。

 

Runnable 并不一定是新开一个线程,比方以下的代码中就是执行在UI主线程中的:

 

public class TestActivity extends Activity implementsOnClickListener {

    /** Calledwhen the activity is first created. */

   

    private Button mBtnTest=null;

    private Handler myHandler=null;

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

       

        mBtnTest=(Button)findViewById(R.id.btn_test);

        mBtnTest.setOnClickListener(this);

       

        myHandler=new Handler();

    }

    @Override

    public void onClick(View v) {

        //注意:此处UI线程被堵塞,由于myHandler是在UI线程中创建的

       myHandler.post(new Runnable() {

           public void run() {

                long i=0;

                while(true){

                i++;

                }

              }

           });

    }

}

官方对这种方法的解释例如以下,注意当中的:“The runnable will be run on the userinterface thread. ”

 

booleanandroid.view.View .post(Runnable action)

Causes the Runnableto be added to the message queue. The runnable will be run on the userinterface thread.

Parameters:

action The Runnablethat will be executed.

Returns:

Returns true if theRunnable was successfully placed in to the message queue. Returns false onfailure, usually because the looper processing the message queue is exiting.

 

这里我们看代码 mHandler.post(new Runnable(){  好像是new 了一个 interface, 事实上是new的一个实现Runnable的匿名内部类(Inner Anonymous Class),这是非常简练的写法。

上面的代码能够看成是: new anonymousClass() implement interface{ [改写interface method]}

Runnable是一个接口,不是一个线程,一般线程会实现Runnable。所以假设我们使用匿名内部类是执行在UI主线程的,假设我们使用实现这个Runnable接口的线程类,则是执行在相应线程的。

【疑问】handler的消息发送与Post方法有什么差别呢?

     Handler对象消息发送sendXXXX相关方法例如以下,同一时候还有postXXXX相关方法, 这些和Win32中的道理基本一致,一个为发送后直接返回,一个为处理后才返回。

 

1.1.3线程间通信

    以下代码中红色标注处即为在MyThread这个线程中,通过UI线程中的handler向UI线程发送消息。

  定义一个线程

  class MyThread extends Thread{

               Handler   mHandler;

               Boolean  boo;

               publicMyThread(Handler handler){

               mHandler = handler;

               }

               publicvoid setBoo(boolean b) {boo = b; }

               publidvoid run(){

                                          if(boo){

                                                         getWeatherInfo();//耗时操作

                                                                                   analyzing();//耗时操作

                                                                                   mHandler.post(newRunnable() {

                                                                                                              publicvoid run() {

                                       setWeather();//更新UI

                                        });//更新UI

                   boo = true;

              }

       }

}

 

在处理单击事件时:

sureButton.setOnClickListener(newButton.OnClickListener(){

                                          publicvoid onClick(View view){

                      setBoo(true);

                                                    myThread.start();

                                          }

                            });

在activity中:

   MyThread myThread = new MyThread(mHandler);

当中mHandler为在UI线程中创建的。

 

1.2职责与关系

熟悉Windows编程的朋友可能知道Windows程序是消息驱动的,而且有全局的消息循环系统。而Android应用程序也是消息驱动的,按道理来说也应该提供消息循环机制。实际上谷歌參考了Windows的消息循环机制,也在Android系统中实现了消息循环机制。 Android通过LooperHandler来实现消息循环机制,Android消息循环是针对线程的(每一个线程都能够有自己的消息队列和消息循环)。

什么是looper?以下就简单的介绍下各个对象的职责:

Message:消息,当中包括了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。

Handler:处理者,负责Message的发送及处理。使用Handler时,须要实现handleMessage(Messagemsg)方法来对特定的Message进行处理,比如更新UI等。

MessageQueue:消息队列,用来存放Handler发送过来的消息,并依照FIFO规则运行。当然,存放Message并不是实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。

Looper:消息泵,不断地从MessageQueue中抽取Message运行。因此,一个MessageQueue须要一个Looper

Thread:线程,负责调度整个消息循环,即消息循环的运行场所。

HandlerLooperMessageQueue就是简单的三角关系。LooperMessageQueue一一相应,创建一个 Looper的同一时候,会创建一个MessageQueue。而Handler与它们的关系,仅仅是简单的聚集关系,即Handler里会引用当前线程里的特定LooperMessageQueue

这样说来,多个Handler都能够共享同一LooperMessageQueue了。当然,这些Handler也就执行在同一个线程里。

【疑问】我们写的Activity中没有看见looper,也没有看见什么Thread去调度整个消息循环?

Activity是一个UI线程,执行于主线程中,Android系统在启动的时候会为Activity创建一个消息队列和消息循环(Looper),具体实现请參考ActivityThread.java文件。

1.3怎样实现消息的发送与处理

接下来,我们简单地看一下消息的循环过程:

1.3.1消息的生成

       Message msg =mHandler.obtainMessage();

       msg.what = what;

       msg.sendToTarget();

1.3.2消息的发送

       MessageQueue queue= mQueue;

        if (queue != null){

            msg.target =this;

            sent =queue.enqueueMessage(msg, uptimeMillis);

        }

Handler.javasendMessageAtTime(Messagemsg, long uptimeMillis)方法中,我们看到,它找到它所引用的MessageQueue,然后将Messagetarget设定成自己(目的是为了在处理消息环节,Message能找到正确的Handler),再将这个Message纳入到消息队列中。

1.3.3消息的抽取

        Looper me =myLooper();

        MessageQueue queue= me.mQueue;

        while (true) {

            Message msg =queue.next(); // might block

            if (msg !=null) {

                if(msg.target == null) {

                    // Notarget is a magic identifier for the quit message.

                   return;

                }

                msg.target.dispatchMessage(msg);

               msg.recycle();

            }

        }

Looper.javaloop()函数里,我们看到,这里有一个死循环,不断地从MessageQueue中获取下一个(next方法)Message,然后通过Message中携带的target信息,交由正确的Handler处理(dispatchMessage方法)。

1.3.4消息的处理

        if (msg.callback!= null) {

           handleCallback(msg);

        } else {

            if (mCallback!= null) {

                if(mCallback.handleMessage(msg)) {

                    return;

                }

            }

           handleMessage(msg);

        }

Handler.javadispatchMessage(Messagemsg)方法里,当中的一个分支就是调用handleMessage方法来处理这条Message,而这也正是我们在职责处描写叙述使用Handler时须要实现handleMessage(Messagemsg)的原因。

至于dispatchMessage方法中的另外一个分支,我将会在后面的内容中说明。

至此,我们看到,一个Message经由Handler的发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler的怀抱。而绕的这一圈,也正好帮助我们将同步操作变成了异步操作。

1.4在工作线程(WorkerThread)中使用Handler

【疑问】上面介绍了怎样在主线程中使用handler以及handler发送和处理消息的详细原理,而且SDK中已经提到能够在自己定义的线程中使用对应的handler来处理消息,这个怎样实现呢?

通用的作法是:

       class LooperThreadextends Thread {

                publicHandler mHandler;

                   publicvoid run() {

                      Looper.prepare();

                       mHandler= new Handler() {

                           public void handleMessage(Message msg) {

                           // process incoming messages here

                            }

                        };

                       Looper.loop();   //不能在这个后面加入�代码,程序是无法执行到这行之后的

                   }

      }

在创建Handler之前,为该线程准备好一个LooperLooper.prepare),然后让这个Looper跑起来(Looper.loop),抽取Message,这样,Handler才干正常工作。

1.5不是全部的Handler都能更新UI

    Handler处理消息总是在创建Handler的线程里执行而我们的消息处理中,不乏更新UI的操作,不对的线程直接更新UI将引发异常。因此,须要时刻关心Handler在哪个线程里创建的。怎样更新UI才干不出异常呢?SDK告诉我们,有下面4种方式能够从其他线程訪问UI线程(也即线程间通信)

·      Activity.runOnUiThread(Runnable)

·      View.post(Runnable)

·      View.postDelayed(Runnable, long)

·      UI线程中创建的Handler

当中,重点说一下的是View.post(Runnable)方法。在post(Runnableaction)方法里,View获得当前线程(即UI线程)的Handler,然后将action对象postHandler里。在Handler里,它将传递过来的action对象包装成一个MessageMessagecallbackaction),然后将其投入UI线程的消息循环中。在 Handler再次处理该Message时,有一条分支(未解释的那条)就是为它所设,直接调用runnablerun方法。而此时,已经路由到UI线程里,因此,我们能够毫无顾虑的来更新UI

几点小结

·      Handler的处理过程执行在创建Handler的线程里

·      一个Looper相应一个MessageQueue,一个线程相应一个Looper,一个Looper能够相应多个Handler

·      不确定当前线程时,更新UI时尽量调用View.post方法

·      handler应该由处理消息的线程创建。

·      handler与创建它的线程相关联,并且也仅仅与创建它的线程相关联。handler执行在创建它的线程中,所以,假设在handler中进行耗时的操作,会堵塞创建它的线程

·      Android的线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个Looper。主线程(UI线程)就是一个消息循环的线程。

·      Looper.myLooper();      //获得当前的Looper

        Looper.getMainLooper() //获得UI线程的Lopper

·      Handle的初始化函数(构造函数),假设没有參数,那么他就默认使用的是当前的Looper,假设有Looper參数,就是用相应的线程的Looper

·      假设一个线程中调用Looper.prepare(),那么系统就会自己主动的为该线程建立一个消息队列,然后调用Looper.loop();之后就进入了消息循环,这个之后就能够发消息、取消息、和处理消息。

2.HandlerThread

在上面的总结中指出,Android的线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个Looper。其实Android提供了一个封装好的带有looper的线程类,即为HandlerThread,详细可參见以下的代码:

public class HandlerThreadActivity extends Activity {

    private static final String TAG = "HandlerThreadActivity";

    private HandlerThreadmHandlerThread;

    private MyHandler mMyHandler;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

       // TODO Auto-generatedmethod stub

       super.onCreate(savedInstanceState);

       TextView text = new TextView(this);

       text.setText("HandlerThreadActivity");

       setContentView(text);

      

       Log.d(TAG, "The mainthread id = " +      Thread.currentThread().getId());

      

       //生成一个HandlerThread对象,实现了使用Looper来处理消息队列的功能,

       //这个类由Android应用程序框架提供

       mHandlerThread = new HandlerThread("handler_thread");

      

       //在使用HandlerThreadgetLooper()方法之前,必须先调用该类的start();

       mHandlerThread.start();

       //即这个Handler是执行在mHandlerThread这个线程中

       mMyHandler = new MyHandler(mHandlerThread.getLooper());

      

       mMyHandler.sendEmptyMessage(1);

    }

   

    private class MyHandler extends Handler {

      

       public MyHandler(Looper looper) {

           super(looper);

       }

 

       @Override

       public void handleMessage(Message msg) {

           Log.d(TAG, "MyHandler-->handleMessage-->threadid = " + Thread.currentThread().getId());

           super.handleMessage(msg);

       }

    }

   

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/298980.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

amos里CFA可行性辨别结果怎么看_论文用问卷调查法,数据分析怎么做?

论文问卷数据的分析,看起来简单,好像每个人都会做。但是做起来还真的有点难度。很多初次使用问卷调查方法的人大多以为,问卷数据分析嘛,无外乎对单选题做做频率分析,看看选择不同的选项的人占比有多少。对于评分题目&a…

转:asp.net 负载平衡-Session相关

来自:http://www.cnblogs.com/zxylonely/archive/2009/12/23/1630197.htmlhttp://blog.csdn.net/lvlingwy/archive/2008/05/08/2418673.aspxhttp://bbs.ibeifeng.com/simple/index.php?t16926.html转自:http://sai5d.blog.sohu.com/131936651.html在WEB…

Hello Blazor:(10)按需加载JavaScript脚本

前言Blazor支持执行JavaScript脚本,通常是将脚本放在wwwroot/index.html(Blazor WebAssembly)或Pages/_Host.cshtml(Blazor Server)中。但是,这种方式会将所有JS方法用全局函数加载,即使某些方法只需要在特定组件中使用。既影响加载性能&…

只有学霸才懂的学习技巧,看完脑洞大开,绝对涨姿势!

▲ 点击查看法国生物学家乔治.居维叶曾说:“天才,首先是注意力。”事实上,除开那些逆天的天才之外,大部分人的智商,在先天因素的决定下,都是差不多的。但是专注力却可以因为后天的刻意训练,而有…

DOM Xerces类库使用方法

DOM Xerces类库使用方法Tuxedo中XML的历史  如 今随着XML逐渐成为主流的数据格式之一,自然而然地 Tuxedo将之作为一种基本缓冲类型予以支持。    Tuxedo 7.1 引入了XML缓冲类型,但迄今为止对于Tuxedo中的XML并无较多的论述。Tuxedo 7.1中并未携带真…

解析邻居的耳朵音乐地址(单页下载)

偶尔听歌的时候发现了一个很小众的音乐分享网站,邻居的耳朵,有个人的电台类型,属于音乐分享文字分享,觉得很不错。从域名来看,属于多米旗下的网站:http://ear.duomi.com/ 看来多米收购了很多这样的小站&am…

为什么电脑不能打字_嘉兴在线丨「生活经济学」为什么笔记本电脑能在任何国家的供电标准下运作,其他大部分电器却不能?...

【生活热搜】从日本背回来的电饭煲煮饭口感总是不对,用几天就坏了;英国邮回来的吹风机怎么都插不上电源只能闲置……如果你有从国外买家用电器的经历,你一定会发现很多电器因为每个国家或地区的供电标准不同而无法正常使用的情况。但笔记本电…

【DotNetMLLearn】.NET Core 人工智能系列-.NET Interactive环境介绍

在进入.NET Core 的人工智能应用开始前,我们先把环境搭建好,为以后的学习提供一个便利。作为一个.NET 程序员,或者其他编程语言的程序员,相信对IDE的依赖必不可少,如Visual Studio/Visual StudioCode , IntelliJ , Ecl…

php curl https 443,PHP CURL支持HTTP、HTTPS 请求亲测可用

废话就不多说 直接上代码,亲测好用原生:/*** curl发送htpp请求* 可以发送https,http,get方式,post方式,post数据发送*/public function dataRequest($url,$httpstrue,$methodget,$datanull){//初始化curl$ch curl_init($url);//字符串不直接输出&#x…

在程序员的道路上,义无反顾的努力,有思想的人,很多,好的想法,需要学习。(以此共勉)...

程序员:差距在哪里,你与开发高手的距离到底有多远?投递人 itwriter 发布于 2010-08-27 13:45 评论(19) 有5369人阅读 [收藏]开发者的技术成长是个永恒的话题。《如何成为一个专家级的开发者》文中虽然界定了专家的概念和开发者成长为专家级的…

2020届的毕业生怎么这么苦!

一提到毕业论文我们身上的每个细胞都在告诉自己这件事太麻烦了!每天要思考着选题、开题报告、查资料写稿、排版、答辩、做PPT......除了8000到20000的字数要求完成一篇毕业论文,要依次经历确定论文选题,提交开题报告查阅行业文献,…

线切割机上的DOS系统

昨天朋友的线切割机上的系统无法启用了。于是朋友叫我过去看看。哈,原来是DOS系统加上线切割机控制软件开机提示HIMEM.SYS未加载,所以EMM386.EXE未能启动。机器是很老的机器了,当然了配置也不需要太高了。因为这样的东西没有加载成功&#xf…

RMSE均方根误差学习笔记

1.均方根误差,它是观测值与真值偏差的平方和观测次数n比值的平方根,在实际测量中,观测次数n总是有限的,真值只能用最可信赖(最佳)值来代替.方根误差对一组测量中的特大或特小误差反映非常敏感,所…

java小数强制转换,Java中的强制类型转换 大数转小数

首先要明确一下转换规则:大数转小数,多出的高位部分会被截断。比如 int 占 4个byte(32 bit), byte占 1个byte(8bit), 那int 转 byte ,int 高位多出的那24个bit会被截断。例1:int b 233; // 正整数强转System.out.println((byte)b);// 原码&a…

js 定时网页点击_反爬 JS 逆向,扣代码解密分析

挺久没发爬虫相关的教程啦,今天给大伙分享一下关于网站反爬对请求参数值的加密分析例子,主要还是看看思路。定位加密点在某网站中进行登录请求:简单抓下包,点击登录按钮之后,可以在浏览器的控制台中看到相关的请求&…

这个时代最重要的技能之一(数据分析)

大家好,我是Z哥。首先说明一下,今天不卖课程哈,就单纯聊聊我在做数据分析时的一些经验。在如今这个数据爆炸的时代,我们每天不管是主动还是被动,都会面对大量的数据扑面而来。如果有较好的数据分析能力,不管…

ADO.NET Entity Framework学习笔记(2)建模[转]

模型结构 [概念模型]中要有[实体键], [实体键]要与表中的 [主键] 对应,也就是说表必须要有主键. 表中的[唯一键]不会在[概念模型]中体现 在[概念模型]中默认是不允许修改[实体键]的值的 联合主健可以正常映射 如果为属性赋值超过字段长度保存时,会向数据库提交,数据库会返回错…

教你怎么一下哄好赌气的女朋友​

1 教你怎么一下哄好赌气的女朋友▼2 有钱人的隔离生活也格外丰富多彩▼3 狗:有一种被掏空的感觉▼4 湘西赶尸的原理找到了!▼5 泰罗奥特曼在打奥特曼你先去找迪迦奥特曼玩▼6 看起来就很值钱的哟而且还是微波炉专用哦!▼7 看完了有点恐…

最受欢迎中国技术博客? 我才狂写4周唉

今天察看了一下blog的访问来源,居然发现有很多点击来自同一个URL:http://blog.zdnet.com.cn/popblogger50.shtml。进去一看,发现是一个‘最受欢迎中国技术博客’评选活动。这让我很惊讶,因为这个Blog1)放得很冷僻&…

Linux挂载命令mount详解

Mount挂载命令使用方法语法: mount -t 类型 -o 挂接方式 源路径 目标路径-t 详细选项:光盘或光盘镜像:iso9660DOS fat16文件系统:msdosWindows 9x fat32文件系统:vfatWindows NT ntfs文件系统:ntfsMount Windows文件网…