Android异步消息机制

Android中的异步消息机制分为四个部分:Message、Handler、MessageQueue和Looper。java

其中,Message是线程之间传递的消息,其what、arg一、arg2字段能够携带整型数据,obj字段能够携带一个Object对象。android

Handler是处理者,主要用于发送消息和处理消息。发送消息的方法是sendMessage;处理消息的方法是handleMessage(),Message字段携带的信息在该方法中用做判别。app

MessageQueue是消息队列,存放全部Handler发送的消息。异步

Looper是消息队列的“管家”,将消息从消息队列中一条条取出,并分派到Handler的handleMessage()方法中。ide

————————————————————————————————————————————————————————————————————————————————oop

异步消息处理的流程为:this

①首先,须要在主线程中建立一个Handler对象,并重写handleMessage()方法。spa

②当子线程处理完耗时操做,须要将处理结果反馈到UI中时,先建立一个Message对象,并让其what字段携带一个int值,而后经过Handler对象发送出去。线程

③以后该消息会被添加到MessageQueue中等待被处理,而Looper会一直尝试从MessageQueue中取出待处理消息,最后分发回Handler对象中的handleMessage()方法中。因为Handler对象是在主线程中建立的,因此能够在handleMessage()方法中安心地进行UI操做。code

————————————————————————————————————————————————————————————————————————————————

经过一个例子来验证一下:活动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~

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

相关文章
相关标签/搜索