Android的handler机制

一、引言

1. Android Handler的概述

Android中的Handler是一种用来处理和分发Message和Runnable对象的工具,它通常用于在子线程中执行耗时操作,然后将结果传回主线程更新UI。一般情况下,Handler是与Looper和MessageQueue一起工作的。
在Android系统中,主线程也叫UI线程,它负责处理所有的用户交互动作和屏幕显示工作,因此,主线程需要保持流畅,不能有耗时的操作,否则会导致应用无响应(ANR)。
但是在实际的开发过程中,我们不可避免地需要进行一些耗时的操作,比如网络请求、文件读写等。这时我们就需要用到Handler。我们可以在子线程中使用Handler发送Message或者Runnable对象,然后这些对象会被添加到主线程的MessageQueue中,主线程的Looper会不断地从MessageQueue中取出Message或者Runnable对象进行处理,从而实现了线程间的通信,更新UI。

简单来说,Handler就是Android中一个非常重要的用来进行线程间通信,执行耗时操作并能够返回主线程进行UI操作的工具。

2. Handler的重要性及作用

实现线程间通信:在Android系统中,主线程(也称UI线程)负责处理所有的用户交互动作和屏幕显示工作,因此,必须保持流畅,不能有耗时的操作,否则会导致应用无响应(ANR)。但在实际开发中,我们不可避免地需要进行一些耗时的操作,比如网络请求、文件读写等。这时我们就需要用到Handler,通过Handler,我们可以将这些耗时操作放到子线程中执行,然后将执行结果通过Handler传递到主线程,更新UI。

定时任务和延时任务:Handler可以执行定时任务和延时任务,比如需要在一段时间后执行某个操作,或者需要定时更新UI等。

异步消息处理:Handler还能处理异步消息。当我们发送一个Message或者Runnable到Handler时,这个消息会被添加到消息队列中,然后按照FIFO(先进先出)的顺序依次处理。这就是一个典型的异步消息处理过程。

避免内存泄露:在Android开发中,非静态内部类和匿名内部类会持有外部类的引用,如果这个外部类是一个Activity,那么就可能导致内存泄露。而Handler是一个非静态内部类,所以在使用Handler时需要特别注意避免内存泄露。对此,Android提供了一种解决方案,那就是使用静态内部类+弱引用的方式。

总的来说,Handler是Android中非常重要的一个工具,它可以帮助我们更好地进行线程间通信,执行定时任务和延时任务,处理异步消息,以及避免内存泄露。

二、Handler的基本概念

在这里插入图片描述

上面是handler工作原理的示意图(比较简略)。

主线程(Main Thread):这个线程通常是应用启动时由系统创建的,它负责UI的更新和处理用户的交云操作。它自带一个Looper对象和一个MessageQueue消息队列。

子线程(Other Thread):这是在主线程之外创建的线程,用于执行耗时操作以避免阻塞主线程。如果这个线程需要更新UI或者需要与主线程通讯,它需要使用Handler。

Looper:每个线程中只能有一个Looper对象,它会循环遍历MessageQueue中的消息,并将它们分发给对应的Handler处理。

MessageQueue:这是一个存储Message和Runnable对象的队列,它按照消息的时间顺序来存储它们。

Handler:主要用于在不同线程间发送和处理消息。Handler可以与Looper所在的线程的MessageQueue关联起来。

sendMessage/post:Handler提供了发送Message和Runnable的方法。当这些方法被调用时,它们会将Message或Runnable对象添加到Handler关联的Looper的MessageQueue中。

Looper.prepare():在非主线程中,如果你想使用Looper,你需要先调用Looper.prepare()来为当前线程准备一个Looper对象。

整个流程是这样的:
当其他线程需要与主线程通讯或者需要主线程来更新UI时,它会通过Handler对象发送一个Message或者Runnable到MessageQueue。主线程中的Looper不断循环检查MessageQueue,当发现新的消息时,它会将消息发送给Handler处理。Handler根据Message或者Runnable的指示,在主线程中执行相应的操作(如更新UI)。
请注意,这个图只是一个简略的表示,并没有展示Handler, Message, Looper, MessageQueue的内部工作细节。实际情况中,这些组件间的交互会更加复杂。
下面是一个handler机制的示例Demo

package com.example.myapplication;

import android.annotation.SuppressLint;
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 androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

import java.lang.ref.WeakReference;

public class MainActivity extends AppCompatActivity {

    static int number = 0;
    private static final int MESSAGE_CODE = 1;
    private Button button;

    // 使用静态内部类,避免内存泄露
    static class MyHandler extends Handler {
        // 使用WeakReference,避免内存泄露
        private final WeakReference<MainActivity> mActivity;

        MyHandler(MainActivity activity) {
            super(Looper.getMainLooper());
            mActivity = new WeakReference<>(activity);
        }

        @SuppressLint("SetTextI18n")
        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = mActivity.get();
            if (activity != null && msg.what == MESSAGE_CODE) {
                // 在主线程中处理消息
                String data = (String) msg.obj;
                Log.d("handleMessage", Thread.currentThread().getName());
                System.out.println("Received message: " + data);
                //更新UI
                number++;
                activity.button.setText("点击了" + number + "次");
            }
        }
    }

    final MyHandler handler = new MyHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
        button = findViewById(R.id.button);
        //监听按钮的点击事件
        bindClick(button);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacksAndMessages(null);
    }

    private void bindClick(Button button) {
        button.setOnClickListener(new View.OnClickListener() {
            @SuppressLint("SetTextI18n")
            @Override
            public void onClick(View v) {
                button.setText("等待结果返回");
                //子线程处理耗时操作
                otherThread();
            }
        });
    }

    public void otherThread() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                //模拟耗时操作
                    Thread.sleep(3000);
                    Message message = handler.obtainMessage();
                    message.what = MESSAGE_CODE;
                    Log.d("otherThread()", Thread.currentThread().getName());
                    handler.sendMessage(message);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }
}

1. Looper

概述

在Android中,Looper是一个用来管理线程消息队列的类。每个线程中都可以有一个Looper对象,用来处理和调度MessageQueue中的消息

package android.os;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.Log;
import android.util.Printer;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;

/**
* Class used to run a message loop for a thread.  Threads by default do
* not have a message loop associated with them; to create one, call
* {@link #prepare} in the thread that is to run the loop, and then
* {@link #loop} to have it process messages until the loop is stopped.
*
* <p>Most interaction with a message loop is through the
* {@link Handler} class.
*
* <p>This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper.
*
* <pre>
*  class LooperThread extends Thread {
*      public Handler mHandler;
*
*      public void run() {
*          Looper.prepare();
*
*          mHandler = new Handler(Looper.myLooper()) {
*              public void handleMessage(Message msg) {
*                  // process incoming messages here
*              }
*          };
*
*          Looper.loop();
*      }
*  }</pre>
*/
public final class Looper {...}
工作原理

在创建Looper对象后,它会与当前线程绑定,并创建一个MessageQueue消息队列。然后,Looper会开始一个无限循环(称为Looper Loop),在这个循环中,Looper会从MessageQueue中取出消息,并将这些消息分发到对应的Handler上,由Handler的handleMessage方法来处理这些消息。
在Android中,主线程默认会创建一个Looper对象,也就是说,主线程默认是一个Looper线程。比如上面的Demo中,代码没有显式的创建Looper对象,那Looper.getMainLooper()这个获取的对象是哪里来的呢?其实当Android应用程序启动时,会创建一个名为“main”的主线程,并自动调用Looper.prepareMainLooper()方法来创建一个主线程的Looper对象,并通过Looper.setMainLooper(looper)将其设置为主线程的Looper。(该方法在目前已废弃) 并自动调用Looper的prepare方法来创建一个Looper对象,然后把这个对象放在ThreadLocal来
关联到当前线程。Looper.getMainLooper()就是返回这个主线程的Looper对象。
在这里插入图片描述
而对于子线程,如果需要使用Looper,就需要手动调用Looper.prepare()方法来创建Looper对象和对应的消息队列,然后通过Looper.loop()方法来启动消息循环。请注意,Looper.loop()是一个无限循环,所以通常这个方法是在子线程的最后一行代码调用,否则后面的代码将不会被执行。

2. Message

概述

在Android中,Message是用于线程之间通信的一种机制。它通常与Handler,Looper和MessageQueue一起使用,以在不同的线程之间发送和处理消息。

工作原理

以下是Message的几个主要组成部分:
在这里插入图片描述

what:这是一个整数字段,通常用于描述消息的类型。你可以自己定义不同的整数值来表示不同类型的消息。

arg1 和 arg2:这是两个整数字段,你可以用它们来存储一些额外的信息。但是如果你需要存储更复杂的数据,通常需要使用下面的obj字段或者data字段。

obj:这是一个Object字段,你可以用它来存储任何类型的对象。

data:这是一个Bundle对象,你可以用它来存储一些键值对。它与Intent中的extras非常类似。

target:这是一个Handler对象,它表示这个消息将要发送到的目标。当你通过Handler发送一个消息时,这个字段会自动被设置。

Message对象通常不直接创建,而是通过Handler的obtainMessage方法来获取。这是因为Message内部有一个消息池,通过obtainMessage方法获取的Message对象会自动从消息池中回收,这样可以避免频繁地创建和销毁对象,提高性能。下面是源码(Android 12)


    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() { //获取Message对象
    //Message池不为空就从池中取,可以看到对象池的实现是链表结构
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        //池为空就新创建
        return new Message();
    }


    /**
     * Recycles a Message that may be in-use.
     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
     */
    @UnsupportedAppUsage
    void recycleUnchecked() { //回收使用后的Message
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        //清空当前Message对象的属性
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;
		//进入同步块,将当前Message对象放入对象池中
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

发送和处理Message的主要方式是通过Handler对象。Handler对象有一个与之关联的Looper对象,这个Looper对象有一个MessageQueue消息队列。当你通过Handler发送一个Message时,这个Message会被加入到Looper的MessageQueue中。然后Looper会循环地从MessageQueue中取出Message,并调用Handler的handleMessage方法来处理这个Message。

3. MessageQueue

概述

在Android中,MessageQueue是一个用于存储和调度Message或Runnable对象的消息队列。MessageQueue是Looper的一部分,每个Looper都有一个与之关联的MessageQueue。

工作原理

以下是MessageQueue的主要特点和功能:

  1. 存储和调度消息:MessageQueue的主要作用是存储和调度消息。当你通过Handler发送一个消息时,这个消息会被加入到Looper的MessageQueue中。然后Looper会循环地从MessageQueue中取出消息,并调用Handler的handleMessage方法来处理这个消息。MessageQueue保证了消息的处理顺序与它们被发送的顺序一致。

  2. 支持延迟消息:MessageQueue支持延迟消息的发送。你可以在发送一个消息时指定一个延迟时间。这个消息会在延迟时间过后才会被处理。

  3. 支持优先级:每个Message或Runnable对象都有一个与之关联的时间戳,这个时间戳决定了消息被处理的顺序。时间戳较小的消息会先被处理,因此你可以通过设置时间戳来控制消息的优先级。

  4. 线程安全:MessageQueue是线程安全的,你可以从任何线程向MessageQueue中发送消息,但只能在拥有该MessageQueue的Looper所在的线程中处理消息。

  5. 配合Looper和Handler使用:MessageQueue通常与Looper和Handler一起使用,它们三者共同构成了Android的消息机制。Looper负责循环地从MessageQueue中取出消息,Handler负责发送消息到MessageQueue,以及处理从MessageQueue中取出的消息。

  6. 内部实现:在内部,MessageQueue使用了一个链表结构来存储消息。这个链表是按照消息的时间戳排序的,因此取出和处理消息的操作都是O(1)的时间复杂度。

MessageQueue、Looper和Handler一起构成了Android的事件处理和线程模型的基础,它们使得我们可以在Android中方便地进行跨线程的通信和任务调度。

4. Handler

概述

在Android中,Handler是一个非常重要的类,主要用于两个目的:一是调度消息或Runnable对象到主线程的MessageQueue(消息队列)中;二是处理MessageQueue中的消息或Runnable对象。

工作原理

以下是Handler类的一些主要特性:

关联Looper:Handler必须关联一个Looper对象。Looper对象维护了一个线程内的消息队列(MessageQueue),用于存放待处理的消息。每个线程只能有一个Looper对象,通常在该线程的主线程中创建。
发送消息:Handler提供了sendMessage(Message msg)、post(Runnable r)等方法,用于将消息或Runnable对象发送到与之关联的Looper的消息队列中。这些消息或Runnable对象会在Looper的消息队列中排队,等待被处理。
处理消息:当Looper从消息队列中取出一个消息时,会调用Handler的handleMessage(Message msg)方法来处理这个消息。这个方法需要在子类中实现,用于定义消息的处理逻辑。

线程切换:Handler通常用于在不同的线程之间传递消息,实现线程间的通信。例如,你可以在子线程中使用Handler发送一个消息,然后在主线程中处理这个消息,从而实现从子线程向主线程发送数据。

三、Handler机制的详解

1. Handler的创建

在Android中,创建Handler的一般步骤如下:

  1. 创建Handler对象: 在Android中,可以通过实例化Handler类的对象来创建一个Handler。Handler类有几个构造方法,可以选择最适合你的需求的构造方法。最常用的一种方法是创建一个新的Handler,它将与当前线程和默认的Looper对象关联:
Handler handler = new Handler();

如果你希望Handler与主线程(即UI线程)关联,你可以使用Looper.getMainLooper()作为参数创建Handler:

Handler handler = new Handler(Looper.getMainLooper());
  1. 重写handleMessage方法: Handler类有一个方法叫做handleMessage,这个方法用于处理消息队列中的消息。你需要重写这个方法,定义你的消息处理逻辑。例如:
Handler handler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        // 在这里处理消息
    }
};
  1. 发送消息: 创建好Handler之后,你可以用它来发送消息。Handler类提供了几个发送消息的方法,例如sendMessage、post等。这些方法将一个Message或一个Runnable对象发送到消息队列中。例如:
Message message = new Message();
handler.sendMessage(message);

或者,你也可以发送一个Runnable对象:

handler.post(new Runnable() {
    @Override
    public void run() {
        // 这里的代码将在Handler关联的线程中运行
    }
});

以上就是在Android中创建Handler的基本步骤。需要注意的是,Handler通常用于在不同的线程之间传递和处理消息,因此在使用Handler时,你需要考虑线程安全的问题。另外,为了避免内存泄露,你应该在不再需要Handler时及时地移除消息和回调。

2. Message的发送

在Android中,Message对象用于在不同线程间传递数据。Handler可以用来发送和处理这些消息。Message通常包含描述事件的数据,例如命令、请求或者需要处理的数据本身。以下是Message发送的详细步骤和注意事项:

1. 创建Message对象

你可以使用多种方式创建和获取Message实例:

  • 使用HandlerobtainMessage方法获取一个Message对象。这种方式是首选,因为它从消息池中获取一个可用的Message实例,避免了频繁的对象创建和回收,从而提高性能。
Message message = handler.obtainMessage();
  • 直接实例化一个Message对象。不推荐这种方式,因为它不使用消息池,每次都创建新的对象。
Message message = new Message();

2. 设置Message的内容

Message对象提供了几个字段用于携带信息:

  • what:用于描述消息类型的用户自定义整数值。
  • arg1arg2:额外的用户自定义整数值。
  • obj:任意对象,用于携带消息携带的对象数据。
  • Bundle:复杂的数据结构,通过setData(Bundle data)设置。

设置消息内容的示例:

message.what = MY_MESSAGE_TYPE;
message.arg1 = someValue;
message.obj = someObject;

3. 发送Message

有多种方式可以发送Message

  • sendMessage(Message msg):发送消息,消息会被加入到消息队列中,并在Looper处理该队列时被处理。
  • sendEmptyMessage(int what):发送一个只有what值的空消息,通常用于简单的通知。
  • sendMessageAtTime(Message msg, long uptimeMillis):将消息发送到指定的未来时间。
  • sendMessageDelayed(Message msg, long delayMillis):发送消息,并在指定的延迟时间后处理。

发送消息的示例:

handler.sendMessage(message);

或者延迟发送:

handler.sendMessageDelayed(message, 1000); // 1秒后发送

4. 处理Message

Handler中重写handleMessage(Message msg)方法来处理接收到的消息。例如:

Handler handler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case MY_MESSAGE_TYPE:
                // 处理消息
                break;
            // 更多case...
        }
    }
};

注意事项

  • 在发送和处理消息时,要注意线程安全问题。例如,避免在多个线程中修改同一个Message对象。
  • 如果消息不再需要,应该使用recycle()方法来回收它,以便它可以被消息池再次使用。这通常在消息处理结束后自动完成。
  • 避免内存泄露,例如在Activity中使用匿名Handler类时,可能会隐式持有Activity的引用。可以使用静态内部类和弱引用来防止泄露。
  • 在不需要时,清理消息队列中的消息和回调,特别是在Activity或Fragment的onDestroy()方法中。使用如handler.removeCallbacksAndMessages(null)来清除所有回调和消息。

遵循这些步骤和注意事项可以确保消息被有效地发送和处理,同时避免常见的问题,如内存泄漏和性能降低。

3. Looper的循环

在Android中,Looper类用于运行一个消息循环。Looper循环是在一个线程中运行的一种特殊机制,这种机制可以将消息队列中的消息逐一取出并处理。

一个线程默认是没有与之关联的Looper的。如果你想在一个线程中使用Looper来处理消息,你必须在该线程中创建一个新的Looper对象。对于主线程(UI线程),Android系统已经默认创建了一个Looper,因此你无需手动创建。

下面是Looper循环的基本步骤:

  1. 准备Looper:在你想要运行Looper循环的线程中,首先需要调用Looper.prepare()方法来准备Looper。这个方法会为当前线程创建一个新的Looper和一个与之关联的消息队列。注意,每个线程只能有一个Looper,因此Looper.prepare()方法只能在一个线程中调用一次。

  2. 创建并关联Handler:在准备好Looper后,你需要创建一个或多个Handler对象,并将它们与Looper关联。这样,这些Handler就可以接收并处理消息队列中的消息了。

  3. 启动Looper循环:准备好Looper并创建好Handler后,你可以调用Looper.loop()方法来启动Looper循环。这个方法会使当前线程进入一个无限循环,不断地从消息队列中取出并处理消息。

下面是一个简单的示例,演示了如何在一个新线程中运行Looper循环:

new Thread(new Runnable() {
    @Override
    public void run() {
        Looper.prepare();

        Handler handler = new Handler();

        Looper.loop();
    }
}).start();

请注意,Looper.loop()方法会阻塞当前线程,直到你决定停止Looper循环。因此,你不应该在主线程(UI线程)中调用这个方法,否则会导致应用无响应。(但注意,这里指的是不应该自己在主线程手动调用loop方法,因为在应用启动的时候,主线程就会自动开启loop循环)

如果你想停止Looper循环,你可以调用Looper对象的quit()quitSafely()方法。这两个方法都可以使Looper.loop()方法返回,从而停止Looper循环。其中,quit()方法会丢弃消息队列中所有未处理的消息,而quitSafely()方法则会在处理完所有延迟到未来的消息后再停止Looper循环。

4. Message的处理

在Android中,消息的处理主要是通过 Handler 类来完成的。Handler 提供了一种机制,可以将一个 Message 对象或一个 Runnable 对象发送到消息队列,并在适当的时候执行。

以下是处理 Message 的基本步骤:

  1. 创建Handler:首先,你需要创建一个 Handler 对象。在创建 Handler 对象时,你需要提供一个实现了 handleMessage(Message msg) 方法的回调对象,或者直接重写 HandlerhandleMessage(Message msg) 方法。这个方法将在 Looper 循环中处理消息。
Handler handler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        // 在这里处理消息
    }
};
  1. 处理Message:在 handleMessage(Message msg) 方法中,你可以编写处理消息的代码。你可以使用 Message 对象的 what 字段来判断消息的类型,然后根据消息的类型进行不同的处理。
@Override
public void handleMessage(Message msg) {
    switch (msg.what) {
        case MESSAGE_TYPE_1:
            // 处理类型1的消息
            break;
        case MESSAGE_TYPE_2:
            // 处理类型2的消息
            break;
        // 更多case...
    }
}

handleMessage(Message msg) 方法中,你还可以访问 Message 对象的其他字段,如 arg1arg2obj,这些字段可以用来携带消息的数据。

请注意,在 handleMessage(Message msg) 方法中处理消息时,应该尽可能快地完成处理,因为这个方法是在 Looper 循环中调用的,如果处理时间过长,可能会阻塞 Looper 循环,导致其他消息无法及时处理。

  1. 回收Message:在 Message 对象被处理完毕后,Android系统会自动将其回收并放入消息池中,以供后续使用。因此,你不应该在 handleMessage(Message msg) 方法执行完毕后再次使用 Message 对象,否则可能会导致不可预知的结果。

四、Handler的具体使用

  1. 在主线程更新UI
public class MyActivity extends AppCompatActivity {

 // 主线程的Handler
 private Handler mainHandler = new Handler(Looper.getMainLooper());

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

     // 示例:启动一个工作线程来执行后台任务
     new Thread(new Runnable() {
         @Override
         public void run() {
             // 执行一些耗时操作
             doBackgroundWork();

             // 更新UI
             mainHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     // 这里更新UI
                     updateUI();
                 }
             });
         }
     }).start();
 }

 private void doBackgroundWork() {
     // 执行耗时操作
 }

 private void updateUI() {
     // 例如,更新一个TextView的文本
     TextView textView = findViewById(R.id.myTextView);
     textView.setText("工作完成!");
 }
}
  1. 在子线程发送消息到主线程
public class MainActivity extends AppCompatActivity {

 private static final int UPDATE_TEXT = 1;

 private Handler handler = new Handler() {
     public void handleMessage(Message msg) {
         switch (msg.what) {
             case UPDATE_TEXT:
                 // 在这里进行UI操作
                 TextView text = (TextView) findViewById(R.id.text);
                 text.setText("Nice to meet you");
                 break;
             default:
                 break;
         }
     }
 };

 @Override
 protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
     Button button = (Button) findViewById(R.id.change_text);
     button.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
             new Thread(new Runnable() {
                 @Override
                 public void run() {
                     Message message = new Message();
                     message.what = UPDATE_TEXT;
                     handler.sendMessage(message); // 将Message对象发送出去
                 }
             }).start();
         }
     });
 }
}
  1. Handler的定时任务与延时任务
public class MainActivity extends AppCompatActivity {
 private Handler handler = new Handler(Looper.getMainLooper());
 @Override
 protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
     // 执行延时任务
     handler.postDelayed(new Runnable() {
         @Override
         public void run() {
             // 这个任务会在3秒后执行
             Log.d("MainActivity", "3 seconds passed!");
         }
     }, 3000);

     // 执行定时任务
     handler.postAtTime(new Runnable() {
         @Override
         public void run() {
             // 这个任务会在指定的时间执行
             Log.d("MainActivity", "It's time!");
         }
     }, SystemClock.uptimeMillis() + 5000);
 }
 @Override
 protected void onDestroy() {
     super.onDestroy();
     // 在Activity销毁时,取消所有未执行的任务,防止内存泄漏
     handler.removeCallbacksAndMessages(null);
 }
}
  1. Handler的异步消息处理
Android中,我们可以使用`Handler`类来处理异步消息。以下是一个示例:

```java
public class MainActivity extends AppCompatActivity {
 private static final int MESSAGE_WHAT = 0;
 private TextView textView;
 private Handler handler = new Handler(Looper.getMainLooper()) {
     @Override
     public void handleMessage(Message msg) {
         if (msg.what == MESSAGE_WHAT) {
             String text = (String) msg.obj;
             textView.setText(text);
         }
     }
 };

 @Override
 protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_main);
     textView = (TextView) findViewById(R.id.textview);
     Button button = (Button) findViewById(R.id.button);
     button.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
             new Thread(new Runnable() {
                 @Override
                 public void run() {
                     try {
                         Thread.sleep(2000);  // 模拟耗时操作
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                     Message message = Message.obtain();  // 获得一个Message对象
                     message.what = MESSAGE_WHAT;  // 设置消息类型
                     message.obj = "Hello, Handler!";  // 设置消息内容
                     handler.sendMessage(message);  // 发送消息到Handler
                 }
             }).start();
         }
     });
 }
}

五、Handler的常见问题及解决方案

内存泄漏问题

Handler在Android中非常常用,但是如果使用不当,很容易导致内存泄漏问题。Handler导致内存泄漏的主要原因是:Handler是一个内部类,它默认持有外部类的引用,如果Handler生命周期比Activity更长,那么就会导致Activity无法被垃圾回收,从而导致内存泄漏。

解决内存泄漏的策略

解决方案有以下几种:

1. 使用静态内部类+弱引用(无论是否有引用指向它,垃圾回收时这个对象都会被回收):
public class MainActivity extends AppCompatActivity {
    private static class MyHandler extends Handler {
        private final WeakReference<MainActivity> mActivity;
        public MyHandler(MainActivity activity) {
            mActivity = new WeakReference<>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = mActivity.get();
            if (activity != null) {
                // TODO: 处理消息
            }
        }
    }
    private final MyHandler mHandler = new MyHandler(this);
    //...
}

在这个例子中,我们创建了一个静态Handler内部类,它不持有外部类的引用。但是我们需要在Activity中处理消息,所以我们传入一个Activity的弱引用,这样就不会阻止Activity被垃圾回收。

2. 在Activity销毁时移除消息和Runnable:
public class MainActivity extends AppCompatActivity {
    private Handler mHandler = new Handler();
    //...
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
}

在Activity的onDestroy()方法中,我们调用Handler的removeCallbacksAndMessages(null)方法,移除所有的消息和Runnable,这样Handler就不再引用这些消息和Runnable,也就不会阻止Activity被垃圾回收。

3. 使用Android架构组件(例如ViewModel和LiveData)来处理后台任务和更新UI,这样可以避免使用Handler,从而避免内存泄漏。

下面这个例子展示了如何使用ViewModel和LiveData来处理后台任务和更新UI。在这个例子中,我们创建一个ViewModel,它包含一个LiveData对象。我们在ViewModel中启动一个后台任务,完成后更新LiveData。然后,在Activity中观察LiveData的变化,并在LiveData更新时更新UI。

public class MyViewModel extends ViewModel {
    private MutableLiveData<String> data = new MutableLiveData<>();

    public LiveData<String> getData() {
        return data;
    }

    public void loadData() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);  // 模拟耗时操作
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                data.postValue("Hello, LiveData!");
            }
        }).start();
    }
}
public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private MyViewModel myViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textview);
        myViewModel = new ViewModelProvider(this).get(MyViewModel.class);
        myViewModel.getData().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                textView.setText(s);
            }
        });
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myViewModel.loadData();
            }
        });
    }
}

在这个例子中,当我们点击按钮时,ViewModel开始一个后台任务,完成后更新LiveData。然后,Activity观察LiveData的变化,当LiveData更新时,Activity更新UI。这样,我们不需要使用Handler,也避免了由于Handler导致的内存泄漏问题。

六、Handler的优缺点分析

1.优点分析

  • 线程通信:Handler可以在不同的线程之间传递消息,这对于实现线程间的通信非常有用。

  • UI操作:Android规定UI操作必须在主线程(UI线程)中进行,而Handler正好可以在子线程中发送消息,然后在主线程中处理消息,从而更新UI。

  • 延时操作:通过Handler的postDelayed()方法,可以方便地实现延时操作。

  • 循环操作:通过Handler的postDelayed()方法和Runnable,可以方便地实现循环操作。

    2. 缺点分析

内存泄漏风险

引起原因:Handler通常作为内部类在Activity中使用,而内部类会隐式持有外部类(即Activity)的引用。如果你在Activity中创建了一个Handler并向它发送了延迟消息或者持续的操作(比如使用postDelayed方法),即使用户已经退出了该Activity,因为消息队列中还有未处理的消息或者正在执行的操作,这会阻止垃圾回收器回收Activity,从而导致内存泄漏。

解决方法:使用静态内部类和弱引用(WeakReference)来引用Activity,或者确保在Activity销毁时移除所有回调和消息,来减少内存泄漏的风险。

生命周期管理复杂

引起原因:Handler本身不会感知到Activity的生命周期。如果Handler在后台线程处理耗时操作,并且尝试更新UI,但此时Activity已经被销毁或重建,可能会导致应用崩溃或者更新了一个不存在的UI。

解决方法:开发者需要手动管理Handler的生命周期,确保在Activity的onDestroy方法中移除所有的消息和Runnable,或者使用现代的架构组件(如LiveData)来处理UI更新,这些组件能更好地管理生命周期。

多线程问题

引起原因:Handler本身是线程安全的,但是当它用于更新UI或共享资源时,如果不当处理,可能会在多线程环境下引入数据竞争或同步问题。

解决方法:需要确保对共享资源的访问是线程安全的,比如使用同步块或者其他并发工具来管理数据的一致性和完整性。

七、Handler与其他通信方式的比较

1. Handler与IntentService比较

IntentService

设计目的:IntentService是为了简化服务的创建和后台线程的使用而设计的,主要用于执行不需要与用户交互且希望顺序完成的任务。
工作线程:IntentService内部创建了一个工作队列和一个工作线程,用于逐一处理所有传递给它的Intent。每个Intent都会触发onHandleIntent(Intent intent)方法的调用,在这个方法中执行实际的后台任务。
生命周期:当所有任务执行完毕后,IntentService会自动停止自己,无需手动管理服务的生命周期。
使用场景:适用于一次性、耗时的操作,例如下载文件、上传数据等。

对比总结

功能定位:IntentService更适合处理一次性后台任务,自动管理线程和服务的生命周期;而Handler更适合用于线程间的通信和在特定线程上执行任务。
线程管理:IntentService自动创建工作线程并在任务完成后自动停止;Handler依赖于创建它的线程,需要手动管理线程和生命周期。
应用场景:IntentService适合执行不需要与用户交互的后台任务;Handler适用于更新UI、处理线程间通信和执行有时间要求的操作。

2. Handler与EventBus比较

EventBus

定义
EventBus是一个第三方库,用于组件间的事件发布/订阅通信,简化了应用组件间的通信。
工作原理:EventBus允许组件订阅特定类型的事件。当事件被发布时,EventBus会通知所有订阅者,并调用它们的事件处理方法。
线程管理:EventBus支持在不同线程模式下处理事件,例如在发布事件的同一个线程、主线程或后台线程中处理。
用途:适用于组件间解耦的通信,例如Activities、Fragments、Services之间的消息传递。
生命周期管理:需要确保在合适的时机注册和注销订阅者,否则可能会导致内存泄露或者崩溃。
比较总结
简洁性:EventBus提供了更简洁的API来实现组件间通信,减少了模板代码的编写,而Handler通常需要更多的样板代码。
解耦
:EventBus在组件间提供了更高的解耦,订阅者不需要知道事件来源,而Handler通常是在已知的发送者和接收者之间进行通信。
性能:Handler作为Android系统的一部分,通常比EventBus拥有更好的性能,尤其是在处理大量消息时。
易用性:EventBus易于使用,可以快速实现组件间通信,但在复杂的应用中可能会使得调试和跟踪事件变得困难。
维护性:EventBus可以减少接口和回调的使用,但过度依赖EventBus可能会导致代码难以维护,而Handler通常更容易跟踪和维护。
在选择Handler或EventBus时,应考虑应用的具体需求,以及对性能、解耦、维护性和易用性的不同考量。随着Android架构组件(如LiveData、ViewModel)的推出,许多开发者也在转向这些更现代的通信机制,它们与Android生命周期紧密集成,提供了更好的生命周期管理和数据持久性。

3. Handler与RxJava比较

RxJava

定义
RxJava是一种基于Java的响应式编程库,它提供了丰富的API来处理异步流式数据处理和事件驱动的程序。
工作原理:RxJava使用Observable流来表示异步数据流,并通过操作符来转换、过滤、合并等操作流。订阅者(Observer)可以订阅这些流,并在数据变化时响应。
线程管理:RxJava通过Scheduler提供了强大的线程控制能力,可以轻松地在不同的线程之间切换任务执行的上下文。
使用场景:适合复杂的异步操作、数据流处理和事件链式处理,例如网络请求、数据库操作、大量的数据处理和组合多个异步任务。
对比总结
复杂性与学习曲线:Handler相对简单,是Android开发的基础部分,学习成本较低;而RxJava功能强大,但有较高的学习曲线。
功能范围:Handler主要用于线程通信和任务调度;RxJava提供了更广泛的功能,如操作符、背压管理、流组合等。
代码表达性:RxJava的链式调用和丰富的操作符使得代码更加表达性强,有助于实现复杂的异步逻辑;Handler的代码表达性相对较弱。
性能:Handler较轻量级,对性能的影响较小;RxJava在管理复杂的数据流时可能对性能有一定影响,尤其是在大量使用时。
维护性:RxJava的响应式编程模式可能使得代码更容易维护,但在错误处理和调试方面可能会更复杂;Handler的代码更直观,但在处理复杂场景时可能导致代码结构混乱。
在选择Handler或RxJava时,应根据项目的具体需求和团队的熟悉程度来决定。对于简单的线程通信和UI更新,Handler可能是足够的。而对于需要处理复杂的异步逻辑、数据流和多线程操作的场景,RxJava可能更合适。随着Android开发的演进,许多开发者也在采用如LiveData和Kotlin Coroutines等新的工具来处理异步操作和线程间通信。

八、Handler的未来趋势和发展(待补充,有大佬了解的可分享到评论区,让我学习学习)

  1. Android系统的更新对Handler的影响
    待补充
  2. Android开发者社区对Handler的改进和优化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值