Android的handler

本文详细解析了Android中Handler机制的工作原理,包括如何在主线程和子线程间使用Handler进行消息传递,避免Can’t create handler inside thread that has not called Looper.prepare()错误,以及Handler、Looper、MessageQueue之间的关系。同时,对比了handler.post(Runnable)和handler.sendMessage(Message)的区别,介绍了AsyncTask异步任务的使用方式。

1.Google官方建议开发人员使用Handler实现异步刷新UI,我们在平常的工作中也很好地采纳了这个提议:首先在主线程中建立Handler,然后在子线程中利用handler.sendMessage(message)发送消息至主线程,最终消息在handleMessage(Message msg) {}得到相应的处理。

package com.ysl.myandroidbase.activity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

public class HandleActivity extends AppCompatActivity {
    public static final int MSG_CODE = 0;
    public static final int MSG_CODE2 = 1;
    public static final String TAG = "HandleActivity";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        doThread();
        new MyThread().start();
    }

    //启动一个耗时线程
    public void doThread() {
        new Thread() {
            public void run() {
                try {
                    Log.e("HandleActivity子线程Name:", Thread.currentThread().getName());
                    //在子线程运行
                    Thread.sleep(2000);
                    //完成后,发送消息
                    Message message = Message.obtain();
                    message.what = MSG_CODE;
                    Bundle bundle = new Bundle();
                    bundle.putString("data", "hi");
                    message.setData(bundle);
                    handler.sendMessage(message);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    public static Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case MSG_CODE:
                    Log.d(TAG, msg.getData().getString("data")+"  线程:"+Thread.currentThread().getName());
                    break;
                case MSG_CODE2:
                    Log.d(TAG, msg.getData().getString("data")+"  线程:"+Thread.currentThread().getName());
                    break;
            }
        }
    };
}
package com.ysl.myandroidbase.activity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import static com.ysl.myandroidbase.activity.HandleActivity.MSG_CODE2;

public class MyThread extends Thread {

    public static final String TAG = "MyThread";

    @Override
    public void run() {
        super.run();
        try {
            Log.e("MyThread子线程Name:", Thread.currentThread().getName());

            Message message = Message.obtain();
            message.what = MSG_CODE2;
            Bundle bundle = new Bundle();
            bundle.putString("data", "sdfbsdkjgsdklj");
            message.setData(bundle);
            HandleActivity.handler.sendMessage(message);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

上面时一个简单的发送handler消息过程。可以看到都是拿到同一个handler对象来发送消息的。

那如果在子线程中创建handler会怎样呢:

直接报错:Can’t create handler inside thread that has not called Looper.prepare()。

这是由于子线程中的Looper.prepare和Looper.loop都没有被调用,那么在子线程中应该怎么使用呢:

public class MyThread extends Thread {

    public static final String TAG = "MyThread";

    @Override
    public void run() {
        super.run();
        try {
            Log.e("MyThread子线程Name:", Thread.currentThread().getName());

            //在子线程运行
            Thread.sleep(2000);
            Looper.prepare();
            Handler handler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    switch (msg.what){
                        case 0:
                            Log.d(TAG, "收到消息  线程:"+Thread.currentThread().getName());
                            break;
                    }
                }
            };
            //完成后,发送消息
            handler.sendEmptyMessage(0);
            Looper.loop();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在主线程,系统会自动调用Looper.prepareMainLooper()方法创建主线程的Looper和消息队列MessageQueue,所以我们从来没用过Looper.prepare()。

2.handler机制

一个线程对应一个Looper;一个Looper对应一个消息队列;一个线程对应一个消息队列;线程,Looper,消息队列三者一一对应。

即:一个线程里有唯一的一个Looper对象和唯一的一个MessageQueue。

但是:可以有多个handler对象,它们可以发送消息和处理消息。

参考:https://blog.youkuaiyun.com/lfdfhl/article/details/53332936

3.如果一个Activity有多个handler时候,怎样知道handler1发送的消息不会被handler2接收,同理handler2发送的消息不会被handler1接收。

我们来追踪源码,当发送消息时:

可以看到在enqueueMessage方法中,把message的target属性赋值为this,this指的就是当前的handler对象。

接下来再看Looper.loop方法是怎么取出message的:

可以看到里面是一个死循环;取出message。

然后获取到message的target值,并调用了dispatchMessage方法:

上面我们提到target其实就是handler对象,我们再去看handler的dispatchMessage方法:

可以发现就是调用了handleMessage方法,也就是我们重写的handler的方法,在里面可以更新UI等操作。

也就是,每个message中都携带了发送自己的handler对象,当Looper.loop取出message时,就会把它携带的handler对象拿出来,并调用handleMessage方法。这样就达到了:谁发送的消息,最后由谁来处理。冤有头债有主。

4.handler.post(Runnable runnable)和handler.sendMessage(Message message)

这两个不同的方法在本质上是相同的——Handler发送了一条消息。在该示例中handler是在主线程中创建的,所以它当然会在主线程中处理消息;如此以来该Runnable亦会在主线程中执行;所以,在Runnable的run()方法中执行耗时的操作是不可取的容易导致应用程序无响应。

调用view.post(Runnable runnable)会在哪个线程,看看源码:

可以看到又调用了handler.post(Runnable runnable)方法。

调用Activity.runOnUiThread(Runnable runnable)方法会在哪个线程,看看源码:

也同样调用了handler.post(Runnable runnable)方法。

handler.post(Runnable runnable)、view.post(Runnable runnable)、Activity.runOnUiThread(Runnable runnable)的runnable均会在主线程中执行,所以切勿在其run()方法中执行耗时的操作。

5.Asynctask异步任务

public class HandleActivity extends AppCompatActivity {
    public static final String TAG = "HandleActivity";
    private TextView tv;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = findViewById(R.id.tv1);
        asyncTask.execute();
        tv.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                asyncTask.cancel(false);
            }
        });
    }

    private AsyncTask asyncTask = new AsyncTask() {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            Log.d(TAG, "onPreExecute调用了");
            tv.setText("准备加载");
        }

        @Override
        protected Object doInBackground(Object[] objects) {
            Log.d(TAG, "doInBackground调用了");
            try {
                int count = 0;
                int length = 1;
                while (count < 99) {

                    count += length;
                    // 可调用publishProgress()显示进度, 之后将执行onProgressUpdate()
                    publishProgress(count);
                    // 模拟耗时任务
                    Thread.sleep(50);
                }
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Object[] values) {
            super.onProgressUpdate(values);
            Log.d(TAG, "onProgressUpdate调用了");
            tv.setText("loading..." + values[0] + "%");
        }

        @Override
        protected void onPostExecute(Object o) {
            super.onPostExecute(o);
            Log.d(TAG, "onPostExecute调用了");
            tv.setText("加载完成");
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
            Log.d(TAG, "onCancelled调用了");
            tv.setText("取消加载");
        }

    };
    
}

异步任务利用的是线程池+handler的原理,来实现子线程请求处理数据,然后通过handler发送消息到主线程更新UI。doInBackground()方法不可以更新UI,其他的方法都可以。线程池的使用,减少的资源的消耗,提高了程序的性能。

参考:https://www.jianshu.com/p/ee1342fcf5e7

### 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` 可以提升应用的响应能力和用户体验。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值