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异步任务的使用方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值