Android中的Thread, Looper和Handler机制(附带HandlerThread与AsyncTask)

转载自:Android 中的 Thread, Looper 和 Handler 机制 · 笔试面试知识整理 (hit-alibaba.github.io)

Thread,Looper和Handler的关系

与Windows系统一样,Android也是消息驱动型的系统。引用一下消息驱动机制的四要素:

  • 接收消息的“消息队列”
  • 阻塞式地从消息队列中接收消息并进行处理的“线程”
  • 可发送的“消息的格式”
  • “消息发送函数”

与之对应,Android中的实现对应了

  • 接收消息的“消息队列” ——【MessageQueue】
  • 阻塞式地从消息队列中接收消息并进行处理的“线程” ——【Thread+Looper】
  • 可发送的“消息的格式” ——【Message】
  • “消息发送函数”——【Handler的post和sendMessage】

一个Looper类似一个消息泵。它本身是一个死循环,不断地从 MessageQueue 中提取 Message 或者 Runnable 。而 Handler 可以看做是一个 Looper 的暴露接口,向外部暴露一些事件,并暴露 sendMessage() 和 post()函数。

在安卓中,除了UI线程/主线程以外,普通的线程(先不提 HandlerThread)是不自带 Looper的。想要通过UI线程与子线程通信需要在子线程内自己实现一个 Looper。开启Looper分三步走

  1. 判定是否已有 Looper并 Looper.prepare()
  2. 做一些准备工作(如暴露handler等)
  3. 调用 Looper.loop(),线程进入阻塞态

由于每一个线程内最多只可以有一个 Looper,所以一定要在 Looper.prepare()之前做好判定,否则会抛出 java.lang.RuntimeException: Only one Looper may be created per thread。为了获取Looper的信息可以使用两个方法:

  • Looper.myLooper()
  • Looper.getMainLooper()

Looper.myLooper()获取当前线程绑定的 Looper,如果没有返回 nullLooper.getMainLooper()返回主线程的 Looper,这样就可以方便的与主线程通信。注意:Thread的构造函数中调用Looper.myLooper只会得到主线程的Looper,因为此时新线程还未构造好

下面给一段代码,通过Thread,Looper和Handler实现线程通信:

MainActivity.java

public class MainActivity extends Activity {
    public static final String TAG = "Main Acticity";
    Button btn = null;
    Button btn2 = null;
    Handler handler = null;
    MyHandlerThread mHandlerThread = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn = (Button)findViewById(R.id.button);
        btn2 = (Button)findViewById(R.id.button2);
        Log.d("MainActivity.myLooper()", Looper.myLooper().toString());
        Log.d("MainActivity.MainLooper", Looper.getMainLooper().toString());


        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mHandlerThread = new MyHandlerThread("onStartHandlerThread");
                Log.d(TAG, "创建myHandlerThread对象");
                mHandlerThread.start();
                Log.d(TAG, "start一个Thread");
            }
        });

        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(mHandlerThread.mHandler != null){
                    Message msg = new Message();
                    msg.what = 1;
                    mHandlerThread.mHandler.sendMessage(msg);
                }

            }
        });
    }
}

MyHandlerThread.java

public class MyHandlerThread extends Thread {
    public static final String TAG = "MyHT";

    public Handler mHandler = null;

    @Override
    public void run() {
        Log.d(TAG, "进入Thread的run");
        Looper.prepare();
        Looper.prepare();
        mHandler = new Handler(Looper.myLooper()){
            @Override
            public void handleMessage(Message msg){
                Log.d(TAG, "获得了message");
                super.handleMessage(msg);
            }
        };
        Looper.loop();
    }
}

HandlerThread 和 AsyncTask

HandlerThread

Android为了方便对ThreadHandler进行封装,也就是HandlerThread。文档中对HandlerThread的定义是:

Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.

HandlerThread继承自Thread,说白了就是Thread加上一个一个Looper。分析下面的代码:

public class MyHandlerThread extends HandlerThread{
    @Override
    public void run(){
        if(Looper.myLooper == null){
            Looper.prepare();
        }
        super.run();
    }
}

会抛出java.lang.RuntimeException: Only one Looper may be created per thread错误。如果我们把super.run()注释掉就不会有这样的错误。显然在super.run()中进行了Looper的绑定。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android Handler 的工作原理是通过消息队列(Message Queue)和线程(Thread)来实现的。当我们使用 Handler 发送消息或者执行延迟任务时,其实是将消息或任务封装成 Message 对象,然后将其加入到消息队列。然后,Handler 所在的线程会从消息队列取出消息,并按照一定的逻辑进行处理。 在 Android ,UI 线程(也称为主线程)是用来处理用户界面操作的,例如响应用户的点击事件、更新 UI 界面等。而 Handler 默认是与 UI 线程关联的,通过将消息发送到消息队列,然后由 UI 线程处理,这样可以保证在 UI 线程执行相关操作。 当 Handler 执行耗时操作时,如果没有采取适当的措施,可能会导致界面卡顿或 ANR(Application Not Responding)问题。为了避免这种情况发生,通常有以下几种方式: 1. 在主线程避免执行耗时操作:将耗时操作放在子线程执行,例如使用 AsyncTaskThread 等方式。 2. 使用 Handler 的 post 方法:通过 post 方法将耗时操作放在 Runnable 对象,然后通过 Handler.post 方法将其发送到主线程执行。 3. 使用 HandlerThreadHandlerThread 是一个带有 Looper 的线程,可以用于处理耗时操作,并且不会影响主线程的响应速度。 虽然 Handler 在处理消息时会阻塞线程,但由于它是与 UI 线程关联的,所以在主线程阻塞一段时间并不会导致 ANR。但是,如果在主线程执行耗时操作(如网络请求、数据库操作等),则会导致线程阻塞时间过长,从而触发 ANR。因此,在开发需要注意避免在主线程执行耗时操作,以保证应用的响应性和用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值