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 ...
随机推荐
- Hive简单安装
数据库的创建 Hive1版本 在此之前要安装好JDK,HADOOP,下载解压Hive 在root下安装mysql:yum install mysql-server mysql-client //一般M ...
- HTML5轻松实现拍照上传功能[转载]
转载 http://www.18sucai.com/article/275.htm 传统方法如果想实现拍照功能,需要后台应用程序复杂的编写,但随着html5的发展,在HTML5规范的支持下,Web A ...
- AMD与commonJS
CommonJS:它是一个同步的模式.但是这种模式并不适合于浏览器端,如果浏览器同步模式一个一个加载模块,那么打开将会变得非常的慢. AMD:它最大的特点就是可以异步的方式加载模块,具体的不同在于AM ...
- MSSQL 备份数据库还原
-- 完整还原RESTORE DATABASE XXXX FROM DISK = 'd:\XXXX.bak' WITH MOVE 'XXXX' TO 'D:\MSSQL\Data\XXXX.mdf', ...
- JQ图片文件上传之前预览功能
1.先准备一个div onchange触发事件 <input type="file" onchange="preview(this)" >< ...
- ANDROID基础ACTIVITY篇之ACTIVITY的生命周期(二)
除了Activity的七大生命周期方法外外,还有两个相当重要的方法需要大家熟记那就是onSavelnstanceState()和onRestoreinstanceState(). 那么什么时候会调用这 ...
- JavaScript学习笔记(二)——字符串
在学习廖雪峰前辈的JavaScript教程中,遇到了一些需要注意的点,因此作为学习笔记列出来,提醒自己注意! 如果大家有需要,欢迎访问前辈的博客https://www.liaoxuefeng.com/ ...
- EasyNVR-流媒体服务详解
1.什么是流媒体 所谓流媒体是指采用流式传输的方式在Internet播放的媒体格式. 流媒体又叫流式媒体,它是指商家用一个视频传送服务器把节目当成数据包发出,传送到网络上. 2.Eas ...
- Python——正则表达式特殊符号及用法
由于正则表达式的内容比较多,所以单独写成一系列文章,主要内容是根据小甲鱼所讲的内容综合一下正则表达式的笔记. 贴上小甲鱼的<Python3 如何优雅地使用正则表达式>系列可观看的博客地址: ...
- React+Redux实现追书神器网页版
引言 由于现在做的react-native项目没有使用到redux等框架,写了一段时间想深入学习react,有个想法想做个demo练手下,那时候其实还没想好要做哪一个类型的,也看了些动漫的,小说阅读, ...