Android异步消息机制
Android中的异步消息机制分为四个部分:Message、Handler、MessageQueue和Looper。
其中,Message是线程之间传递的消息,其what、arg1、arg2字段可以携带整型数据,obj字段可以携带一个Object对象。
Handler是处理者,主要用于发送消息和处理消息。发送消息的方法是sendMessage;处理消息的方法是handleMessage(),Message字段携带的信息在该方法中用作判别。
MessageQueue是消息队列,存放所有Handler发送的消息。
Looper是消息队列的“管家”,将消息从消息队列中一条条取出,并分派到Handler的handleMessage()方法中。
————————————————————————————————————————————————————————————————————————————————
异步消息处理的流程为:

①首先,需要在主线程中创建一个Handler对象,并重写handleMessage()方法。
②当子线程处理完耗时操作,需要将处理结果反馈到UI中时,先创建一个Message对象,并让其what字段携带一个int值,然后通过Handler对象发送出去。
③之后该消息会被添加到MessageQueue中等待被处理,而Looper会一直尝试从MessageQueue中取出待处理消息,最后分发回Handler对象中的handleMessage()方法中。由于Handler对象是在主线程中创建的,所以可以在handleMessage()方法中安心地进行UI操作。
————————————————————————————————————————————————————————————————————————————————
通过一个例子来验证一下:活动MainActivity中有一个按钮和一个TextView。TextView初始化显示“Hello World!”,之后点击按钮,进行耗时操作;耗时操作结束后,TextView显示“Nice to meet you”。根据以上的分析,我无比自然地写出了以下代码:
package com.shaking.androidthreadtest; import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView; public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private static final int UPDATE_TEXT=1;
private String data;
private TextView textView; private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case UPDATE_TEXT:
textView.setText(data);
}
}
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
Button button=findViewById(R.id.button);
textView=findViewById(R.id.text_view);
button.setOnClickListener(this);
} @Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
//假设此处进行了耗时操作,最终得到结果字符串data
data="Nice to meet you";
Message message=new Message();
message.what=UPDATE_TEXT;
handler.sendMessage(message);
}
}).start();
}
}
首先,这么写,是肯定没有错误的!程序也可以正常运行。但是IDE给出了警告:“This Handler class should be static or leaks might occur”。
这个警告的意思是:我们使用Handler这个类时,应该将其声明为静态,否则会导致内存泄露。
那么,为什么会发生内存泄露呢?原因是:
第一:当我们通过Handler对象的sendMessage()方法发送一个Message对象时,该Message对象持有对该Handler对象的引用(正是依靠这个引用,Looper在消息队列中取出该Message对象后,才能准确地将该Message对象分派回该Handler对象!)。
第二,我们在主线程中创建Handler对象时,为了重写其handleMessage()方法,使用了匿名内部类的方式来创建该Handler对象。而匿名内部类和非静态内部类都是隐性地持有一个对外部类的引用!所以,该Handler对象持有外部类MainActivity的引用。
以上两个结合在一起,问题就来了:Message对象持有Handler对象引用,Handler对象持有MainActivity的引用。所以,MainActivity该活动永远无法被内存回收,直到Message被回收为止!如果Message对象在子线程中被发送至消息队列,然后一直没有被处理,该活动所在的主线程也会一直挂着,而不会被内存回收。所以,会导致内存泄露。
————————————————————————————————————————————————————————————————————————————————
知道了原因,那么解决方法是什么?其实之前的警告,已经给出了解决方案。那就是通过静态内部类的方式创建Handler对象,因为静态内部类不会持有对外部类对象的引用。
这时候,我又自然而然地创建一个静态内部类,继承自Handler类,然后重写其handleMessage方法。
private static class MyHandler extends Handler{
@Override
public void handleMessage(Message msg) {
}
}
但是,此处又出现了一个问题!如果我不持有对外部类的引用了,那么我怎么使用外部类的方法和对象?毕竟我是要在handleMessage()方法中进行UI操作的。
对于这种使用了静态内部类来避免内存泄露,同时又需要调用外部类的方法的情况:可以使用弱引用!即我们在该内部类中声明一个对外部类对象的弱引用。这样即可以调用外部类的方法,又不会导致内存泄露。
具体修改后的代码,如下:
package com.shaking.androidthreadtest; import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.lang.ref.WeakReference; public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private static final int UPDATE_TEXT=1;
private String data;
private TextView textView; private static class MyHandler extends Handler{
//使该内部类持有对外部类的弱引用
private WeakReference<MainActivity> weakReference;
//构造器中完成弱引用初始化
MyHandler(MainActivity activity){
weakReference=new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
//通过弱引用的get()方法获得外部类对象的引用
MainActivity activity=weakReference.get();
activity.textView.setText(activity.data);
}
}
//创建Handler对象
private MyHandler handler=new MyHandler(this); @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
Button button=findViewById(R.id.button);
textView=findViewById(R.id.text_view);
button.setOnClickListener(this);
} @Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
//假设此处进行了耗时操作,最终得到结果字符串data
data="Nice to meet you";
Message message=new Message();
message.what=UPDATE_TEXT;
handler.sendMessage(message);
}
}).start();
}
}
完美解决以上所有问题!6~
最后推荐直接使用最后的解决方案:静态内部类+弱引用。
Android异步消息机制的更多相关文章
- Android之消息机制Handler,Looper,Message解析
PS:由于感冒原因,本篇写的有点没有主干,大家凑合看吧.. 学习内容: 1.MessageQueue,Looper,MessageQueue的作用. 2.子线程向主线程中发送消息 3.主线程向子线程中 ...
- Android 异步消息处理机制前篇(二):深入理解Message消息池
版权声明:本文出自汪磊的博客,转载请务必注明出处. 上一篇中共同探讨了ThreadLocal,这篇我们一起看下常提到的Message消息池到底是怎么回事,废话少说吧,进入正题. 对于稍有经验的开发人员 ...
- Android 异步消息处理机制终结篇 :深入理解 Looper、Handler、Message、MessageQueue四者关系
版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.概述 我们知道更新UI操作我们需要在UI线程中操作,如果在子线程中更新UI会发生异常可能导致崩溃,但是在UI线程中进行耗时操作又会导致ANR,这 ...
- Android 基础 十一 Android的消息机制
Handler是Android消息机制的上层接口,这使得在开发应用过程中我们只需要和Handler交互即可.Handler的使用过程很简单,通过它可以轻松地将一个任务切换到Handler所在的线程中去 ...
- Android异步消息传递机制源码分析
1.Android异步消息传递机制有以下两个方式:(异步消息传递来解决线程通信问题) handler 和 AsyncTask 2.handler官方解释的用途: 1).定时任务:通过handler.p ...
- Android异步消息处理机制(多线程)
当我们需要执行一些耗时操作,比如说发起一条网络请求时,考虑到网速等其他原因,服务器未必会立刻响应我们的请求,如果不将这类操作放在子线程里去执行,就会导致主线程被阻塞住,从而影响用户对软件的正常使用. ...
- 聊一聊Android的消息机制
聊一聊Android的消息机制 侯 亮 1概述 在Android平台上,主要用到两种通信机制,即Binder机制和消息机制,前者用于跨进程通信,后者用于进程内部通信. 从技术实现上来说,消息机制还是比 ...
- 【转载】Android异步消息处理机制详解及源码分析
PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbob ...
- 《Android开发艺术探索》读书笔记 (10) 第10章 Android的消息机制
第10章 Android的消息机制 10.1 Android消息机制概述 (1)Android的消息机制主要是指Handler的运行机制,其底层需要MessageQueue和Looper的支撑.Mes ...
随机推荐
- spring mvc常用注解的说明
最近一段时间学习了springboot,所以熟悉一下mvc中常用的注解,这样可以方便开发 简介: @RequestMapping RequestMapping是一个用来处理请求地址映射的注解,可用于类 ...
- 简单Spring+Struts2+Hibernate框架搭建
使用Maven+Spring+Struts2+Hibernate整合 pom文件 <project xmlns="http://maven.apache.org/POM/4.0.0&q ...
- C++分布式实时应用框架 (Cpp Distributed Real-time Application Framework)----(一):整体介绍
C++分布式实时应用框架 (Cpp Distributed Real-time Application Framework) 在现今软件系统纷纷"云化"的浪潮下,各种支持" ...
- linux-touch
linux-touch 用于创建文件或者更新文件的修改日期 命令参数 - d yyyymmdd:把文件的存取或修改时间改为 yyyy年mm月dd日 - a :只把文件的存取时间改成当前时间 - m: ...
- WebService--axis
axis WebService虽然现在已经很少使用,但是还是把它的配置过程写出来,开发环境jdk 1.6 服务端: 1,导入需要jar包,自行下载 2,创建WebService接口 public in ...
- 《Linux命令行与shell脚本编程大全》 第六章环境变量
很多程序和脚本都通过环境变量来获取系统信息.存储临时数据和配置信息. 6.1 什么是环境变量: bash shell用一个叫环境变量(environment variable)的特性来存储有关shel ...
- PHP基础入门(三)【PHP中的数组】
PHP数组的分类 按照下标的不同,PHP数组分为关联数组与索引数组: 索引数组:下标从0开始,依次增长: 关联数组: 下标为字符串格式,每个下标字符串与数组的值一一关联对应.(有点像对象的键值对) 关 ...
- 40个Java多线程问题
1.多线程有什么用? 一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡.所谓”知其然知其所以然”,”会用”只是”知其然”,”为什么用”才是”知其所以然 ...
- Linux系列教程(二十二)——Linux的bash变量
上篇博客我们介绍了bash的一些基本功能,这是我们平时操作最频繁的.本篇博客我们介绍bash的变量,为后面编写shell脚本做铺垫. 1.什么是变量 变量是计算机内存的单元,其中存放的值可以改变. 当 ...
- Request 和 Response 区别
Request 和 Response 对象起到了服务器与客户机之间的信息传递作用.Request 对象用于接收客户端浏览器提交的数据,而 Response 对象的功能则是将服务器端的数据发送到客户端浏 ...