多线程开发(二)-Thread、 Looper与Handler关系解密

第3节 Handler

多个线程之间除了有“执行的同步”关系,还有“数据的共享”关系,以及“功能的委托”关系。

例如之前提到的视频信息显示到列表上,

  1. 委托数据查询功能:主线程启动一个工作线程thread-查询视频信息;
  2. 委托数据的界面更新功能:工作线程查询完成后,因为不能够修改界面元素,所以必须将结果通知到主线程,委托主线程将结果的结果显示到界面上。

为此,Android SDK提供了Handler帮助各个不同的线程之间传递数据或委托功能处理。

3.1 Thread、Looper与Handler的关系

一个线程创建以后,就会开始执行它Runnable中的run()方法。如果要这个线程一直运行而不退出的话,就要在里面设置一个循环,

Runnable runnable = new Runnable() {

    @Override
    public void run() {
        //开始循环
        while(xxx)
        {

        }
    }
};

Looper可以为Thread创建这样一个循环的环境,

@Override
public void run() {
    ......
    Looper.prepare();
    ......
    //相当于while()循环
    Looper.loop();
    ......
}

Looper具有一个消息队列,可以存放任何线程(包括它自己)给自己布置的任务。这些任务被一条一条的放在队列当中,在loop()函数中被取出来-执行,然后又取出来-执行,周而复始,永不停止。

public static void loop() {
    ......
    for (;;) {
        //从消息队列queue中取出任务
        Message msg = queue.next();
        //用消息提供的方法来处理每个消息蕴含的任务
        msg.target.dispatchMessage(msg);

        msg.recycleUnchecked();
    }
}

Handler是和Looper相关联的,通过Handler,任何线程可以把需要完成的任务放到Looper的消息队列里面。

Thread就好比一条产线,Looper中的消息队列就是这个流水线上的传送带,带子上分成了很多格,每一格放要处理的原料和处理这些原料的工人(原料和工人打包成了一个Message)。等轮到格子上的工人时,工人才能开始处理格子里放的原料。Handler就像是一个转运工具,提供给别的模块使用。这个工具可以把原料和工人放到产线的传送带上。

注意,一条产线只有一条传送带(Looper);但可以有多个为同一条产线提供转运服务的转运工具(Handler)。

所以使用这种产线的流程是,

  1. 创建一条产线A;
  2. 在这条产线A上创建一条传送带;
  3. 当别的模块B要向这条产线布置任务的时候,就要创建在产线A上工作的工人;
  4. B要告诉工人携带哪些原料,怎么处理这些原料;
  5. 等轮到产线A上对应的格子被处理的时候,上面的工人就开始操作了;

3.2 Handler的使用

Handler最常见的使用场景是:

  1. 为了进行耗时操作,主线程创建一个工作线程完成耗时操作;
  2. 工作线程开始耗时工作,时不时向主线程发送消息,告知当前完成的状态;
  3. 主线程根据工作线程的报告,更新界面元素,展示工作进度;

为了实现这样的功能,我们首先需要知道,

  1. 每个Activity都是运行在程序的主线程当中的;
  2. 只有主线程能修改界面元素,其他线程修改的话,应用会崩溃;
  3. 主线程在创建之后,系统已经为它设置了Looper,主线程已经有了消息处理的队列(生产流水线和流水线上的格子);

Handler处理这种场景时,我们有2种方式。

3.2.1 使用sendMessage()

  1. 创建一个能向主线程消息队列中布置任务的Handler;如果创建时直接new Handler(),说明这个Handler是放在主线程的消息队列中的“工人”;
  2. 重写HandlerhandleMessage()函数;
  3. handleMessage()函数中,根据msg.what的不同,进行对应的处理;
//创建的Handler是挂在主线程上的
private Handler mHandler = new Handler()
{
    @Override
    public void handleMessage(Message msg) {
        switch(msg.what)
        {
            case MSG_XXX:
            {
                //这里是在主线程中执行的,可以修改界面元素了
            }
            break;

            case ......
            break;

            default:
                super.handleMessage(msg);
        }
    }
};

任何想要布置任务的线程只需要利用HandlersendMessage()函数,就能把任务放到主线程的消息队列中,

Runnable runnable = new Runnable() {

    @Override
    public void run() {
        //耗时的操作
        while(!stop)
        {
            Message msg = mHandler.obtainMessage(MSG_XXX);
            //用主线程的Handler向主线程布置任务
            mHandler.sendMessage(msg);
        }
    }
};

Thread work = new Thread(runnable);
work.start();

这样主线程的消息队列中就有了这个新任务。等到这个消息被处理的时候,主线就可以根据参数修改界面元素了。

除了使用

Message msg = mHandler.obtainMessage(MSG_XXX);
mHandler.sendMessage(msg);

也可以使用

mHandler.obtainMessage(MSG_XXX).sendToTarget();

这里使用的Message就携带了“工人”和“原料”,Message通过Handler对象获取,

//只设置Message的what数值
Message msg = mHandler.obtainMessage(what);

//设置Message的what数值和Object值
Message msg = mHandler.obtainMessage(what, object);

//设置Message的what数值,arg1、arg2和Object值
Message msg = mHandler.obtainMessage(what, arg1, arg2, object);

发送Message可以直接发送,也可以延时发送,

//直接发送消息,将任务布置到消息队列中
mHandler.sendMessage(msg);

//延迟1000毫秒发送消息
mHandler.sendDelayedMessage(msg,1000)

3.2.2 使用post()

  1. 创建一个能向主线程消息队列中布置任务的Handler;如果创建时直接new Handler(),说明这个Handler是放在主线程的消息队列中的工人

    Handler mHandler = new Handler();
  2. 在工作线程中使用Handlerpost()方法,里面的Runnable就是在Handler所服务线程中运行;
    Runnable mHandlerRunnable;
    Runnable runnable = new Runnable() {
    
        @Override
        public void run() {
            //耗时的操作
            while(!stop)
            {
                mHandlerRunnable = new Runnable() {
                    @Override
                    public void run() {
                        //这里是在主线程中执行的,可以修改界面元素了
                    }
                };
                //用主线程的Handler向主线程发送任务
                mHandler.post(mHandlerRunnable);
            }
        }
    };
    
    Thread work = new Thread(runnable);
    work.start();

还可以使用

//延迟1000毫秒执行runnable
mHandler.postDelayed(mHandlerRunnable, 1000);

3.3 Handler任务的移除

大多数情况下,当Activity退出以后,需要将它布置给主线程的任务给移除掉。如果不移除,可能会遇到大麻烦。可以想象一下,

  1. 工作线程通过Handler给主线程布置了一个任务-根据一个参数修改界面显示,此时这个任务已经放到了主线程的任务队列里面;
  2. 用户突然退出了这个Activity,Activity被销毁了,但是主线程是不会退出的(Activity只是主线程上长的一个果子,果子被摘了,但是不会影响树的存在);
  3. 主线程的任务队列依次执行到了工作线程布置的任务,任务要求更新Activity上的界面元素,但是这个Activity已经被销毁了;

这时,程序就会出错。所以当一个Activity退出销毁的时候,一定要把相关的任务移除。

3.3.1 使用removeMessages()

通过它移除任务队列中所有特定Message,

mHandler.removeMessages(MSG_XXX);

3.3.2 使用removeCallbacks()

通过它移除任务队列中所有特定Runnable,

mHandler.removeCallbacks(mHandlerRunnable);

这里的mHandlerRunnable就是之前post()方式使用的mHandlerRunnable,所以在使用post()方式的时候,我们要把Runnable的引用保存起来,以便以后的移除操作。

第4节 HandlerThread

3.1章节介绍了Thread、Looper与Handler的关系。为了让开发者能简单的使用具备LooperThread,而不需要开发者写代码组合它们,Android SDK提供了HandlerThread

HandlerThread的原理很简单,

  1. 创建一个线程A;
  2. 在A上创建一个Looper

这些这个新创建的线程就可以不停的接收和处理任务了。

4.1 HandlerThread的使用

  1. 创建并启动HandlerThread

    HandlerThread mHandlerThread = new HandlerThread("创建的线程名字");
    mHThread.start();
  2. 获取可以访问HandlerThread对象任务队列的Handler;把Thread的Looper指定给Handler
    Handler mHandler = new Handler(mHandlerThread.getLooper());
  3. 使用HandlerHandlerThread对象布置任务;
    mHandler.sendMessage(MSG_XXX);
  4. HandlerThread对象退出;
    //马上退出线程
    mHandlerThread.quit();
    
    //执行完队列中的任务后退出线程
    mHandlerThread.quitSafely();

4.2 HandlerThread与Handler、Thread名称区分

HandlerThread与Handler、Thread名字相似,这里再做一下澄清。

  • Thread:线程,单纯的一个线程;
  • HandlerThread:具备任务队列的线程,它就是一个线程;
  • Handler:可以访问指定线程任务队列的一个工具;
时间: 2024-08-10 02:10:18

多线程开发(二)-Thread、 Looper与Handler关系解密的相关文章

理解Thread,Looper,MessageQueue,Handler关系

概括来说,Handler是Android中引入的一种让开发者参与处理线程中消息循环的机制.我们在使用Handler的时候与Message打交道最多,Message是Hanlder机制向开发人员暴露出来的相关类,可以通过Message类完成大部分操作Handler的功能.但作为程序员,我不能只知道怎么用Handler,还要知道其内部如何实现的.Handler的内部实现主要涉及到如下几个类: Thread.MessageQueue和Looper.这几类之间的关系可以用如下的图来简单说明: Threa

带你玩转java多线程系列 二 Thread 和 Runnable

Thread 和 Runnable 1 Runnable是接口,而Thread是继承,可以同时实现多个接口,但是不能同时继承多个类 2 Runnable比Thread灵活的多 当我们使用Runnable测试多线程非常简单,因为Runnable是对Thread的进一步解耦 我们首先建立一个类记做为Model,实现Runnable接口,在类里建立一个实例变量,接着覆盖run方法. 我们重新建立一个含有main函数的类,在该类中我们首先建立一个Model类型的实例变量model,接着将该Runnabl

Java多线程系列二——Thread类的方法

Thread实现Runnable接口并实现了大量实用的方法 public static native void yield(); 此方法释放CPU,但并不释放已获得的锁,其它就绪的线程将可能得到执行机会,它自己也有可能再次得到执行机会 public static native void sleep(long millis) throws InterruptedException; 此方法释放CPU,但并不释放已获得的锁,其它就绪的线程将得到执行机会,在休眠时间结束后,当前线程继续执行 publi

Handle的原理(Looper、Handler、Message三者关系)

"-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> Handle的原理(Looper.Handler.Message三者关系) - Ansen - 博客频道 - CSDN.NET Ansen 当我们失去了青春的时候,起码我们还留下了代码...... 目录视图 摘要视图 订阅 [活动]2017 CSDN博客专栏评选 &n

Android 异步消息处理机制Looper、Handler、Message三者关系

Looper,Handler,Message三者是我们常常用来再子线程跟新UI的,我们把Message发给Handler,然后,handler调用HandlerMessage()方法,我们在这个方法里面更新UI.那么Looper呢,又是什么,下面我来给大家介绍一下三者的关系. 首先要说明的是,每个线程最多只有一个Looper,在线程里面调用Looper.prepare()就是为这个线程设置了一个Looper,所以在子线程,我们使用这个三者前,一定要调用Looper.prepare()方法,而主线

多线程开发之二 NSOperation

效果如下: ViewController.h 1 #import <UIKit/UIKit.h> 2 3 @interface ViewController : UITableViewController 4 @property (strong, nonatomic) NSArray *arrSampleName; 5 6 - (instancetype)initWithSampleNameArray:(NSArray *)arrSampleName; 7 8 @end  ViewContro

深入理解Android消息处理系统——Looper、Handler、Thread

Android应用程序也是消息驱动的,按道理来说也应该提供消息循环机制.实际上谷歌参考了Windows的消息循环机制,也在Android系统中实现了消息循环机制. Android通过Looper.Handler来实现消息循环机制,Android消息循环是针对线程的(每个线程都可以有自己的消息队列和消息循环). 本文深入介绍一下Android消息处理系统原理. Android系统中Looper负责管理线程的消息队列和消息循环,具体实现请参考Looper的源码. 可以通过Loop.myLooper(

Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Android中的Looper , Handler , Message有什么关系?本篇博客目的首先为大家从源码角度介绍3者关系,然后给出一个容易记忆的结论. 1. 概述 Handler . Looper .Message 这三者都与Android异步消息处理线程相关的概念.那么什么叫异步消息处理线程呢?异步

在单线程模型中 Message、Handler、Message Queue、Looper 之间的关系

Message,信息的载体,用来传递数据给Handler. Handler (Handler处理者,是 Message 的主要处理者,负责 Message 的发送,Message 内容的执行处理)发送和处理Message和Runable对象,这些对象和一个线程的MessageQueue相关联.每一个线程实例和一个单独的线程以及该线程的 MessageQueue 相关联.Handler和创建它的线程绑定在一起,把 Message和Runable 对象传递给 MessageQueue,这些对象离开