Looper、Handler与HandlerThread

2024-04-28 11:08

本文主要是介绍Looper、Handler与HandlerThread,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

子线程与主线程通信

Android系统中,线程使用的收件箱叫做消息队列(message queue)。使用消息队列的线程叫做消息循环(message loop)。消息循环会不断循环检查队列上是否有新消息。消息循环由一个线程和一个looper组成。Looper对象管理着线程的消息队列。

主线程也是一个消息循环,因此具有一个looper。主线程的所有工作都是由其looper完成的。looper不断从消息队列中抓取消息,然后完成消息指定的任务。

Message与 Message Handler

消息是Message类的一个实例,包含好几个实例变量。其中有三个须在实现时定义:
what 用户定义的int型消息代码,用来描述消息;
obj 随消息发送的用户指定对象;
target 处理消息的Handler

Message的目标是Handler的一个实例。Message在创建时会自动与一个Handler相关联。Message在准备处理状态下,Handler是负责让消息处理行为发送的对象。Handler不仅仅是Message的目标(target),也是创建和发布Message的接口。

Looper拥有Message对象收件箱,所以Message必须在Looper上发布或读取。基于Looper与Message的这种关系,为与Looper协同工作,Handler总是引用着它。
一个Handler仅与一个Looper相关联,一个Message也仅与一个目标Handler相关联。多个Handler可与一个Looper相关联,这意味着一个Handler的Message可能与另一个Handler的Message存放在同一个消息队列中。

创建并启动后台线程

ThumbnailDownloader.java

package com.example.photogallery;import java.io.IOException;
import java.security.PublicKey;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;import android.R.interpolator;
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.location.GpsStatus.Listener;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;/*** ThumbnailDownloader类需要使用某些对象来标识每一次下载。因此,在类创建对话框中,通过ThumbnailDownloader<Token>* 的命名, 为其提供一个Token泛型参数。*/
public class ThumbnailDownloader<Token> extends HandlerThread {private static final String TAG = "ThumbnailDownloader";private static final int MESSAGE_DOWNLOAD = 0;Handler mHandler;//requestMap是一个同步HashMap,使用Token作为key,可存储或与获取特定Token相关联的URL.Map<Token, String> requestMap = Collections.synchronizedMap(new HashMap<Token, String>());/*** HandlerThread能在主线程上完成任务的一种方式是,让主线程将其自身的Handler传递给HandlerThread。* 主线程是一个拥有Handler和Looper的消息循环。主线程创建的Handler会自动与它的Looper相关联。我们可以* 将主线程上创建的Handler传递给另一线程。传递出去的Handler与创建它的线程Looper始终保持着联系。因此,* 任何已传出Handler负责处理的消息都将在主线程的消息队列中处理。这看上去就像我们在使用HandlerThread的* Handler,实现在主线程上安排后台线程上的任务。*/Handler mResponseHandler;Listener<Token> mListener;public interface Listener<Token>{void onThumbnailDownloaded(Token token, Bitmap bitmap);}public void setListener(Listener<Token> listener){mListener = listener;}public ThumbnailDownloader(Handler responseHandler) {super(TAG);mResponseHandler = responseHandler;}/*** 添加@SuppressLint("HandlerLeak")注解的原因:* 这里,Android Lint将报出Handler类相关的警告信息。Looper控制着Handler的生死,* 因此如果Handler是匿名内部类,则隐式的对象引用很容易导致内存泄漏。不过,所有的对象都与HandlerThread* 绑定在一起,因此这里不用担心任何内存泄漏问题。* * * HandlerThread.onLooperPrepared()方法调用发生在Looper第一次检查消息队列之前,* 所以该方法成了我们创建Handler实现的好地方。*/@SuppressLint("HandlerLeak") @Overrideprotected void onLooperPrepared() {mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {if(msg.what == MESSAGE_DOWNLOAD){/*** 这里必须使用@SuppressWarnings("unchecked")注解,因为Token是泛型类参数,* 而msg.obj是一个Object。由于类型擦除(type erasure),这里的强制类型转换应该是不可以的。*/@SuppressWarnings("unchecked")Token token = (Token) msg.obj;handleRequest(token);}}};}public void queueThumbnail(Token token, String url) {requestMap.put(token, url);/*** Handler.obtainMessage(...)方法会从公共循环持里获取消息,因此比创建新实例更有效率。* Message.sendToTarget()方法会将message发生给它的Handler,紧接着Handler会将Message* 放置在Looper消息队列的末尾。*/Message message = mHandler.obtainMessage(MESSAGE_DOWNLOAD, token);message.sendToTarget();}private void handleRequest(final Token token){try {final String url = requestMap.get(token);if(url == null)return;byte[] bitmapBytes = new FlickrFetchr().getUrlBytes(url);final Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);/*** Handler.post(Runnable)方法是一个张贴Message的便利方法*/mResponseHandler.post(new Runnable() {@Overridepublic void run() {/*** 因为GridView会循环使用它的视图,ThumbnailDownloader完成Bitmap下载后,* GridView可能已经循环使用了ImageView,并继续请求一个不同的URL。该检查可保证每个* Token都能获取到正确的图片,即使中间发生了其他请求也无妨。*/if(requestMap.get(token) != url)return;requestMap.remove(token);mListener.onThumbnailDownloaded(token, bitmap);}});} catch (IOException e) {e.printStackTrace();}}/*** 如果用户旋转屏幕,因ImageView视图的失效,ThumbnailDownloader则可能挂起。* 如果点击这些ImageView,就可能发生异常。*/public void clearQueue(){mHandler.removeMessages(MESSAGE_DOWNLOAD);requestMap.clear();}
}

启动与销毁后台线程ThumbnailDownloader

    @Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setRetainInstance(true);new FetchItemsTask().execute();/*** 创建并启动线程* getLooper()方法必在start()之后调用,这是一种保证线程就绪的处理方式。* * Handler默认与当前线程的Looper相关联。该Handler是在onCreate(...)方法中创建的,因此它将与主线程的Looper相关联。   */mThumbnailThread = new ThumbnailDownloader<ImageView>(new Handler());mThumbnailThread.setListener(new ThumbnailDownloader.Listener<ImageView>() {@Overridepublic void onThumbnailDownloaded(ImageView imageView, Bitmap bitmap) {//保证不会将图片设置到无效的ImageView视图上去if(isVisible()){imageView.setImageBitmap(bitmap);}}});mThumbnailThread.start();mThumbnailThread.getLooper();}
    @Overridepublic void onDestroyView() {super.onDestroyView();mThumbnailThread.clearQueue();}@Overridepublic void onDestroy() {super.onDestroy();mThumbnailThread.quit();//结束线程。若不终止HandlerThread,它会一直运行下去}

代码地址

这篇关于Looper、Handler与HandlerThread的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/943097

相关文章

webservice的安全机制2---handler实现

本节摘要:本节介绍使用handler的方式来实现webservice的IP地址的校验。   1.引言 前一节介绍了使用users.lst文件来实现webservice的用户名和密码的校验, 本节介绍使用webservice的handler来实现webservice的安全校验。 这里,不用用户名和密码来实现安全校验,换一种方式,采用IP地址校验的方式。 这里通过一个配置文件来控制是否打开

Netty源码解析4-Handler综述

Netty中的Handler简介 Handler在Netty中,占据着非常重要的地位。Handler与Servlet中的filter很像,通过Handler可以完成通讯报文的解码编码、拦截指定的报文、 统一对日志错误进行处理、统一对请求进行计数、控制Handler执行与否。一句话,没有它做不到的只有你想不到的 Netty中的所有handler都实现自ChannelHandler接口。按照输入

JAX-WS - Handler详解

一、Handler说明     Handler用于处理Soap消息,如控制Header(如隐式的添加用户信息等)     Handler分成LogicalHandler和SOAPHandler,常用为SOAPHandler;客户端先处理LogicalHeader再处理SOAPHandler,服务器反之 二、开始前的准备     1、服务端         (1)接口: @W

常用的两种handler调用方法

一、Handler、Thread、HandlerThread三者之间的关系如下: 1、Handler:在android中负责发送和处理消息,通过它可以实现其他支线线程与主线程之间的消息通讯。 2、Thread:Java进程中执行运算的最小单位,亦即执行处理机调度的基本单位。某一进程中一路单独运行的程序。 3、HandlerThread:一个继承自Thread的类HandlerThread。

Android消息机制概述 Handler

Android消息机制概述 Android 的消息机制主要是指 Handler 的运行机制, Handler 的运行需要MessageQueue 和 Looper 来支撑。MessageQueue是消息队列,就是内部存储了一组消息,以队列的形式对外提供插入和删除的工作。是采用单链表的数据结构来存储消息列表。Looper是消息循环,由于MessageQueue不能处理消息,然而Looper弥补了这

Handler机制浅析

使用方法 实例化一个handler对象 val handler = object : Handler() {override fun handleMessage(msg: Message) {Log.e(TAG, "收到消息:" + msg.obj.toString())super.handleMessage(msg)}} 在需要发送消息的地方这样调用 val msg = Messag

面试常问Handler 的问题合集

问:Looper.prepare做了什么操作? 检查了当前线程是否已经有了Looper对象,如果已经有了Looper对象,那么就直接报错。保证每个线程只有一个Looper. private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeExcepti

Android源码解析Handler系列第(二)篇--- ThreadLocal详解

在上篇文章Android源码解析Handler系列第(一)篇说了Message的内部维持的全局池机制。这一篇仍然是准备知识,因为在Handler中有ThreadLocal的身影,大家知道,Handler创建的时候会采用当前线程的Looper来构造消息循环系统,那么Handler内部如何获取到当前线程的Looper呢?这就要使用ThreadLocal了,ThreadLocal可以在不同的线程之中互不

Android源码解析Handler系列第(一)篇 --- Message全局池

1、UI不能在子线程中更新是个伪命题 我们常说UI需要在主线程中进行更新,子线程就不能更新UI吗?不是,我们并不是说不能在子线程中更新UI,而是说UI必须要在它的创建线程中进行更新,比如下面一段代码在子线程更新UI就不会报错。 new Thread(new Runnable() {@Overridepublic void run() {TextView textView=new

Android多线程----异步消息处理机制之Handler

一、handler的引入: 我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃。相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创建一个Message对象,然后借助Handler发送出去,之后在Handler的handleMessage()方法中获得刚才发送的Message对象,然后在这里进行UI操作就不会再出现崩溃