当前位置: 首页 > 编程笔记 >

Android异步消息机制详解

司空坚
2023-03-14
本文向大家介绍Android异步消息机制详解,包括了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~

最后推荐直接使用最后的解决方案:静态内部类+弱引用。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。

 类似资料:
  • 一、消息机制概述 1.消息机制的简介 在Android中使用消息机制,我们首先想到的就是Handler。没错,Handler是Android消息机制的上层接口。Handler的使用过程很简单,通过它可以轻松地将一个任务切换到Handler所在的线程中去执行。通常情况下,Handler的使用场景就是更新UI。 如下就是使用消息机制的一个简单实例: public class Activity exte

  • 本文向大家介绍Android Handler消息机制?相关面试题,主要包含被问及Android Handler消息机制?时的应答技巧和注意事项,需要的朋友参考一下 作用: 跨线程通信。当子线程中进行耗时操作后需要更新UI时,通过Handler将有关UI的操作切换到主线程中执行。 四要素: Message(消息):需要被传递的消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQ

  • 本文向大家介绍Android消息处理机制Looper和Handler详解,包括了Android消息处理机制Looper和Handler详解的使用技巧和注意事项,需要的朋友参考一下 Android系统的消息队列和消息循环都是针对具体线程的,一个线程可以存在(当然也可以不存在)一个消息队列和一个消 息循环(Looper),特定线程的消息只能分发给本线程,不能进行跨线程,跨进程通讯。但是创建的工作线程默

  • 一、HandlerThread 的使用场景和用法? HandlerThread 本质上是一个在子线程的handler(HandlerThread=Handler+Thread); 步骤1:创建HandlerThread实例对象 步骤2:启动线程 步骤3:创建工作线程Handler&复写handleMessage () 步骤4:使用工作线程Handler向工作线程的消息队列发送消息 步骤5:结束线程

  • 本文向大家介绍Android消息机制Handler的工作过程详解,包括了Android消息机制Handler的工作过程详解的使用技巧和注意事项,需要的朋友参考一下 综述   在Android系统中,出于对性能优化的考虑,对于Android的UI操作并不是线程安全的。也就是说若是有多个线程来操作UI组件,就会有可能导致线程安全问题。所以在Android中规定只能在UI线程中对UI进行操作。这个UI线

  • 本文向大家介绍Android异步消息处理机制(这个也会经常问到)相关面试题,主要包含被问及Android异步消息处理机制(这个也会经常问到)时的应答技巧和注意事项,需要的朋友参考一下 异步消息处理机制主要是用来解决子线程更新UI的问题 主要有四个部分: ①. Message (消息) 在线程之间传递,可在内部携带少量信息,用于不同线程之间交换数据 可以使用what、arg1、arg2字段携带整型数