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 ...
随机推荐
- C++STL之双端队列容器
C++STL之双端队列容器 deque双端队列容器与vector很类似,采用线性表顺序存储结构.但与vector区别,deque采用分块的线性存储结构来存储数据,每块的大小一般为512B,将之称为de ...
- code forces 436 D. Make a Permutation!
D. Make a Permutation! time limit per test 2 seconds memory limit per test 256 megabytes input stand ...
- Group(), Groups(),& Groupdict()
group() 返回一个或多个匹配的字串.如果只有一个参数,结果只有单个字符串:如果有多个参数,结果是一个元组,元组里每一项对应一个参数.没有参数,group1默认是0(整个匹配串被返回).如果gro ...
- Phonegap开发相关问题
环境搭建:参考http://www.phonegapcn.com/start/zh/1.3/#android 调试: 1.在线远程调试 http://debug.phonegap.com/ 通过USB ...
- luogu P1563 玩具谜题
https://www.luogu.org/problemnew/show/1563 题目: 小南有一套可爱的玩具小人, 它们各有不同的职业. 有一天, 这些玩具小人把小南的眼镜藏了起来. 小南发现玩 ...
- Markdown规则
第一次写随想,写的不好还请包涵呀!!! 这两天在用markdown写一些文档,感觉还不错,整理一下其规则,对于新手会有帮助. 1. 文章的标题,一般写在首行,第二行添加"---------- ...
- iOS开发针对对Masonry下的FPS优化讨论
今天博客的内容就系统的讨论一下Masonry对FSP的影响,以及如何更好的使用Masonry.如果你对iOS开发足够熟悉的话,那么对Masonry框架应该不陌生.简单的说,Masonry的诞生让Aut ...
- 检测应用的内存泄漏情况(shell)
写代码--调试--修BUG 改来改去可能还存在一些没发现的问题,在工程量大的时候更容易出现,例如内存泄漏这样的问题,严重影响着系统性能. 网上有些检测C程序是否存在内存泄漏的工具还不错的,例如valg ...
- Python之os.fork
[参考资料] http://www.01happy.com/python-fork-create-process/ http://www.python-course.eu/forking.php ht ...
- centos安装maven,git,mysql
安装maven1:下载maven包:http://mirror.bit.edu.cn/apache/maven/maven-3/3.5.0/binaries/apache-maven-3.5.0-bi ...