【Android基础】Handler

一、引入

handler通俗一点讲就是用来在各个线程之间发送数据的处理对象。在任何线程中,只要获得了另一个线程的handler,则可以通过 handler.sendMessage(message)方法向那个线程发送数据。
基于这个机制,我们在处理多线程的时候可以新建一个thread,这个thread拥有UI线程中的一个handler。当thread处理完一些耗时的操作后通过传递过来的handler向UI线程发送数据,由UI线程去更新界面。

二、常用类

1、Looper

每一个线程只有一个Looper,每个线程在初始化Looper之后,Looper会维护好该线程的MessageQueue,MessageQueue用来存放Handler发送的Message,并处理MessageQueue中的Message。
当我们在主线程创建Handler时,它就会跟主线程唯一的Looper绑定,从而我们使用Handler在子线程发消息时,最终也是在主线程处理,达到了异步的效果。

2、MessageQueue

MessageQueue是一个消息队列,用来存放Handler发送的消息。每个线程最多只有一个MessageQueue。
MessageQueue通常都是由Looper来管理,而主线程创建时,会创建一个默认的Looper对象,而Looper对象的创建,将自动创建一个MessageQueue。其他非主线程,不会自动创建Looper。

3、Message

被传递和处理的数据。其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
一个MessageQueu可以包括多个Message。
Message虽然也可以通过new来获取,但是通常使用Message.obtain()或Handler.obtainMessage()方法来从消息池中获得空消息对象,以节省资源。

4、Handler

负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
非主线程没有loop对象,所以要调用Looper.prepare()方法。
主线程中使用new来创建Handler对象。而子线程中不能直接new来创建Handler对象就会异常。
子线程中使用Handler的步骤:

        new Thread(new Runnable() {
     
            @Override
            public void run() {
     
                //1、为子线程创建一个Looper
                Looper.prepare();
                //2、创建Handler对象
                Handler handler = new Handler() {
     
                    @Override
                    public void handleMessage(@NonNull Message msg) {
     
                        super.handleMessage(msg);
                    }
                };
                //3、调用Looper.loop()
                //在线程中调用过Looper.loop()之后,后面不能再写代码,因为loop()内部是个死循环,后面的写的代码无法执行到。
                Looper.loop();
            }
        }).start();

三、Handler造成内存泄漏

1、原因

Handler内存泄漏详解及其解决方案

    private Handler handler = new Handler() {
     
        @Override
        public void handleMessage(@NonNull Message msg) {
     
            super.handleMessage(msg);
            textView.setText(msg.what + "");
        }
    };

上面是一段简单的Handler的使用。
非静态内部类和匿名内部类都会隐式持有当前类的外部引用,由于Handler是非静态内部类所以其持有当前Activity的隐式引用,如果Handler没有被释放,其所持有的外部引用也就是Activity也不可能被释放,当一个对象一句不需要再使用了,本来该被回收时,而有另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏(上面的例子就是这个原因)。最终也就造成了OOM

2、解决方案

方法一:通过程序逻辑来进行保护。
1)在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。
2)如果你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。

方法二:将Handler声明为静态类。
在Java 中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,静态的内部类不会持有外部类的引用。
静态类不持有外部类的对象,所以你的Activity可以随意被回收。由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用(WeakReference)。

public class Main2Activity extends AppCompatActivity {
     

    @Override
    protected void onCreate(Bundle savedInstanceState) {
     
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        MyHandler myHandler = new MyHandler(this);
        //延迟1分钟发送
        myHandler.postDelayed(myRunnable,1000 * 60);
    }

    private static final Runnable myRunnable = new Runnable() {
     
        @Override
        public void run() {
     
            //执行我们的业务逻辑
        }
    };

    //创建静态内部类
    private static class MyHandler extends Handler {
     
        //持有弱引用Main2Activity,GC回收时会被回收掉.
        WeakReference<Main2Activity> mWeakReference;
        public MyHandler(Main2Activity main2Activity) {
     
            mWeakReference = new WeakReference<Main2Activity>(main2Activity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
     
            super.handleMessage(msg);
            Main2Activity main2Activity = mWeakReference.get();
            if(main2Activity != null){
     
                //执行业务逻辑
            }
        }
    }
}

四、Handler类中常用方法

1、sendEmptyMessage(int what)

发送一条空消息

2、sendEmptyMessageDelayed(int what, long delayMillis)

在该方法内部首先将what封装成一个Message,同时调用sendMessageDelayed(msg, delayMillis)方法延迟发送一条消息

3、sendMessageDelayed(Message msg, long delayMillis)

表示延迟发送一条消息

4、sendMessageAtTime(Message msg, long uptimeMillis)

方法表示定时发送一条消息

5、handleMessage(Message msg)

对Handler发送来的消息进行处理

6、post(Runnable r)

发送一个子线程

7、postDelayed(Runnable r, long delayMillis)

延迟发送一个子线程

8、obtainMessage()

从消息池中获得一个消息

五、Message消息类中常用属性

arg1:用来存放整型数据
arg2:用来存放整型数据
obj:用来存放Object数据
what:用于指定用户自定义的消息代码,这样便于主线程接收后,根据消息代码不同而执行不同的相应操作

六、案例

实现倒计时

package com.android.mythread;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.lang.ref.WeakReference;

public class Main2Activity extends AppCompatActivity {
     
    private Button button;
    public static TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
     
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        final MyHandler myHandler = new MyHandler(this);

        button = findViewById(R.id.bttime);
        textView = findViewById(R.id.text);
        button.setOnClickListener(new View.OnClickListener() {
     
            @Override
            public void onClick(View v) {
     
                for (int i = 1; i <= 10; i++) {
     
                    Message message = Message.obtain(myHandler);
                    message.what = 10 - i;
                    myHandler.sendMessageDelayed(message, 1000 * i); //通过延迟发送消息,每隔一秒发送一条消息

                }
            }
        });
    }

    //创建静态内部类
    private static class MyHandler extends Handler {
     
        //持有弱引用Main2Activity,GC回收时会被回收掉.
        WeakReference<Main2Activity> mWeakReference;
        public MyHandler(Main2Activity main2Activity) {
     
            mWeakReference = new WeakReference<Main2Activity>(main2Activity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
     
            super.handleMessage(msg);
            Main2Activity main2Activity = mWeakReference.get();
            if(main2Activity != null){
     
                //执行业务逻辑
                textView.setText(msg.what + "");
            }
        }
    }
}

你可能感兴趣的:(Android基础)