[android异步]回顾Handler知识及HandlerThread应用及源码分析

本文深入探讨了Android中的Handler机制,包括主线程发送并接收消息的情境,以及子线程如何通过Handler与主线程通信。接着介绍了HandlerThread的原理,它如何启动子线程并维护Looper进行消息处理。文中还提供了具体的代码示例和源码分析,展示了如何使用HandlerThread每两秒更新主线程UI数据,并通过日志验证了操作发生在预期的线程中。

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

本文分两部分:Handler部分和HandlerThread部分。

一、handler知识:

情境一:主线程发消息,主线程接收。

UI如下:


代码如下:

public class MainAct extends Activity {
    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    text.setText("文案又改了");
                break;
            }
        }
    };
    private TextView text;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);
        text = (TextView) findViewById(R.id.text);

    }

    public void clickme(View view){
        handler.post(new Runnable() {
            @Override
            public void run() {
                text.setText("文案改变");
            }
        });
    }

    public void clickme2(View view){
        handler.sendEmptyMessage(1);
    }
}
点击后效果如下:

handler的两种用法:一是通过post(runnable),一是sendEmptyMessage(),需要知道的事,不管是runnable还是sendEmptyMessage传递消息后的handleMessage方法都是在主线程中执行的。

情景二、子线程发消息,主线程接收:

又称为线程间通信。

常见例子举例:发送验证码倒计时处理。


代码如下:

public class MainAct2 extends Activity {
    private boolean flag = false;
    private static int count = 10;
    private TextView send_message;
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 1:
                    send_message.setText(count + "");
                    count--;
                    break;
                case 2:
                    send_message.setText("重新发送");
                    break;
            }
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout2);
        send_message = (TextView) findViewById(R.id.send_message);
        send_message.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (flag == false) {
                    flag = true;
                    count = 10;
                    //子线程启动
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            while (flag) {
                                handler.sendEmptyMessage(1);
                                try {
                                    Thread.sleep(1000);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                                if (count == 0) {
                                    flag = false;
                                    handler.sendEmptyMessage(2);
                                }
                            }
                        }
                    }).start();
                }
            }
        });
    }
}
此处启动一个子线程,子线程中有一个while死循环,通过flag来判断是否阻塞。点击判断是否阻塞,不阻塞时发送消息给主线程,让主线程更新UI每一秒钟更新一次。

此处只有一个子线程,子线程中有一个死循环。

二、HanlderThread知识:

HanlderThread就类似这种模式。HandlerThread继承自Thread,所以HandlerThread的start方法同样会启动一个子线程,并执行自身的run方法(源码1)。而不同与Thread的是,HandlerThread中初始化了Looper,然后开启了Loop 轮训。

对上述描述的源码分析如下:HandlerThread的run方法如下(源码1

@Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
Looper.prepare(),方法初始化了Looper。如果有对handler源码理解的可以知道,handler初始化的时候会默认当前线程的Looper(如果当前线程是主线程,则获取UI线程的Looper;如果是子线程,则获取子线程Looper,一般情况下子线程需要通过Looper.prepare()方法自己初始化自己的Looper。),通过mLooper = Looper.myLooper();  

public static @Nullable Looper myLooper() {  
        return sThreadLocal.get();  
    }  
假如是主线程,一个app应用程序启动会有这么一个流程:在ActivityManager启动startActivity,执行ActivityThread的main方法中,执行Looper.prepareMainLooper(),初始化主线程的Looper。

Looper.prepareMainLooper()会通过prepare(false)创建一个不可退出的轮

回到我们这里,我们Looper.prepare()会执行prepare(true)创建一个可以退出且需要退出的轮询。

 public static void prepare() {
        prepare(true);
    }
结合下面代码:quitAllowed为true则可以退出,如果为false则不可退出。

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
使用构造方法new Looper(quitAllowed),创建一个独属于子线程自身的Looper轮询。

看一个HandlerThread的例子:每两秒更新一下主UI线程的数据。

UI如下:

代码如下:

public class MainAct3 extends Activity {
    private TextView send_message;

    private android.os.HandlerThread handlerThread;

    private Handler mainHandler = new Handler();

    private Handler sonHandler;

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

        handlerThread =new HandlerThread("mythread");
        handlerThread.start();
        Log.e("Thread","1、"+Thread.currentThread().getName());

        //使用子线程的Looper,减轻主UI线程的压力。通过sonHandler.sendEmptyMessage(1)自己发消息给自己实现while死循环效果
        sonHandler = new Handler(handlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                //此Handler不在主UI线程中,在子线程中
                Log.e("Thread","2、"+Thread.currentThread().getName());
                update();
                sonHandler.sendEmptyMessage(1);
            }
        };

        send_message = (TextView) findViewById(R.id.send_message);
        //主线程触发子线程开始更新
        send_message.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sonHandler.sendEmptyMessage(1);
            }
        });
    }

    //子线程中执行休眠,休眠后发送消息给主UI线程
    public void update(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        mainHandler.post(new Runnable() {
            @Override
            public void run() {
                String result = "每隔2秒更新一下数据:";
                result += Math.random();
                send_message.setText(result);
                Log.e("Thread","3、"+Thread.currentThread().getName());
            }
        });
    }

}
代码说明:此例不同于上面的Handler+thread的地方就在于此处使用的Looper是子线程的Looper(见此代码handlerThread.getLooper()),所以Handler的handleMessage方法也在子线程中。 

sonHandler = new Handler(handlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                //此Handler不在主UI线程中,在子线程中
                Log.e("Thread","2、"+Thread.currentThread().getName());
                update();
                sonHandler.sendEmptyMessage(1);
            }
        };
所以update方法是在子线程中执行的休眠操作。再通过主UI线程的Handler发送消息给主线程实现更新UI。

最后看看打印的日志来确定当前线程:可以看出sonHandler的handleMessage的确就在子线程mythread中。

01-11 18:21:07.244 8610-8610/haibo.com.helloworld E/Thread: 1、main
01-11 18:21:09.234 8610-8630/haibo.com.helloworld E/Thread: 2、mythread
01-11 18:21:11.244 8610-8630/haibo.com.helloworld E/Thread: 2、mythread
01-11 18:21:11.244 8610-8610/haibo.com.helloworld E/Thread: 3、main
01-11 18:21:13.244 8610-8630/haibo.com.helloworld E/Thread: 2、mythread
01-11 18:21:13.244 8610-8610/haibo.com.helloworld E/Thread: 3、main
01-11 18:21:15.244 8610-8630/haibo.com.helloworld E/Thread: 2、mythread
01-11 18:21:15.254 8610-8610/haibo.com.helloworld E/Thread: 3、main
















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值