Android Handler

本文深入探讨了Android中Handler和Looper的工作原理及应用。详细讲解了Handler如何实现跨线程的消息传递,以及Looper如何循环处理消息队列。同时,还介绍了HandlerThread的使用方法及其与主线程交互的过程。

Hanlder简介

相比于 AsyncTask,Handler类允许准确的控制操作的运行时间,而且还可以多次使用,执行的操作会一直运行,直到被显示的停止。每个Handler实例都被包含在一个单独的线程里面。

Handler 有两个主要的用途 :
1. 确保一些调度信息和任务在未来的某一时刻被执行
2. 让一些行为在其他的线程中表现出来

HandlerThread 介绍

HandlerThread 类用于创建一个带有 Looper 的新线程,这个Looper可以用于创建Handler 实例,HandlerThread 的实例必须在调用start() 方法后才可以使用。

构造方法 :
1. HandlerThread(String name)
2. HandlerThread (String name, int priority) //priority 就是线程运行的优先级,由 Process类中的变量 来指定

方法 :
1. getLooper ()

这个方法用于获取与该线程相关联的 Looper,如果该线程没有开启,也即未调用 start() 方法,那么这个方法会返回一个 null 值。如果线程已经开启,这个方法会被阻塞直到 Looper 初始化完成。

  1. getThreadId ()

用于返回线程的标识符

  1. quit ()

停止线程中的Looper,该方法被调用后,任何对Looper 的请求都会失败。比如,sendMessage(Message) 方法会返回false。使用这个方法可能是不安全的,因为在Looper被终止的时候可能还有 Message 未被递交。

  1. quitSafely ()

和上一个方法完成的功能相同,不过这个方法更安全,因为在Looper被终止时,一些未递交的 Message 会因为时间的关系不再递交。

  1. run ()

调用 Runnable 对象中的run() 方法,如果未设置Runnable,就什么也不做。

  1. onLooperPrepared ()

该方法是 protected 类型的,当我们需要在 Looper 的loop() 方法调用前需要完成一些工作,那么可以复写这个方法。

Looper 的简单介绍

Looper 类用来为线程运行 消息循环,默认的Threads 并不包含一个 message loop,为了创建一个,可以在线程中调用 prepare(),然后调用 loop() 去处理messages

官方给出的示例

 class LooperThread extends Thread {
  public Handler mHandler;

  public void run() {
      Looper.prepare();

      mHandler = new Handler() {
          public void handleMessage(Message msg) {
              // process incoming messages here
          }
      };

      Looper.loop();
  }

}

介绍一下Looper 类中的一些方法

  1. Looper getMainLooper ()

获得应用程序的主 Looper,存在于主线程中

  1. Thread getThread ()

返回与该 Looper 关联的 Thread

  1. void loop ()

在线程中运行 message queue,与此方法对应的是 quit() 方,而且这两个方法必须同时出现。

  1. Looper myLooper ()

返回与该线程关联的 Looper,如果该线程没有关联 Looper,就返回 null

  1. MessageQueue myQueue ()

返回与该线程关联的 MessageQueue,如果在未关联 Looper 的线程中调用该方法,会抛出 NullPointerException

  1. void prepare ()

初始化该线程作为一个Looper

  1. void quit ()
  2. void quitSafely()

功能和上面HandlerThread 中介绍的一样

异步消息处理机制

首先,在主线程中创建一个 Handler,并重写 handleMessage() 方法,然后当子线程需要进行 UI 操作时,就创建一个 Message 对象,并通过 Handler 将消息发送出去。之后这条消息会被添加到 MessageQueue 的队列中进行等待,而 Looper 会一直尝试从 MessageQueue 中取出待处理消息,最后分发回 Handler 的 handleMessage() 方法中。由于 Handler 是在主线程中创建的,此时的 handleMessage() 也会在主线程中得到执行。

一个Message 经过一个流程的辗转,从子线程进入到主线程,从不能更新 UI 到可以更新 UI,这就是异步消息处理机制的核心思想。

Hanlder 的简单使用

由于 Handler 中的方法太多,就不逐一介绍了,下面来介绍 几种给 Handler 发送信息的方法。

  1. Message.obtain(mHanlder,FLAG,data).sendToTarget()

创建一个标识为 FLAG,数据为 data 的Message,立刻将其发送到 Handler 去执行

  1. mHandler.sendEmptyMessage(FLAG)

立刻给 Handler 发送一个带有标识的空消息

  1. mHanlder.sendEmptyMessageAtTime(FLAG,1000)

给 Handler 发送一个简单的空消息,该消息会在一秒后被递交给 Looper

  1. mHandler.sendEmptyMessageDelayed(FLAG,1000)

效果同上

下面利用 Handler 来更改 UI
public class MainActivity extends AppCompatActivity {

public static final int FLAG = 0;
private Button mButton;
private TextView mTextView;
private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        if (msg.what == FLAG) {
            mTextView.setText("After Changer");
        }
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mButton = (Button) findViewById(R.id.start);
    mTextView = (TextView) findViewById(R.id.tx);

    mButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mHandler.sendEmptyMessage(FLAG);
                }
            }).start();
        }
    });
  }
}

注意,这个时候mHandler 使用的是默认 Looper,也即 MainLooper,我们也可以通过 HandlerThread 来使用自己的 Looper执行该操作。

public class MainActivity extends AppCompatActivity implements Handler.Callback {

public static final int FLAG = 0;
private Button mButton;
private TextView mTextView;

private HandlerThread mHandlerThread;
private Handler mHandler;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mHandlerThread = new HandlerThread("MyHandlerThread");
    mHandlerThread.start();
    mHandler = new Handler(mHandlerThread.getLooper(), this);

    mButton = (Button) findViewById(R.id.start);
    mTextView = (TextView) findViewById(R.id.tx);

    mButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    mHandler.sendEmptyMessage(FLAG);
                }
            }).start();
        }
    });

}

@Override
public boolean handleMessage(Message message) {
    if (message.what == FLAG) {
        mTextView.setText("After Change");
    }
    return true;
}

}

这个时候会出现一个错误,因为我们自己的 Looper 是没有权限去更新 UI 的,如果想要更新 UI ,可以在 Handler 的构造方法中使用 getMainLooper() 方法。

以上我们都使用使用默认的 mHandler 和 HandlerThread,我们也可以写一个类继承自 HandlerThread 或者 Handler,在自定义的类中可以执行一些耗时任务,因为这个时候所有的任务都是在子线程中执行的,并不会阻塞主线程。

关于 AsyncTask 和 Handler 的选择,如果不是很频繁的执行一个操作,而且操作可以在较短时间内完成,使用 AsyncTask 是十分方便的。如果需要安排操作的时间或者需要快速间隔的执行某操作,Handler 是不错的选择。

### Android Handler 使用与常见问题 在 Android 开发中,`Handler` 是用于在不同线程之间通信的重要工具。它主要用于将任务(如 UI 更新)从后台线程传递到主线程执行,从而避免主线程阻塞并确保 UI 的流畅性。 #### Handler 的基本使用 `Handler` 通常与 `Looper` 和 `MessageQueue` 配合使用。每个 `Handler` 实例都与一个线程及其消息队列相关联。通过 `Handler`,可以发送和处理消息(`Message`)或运行 `Runnable` 对象。 以下是一个简单的 `Handler` 示例: ```java Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override public void run() { // 更新 UI 的代码 } }); ``` 在子线程中使用 `Handler` 时,需要确保该线程已经调用了 `Looper.prepare()` 并启动了 `Looper.loop()`,否则会抛出异常。 #### 常见问题 1. **内存泄漏** 如果 `Handler` 持有对 `Activity` 或 `Context` 的引用,并且在子线程中长时间运行,可能会导致内存泄漏。为避免这种情况,通常建议使用静态内部类或弱引用(`WeakReference`)来持有外部类的引用。 ```java private static class MyHandler extends Handler { private final WeakReference<Activity> mActivityReference; public MyHandler(Activity activity) { mActivityReference = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { Activity activity = mActivityReference.get(); if (activity != null) { // 处理消息 } } } ``` 2. **消息延迟与顺序问题** 使用 `sendMessageDelayed()` 或 `postDelayed()` 可以实现延迟执行任务的功能。然而,如果多次发送相同的消息或 `Runnable`,可能会导致消息堆积,影响程序的响应速度。为避免此类问题,可以使用 `removeCallbacks()` 或 `removeMessages()` 来清理不再需要的消息。 3. **线程安全问题** 虽然 `Handler` 本身是线程安全的,但如果多个线程同时操作共享的数据结构,仍然需要额外的同步机制。例如,使用 `synchronized` 关键字或 `ReentrantLock` 来确保数据的一致性。 4. **主线程阻塞** 如果在 `Handler` 中执行耗时操作,可能会阻塞主线程,导致 ANR(Application Not Responding)错误。应避免在 `Handler` 中执行网络请求、数据库查询等耗时任务,建议使用 `AsyncTask`、`Thread` 或 `ExecutorService` 来处理这些操作。 5. **Looper 未初始化** 在非主线程中使用 `Handler` 时,必须手动调用 `Looper.prepare()` 来创建 `MessageQueue`,并在最后调用 `Looper.loop()` 来开始处理消息。如果忘记调用这些方法,会导致运行时异常。 ```java new Thread(new Runnable() { @Override public void run() { Looper.prepare(); Handler handler = new Handler(); Looper.loop(); } }).start(); ``` 6. **消息优先级** 默认情况下,所有消息的优先级相同。如果需要处理高优先级的任务,可以通过 `Message` 的 `setAsynchronous()` 方法来标记异步消息,但这需要底层支持(如 `ViewRootImpl` 的 `Choreographer`)。 #### 总结 `Handler` 是 Android 中实现线程间通信的核心机制之一,但其使用过程中需要注意内存管理、线程安全、消息顺序等问题。合理使用 `Handler` 可以提升应用的响应能力和用户体验。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值