天天看点

HandlerThread见解之它不是更新ui的

前两天 ,看一个demo 发现了里面有使用HandlerThread。心生好奇,自己到网上看了 看资料,又翻了一下开发文档。写出来 抛砖引玉。

HandlerThread不能更新ui

我第一次看见handlerThread时候,因为,他带着Handler还以为是更新UI的,其实不是的。在验证的过程中,我还发现了 我以前的一个知识点错误(受一篇博文的引导。。。)。以后,任何东西 还是自己验证一下比较好。

首先,我先来说HandlerThread是啥。通俗的来说就是一个子线程具有了 looper和handler这样的机制 ,当这个子线程创建了handler的时候,别的线程可以通过handler来发送信息,并且可以在这个handler里面执行耗时的操作,除了不能更新ui,这样的机制,我们自己也可以通过

Looper.prepare();
Handler inner = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        System.out.println("内部handler 里面" + Thread.currentThread().getName() + "     ");
        Toast.makeText(MainActivity.this,"haahh",Toast.LENGTH_LONG).show();
        textView.setText(">>>>>>>>");
    }
};
inner.sendEmptyMessage(1);
Looper.loop();
           

不过,这样多费劲是吧。所以 谷歌给我们造了HandlerThread这个类 就是完成这样作用的。

如果,你运行以上代码会抛出异常:

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

这个就是只有主线程才能更新view。

看上面代码,我们在handleMessage(Message msg)这个方法里面 想更新 textView.setText(">>>>>>>>")。所以报错 了。这个首先说明了子线程联系的handler是不能更新线程的。

不过,如果 我们把textView.setText()注释的话。

Looper.prepare();
Handler inner = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        System.out.println("内部handler 里面" + Thread.currentThread().getName() + "     ");
        Toast.makeText(MainActivity.this,"haahh",Toast.LENGTH_LONG).show();
        //textView.setText(">>>>>>>>");
    }
};
inner.sendEmptyMessage(1);
Looper.loop();
           

运行 你会发现toast弹出了!!!!

这是怎么回事了?首先,我们看toast的show方法

HandlerThread见解之它不是更新ui的

看我划红线的地方。service服务。我们看一下 getService()方法

HandlerThread见解之它不是更新ui的

我们可以看见 返回了 INotificationManager对象的静态实例。那么这个INotificationManager是什么呢。看上面的英文注释。翻译一下的意思是:

下面的所有内容都是与通知服务的交互,通知服务处理这些系统范围内的正确排序。
           

这个呢 我们可以发现通知服务,其实到这里呢,应该有人就明白了。Android 有一个专门的后台服务Service 来进行Toast 等一些通知的显示 弹出。 而我们知道在Service里面 我们是可以弹出Toast的。所以 ,这个说明了 一些通知信息的显示,都是由后台的 Service来承担的。我们在前台 只是一个通知作用

上面略微跑题

接下来 我们来说HandlerThread

HandlerThread handlerThread = new HandlerThread("first");
handlerThread.start();

final Handler handler = new Handler(handlerThread.getLooper(),new HandlerCallback()) {
    int count = 0;
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        Toast.makeText(MainActivity.this,"haahh",Toast.LENGTH_LONG).show();
        textView.setText(">>>>>>");
    }
};


           

如果,我们这么用代码的会和上面抛出一样的错误。因为这段代码和上面那段代码的作用是相同的。所以HandlerThread是不能更新UI的

正确使用是 我们仍然需要主线程的handler来更新ui,HandlerThread来接受其他子线程发送过来的消息,这些消息的处理不需要更新ui

//这个是主线程Handler 实例 还有一个知识点就是如果new Handler时候没有给她传递looper的话,他会默认的去当前运行线程的looper进行联系
       MainHandler mainHandler = new MainHandler(this);


       HandlerThread handlerThread = new HandlerThread("first");
       handlerThread.start();
        //这是子线程联系的handler 传入了handlerThread的looper
        final Handler childHandler = new Handler(handlerThread.getLooper()) {
            int count = 0;
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                System.out.println("子线程 的handler 接受到数据  现在 将数据发送给 主线程 执行 "+Thread.currentThread().getName());
                //发送消息给 主线程的handler 让她更新ui
                Message.obtain(mainHandler, 1, ++count).sendToTarget();
            }
        };



    // 开启一个子线程 调用childHandler 发送信息   
    button.setOnClickListener(new View.OnClickListener() {
         @Override
        public void onClick(View v) {

           new Thread(new Runnable() {
              @Override
               public void run() {
                System.out.println("子线程发送数据 " +  Thread.currentThread().getName());
                //发送给handlerThread的handler 
                 childHandler.sendEmptyMessage(1);
              }
         }).start();
     }
  });


    //这个是主线程的Handler 定义成这种形式,可以防止内存泄露
   static class MainHandler extends Handler{
        private WeakReference<MainActivity> mainActivityWeakReference;
        
        MainHandler(MainActivity mainActivity) {
            this.mainActivityWeakReference =new WeakReference<>(mainActivity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            mainActivityWeakReference.get().textView.setText(msg.obj+"");
        }
    }
           

在上面我们可以看见,我们开启了一个子线程,在子线程里面调用了 HandlerThread的childHandler发送信息,childHandler接受到消息,再调用主线程的mainHandler刷新ui。

通过上面的例子来看 ,大家肯定感觉 这不是多此一举么。一开始 我也是这么想的,后来 我仔细思考,这个HandlerThread 其实可以应用于这些情况下面:

  1. 比如,你同时有好几个子线程,你需要得到他们运算时候发出的信息,以前使用接口回调,还得实现的 ,如果 好几个子线程 岂不是要好几个接口。
  2. 比如 还有 一个子线程运算完 你想知道结果,也可以通过这个来实现。以前你使用callable,接口的话 也还是 比较复杂一点的。

小提示

这个handler的构造方法中的callback 官方是这么说的

在实例化处理程序时可以使用回调接口,以避免必须实现自己的处理程序子类。
           

简而言之呢,就是 你可以实现这个回调接口,这样你在实例化handler时候,就不要重写handleMessag()这个方法了。

//在实例化处理程序时可以使用回调接口,以避免必须实现自己的处理程序子类。
    private class HandlerCallback implements Handler.Callback {
        @Override
        public boolean handleMessage(Message msg) {
            System.out.println("我是handler Callback " + Thread.currentThread().getName());
            //返回true意味着即使子类重写了handlerMessage方法 也不会被调用。
            //返回false  如果有子类的话,会调用handlerMessager方法
            return true;
        }
    }
           

callback 已经实现了 handler方法

还有 调用HandlerThread的handler.post(Runnable) 也是在 子线程 运行的。

贴上全部代码

package com.example.handlerthread;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import java.lang.ref.WeakReference;

public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private Button   button;

    MainHandler mainHandler = new MainHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.text);
        button = findViewById(R.id.button);
        HandlerThread handlerThread = new HandlerThread("first");
        handlerThread.start();

        final Handler childHandler = new Handler(handlerThread.getLooper(), new HandlerCallback()) {
            int count = 0;

            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                System.out.println("子线程 的handler 接受到数据  现在 将数据发送给 主线程 执行 " + Thread.currentThread().getName());
                Message.obtain(mainHandler, 1, ++count).sendToTarget();
            }
        };
        
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("子线程发送数据 " + Thread.currentThread().getName());
                        childHandler.sendEmptyMessage(1);
                    }
                }).start();
            }
        });
    }
    //这个是主线程的Handler 定义成这种形式,可以防止内存泄露
    static class MainHandler extends Handler {
        private WeakReference<MainActivity> mainActivityWeakReference;


        MainHandler(MainActivity mainActivity) {
            this.mainActivityWeakReference = new WeakReference<>(mainActivity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            mainActivityWeakReference.get().textView.setText(msg.obj + "");
        }
    }

    //在实例化处理程序时可以使用回调接口,以避免必须实现自己的处理程序子类。
    private class HandlerCallback implements Handler.Callback {
        @Override
        public boolean handleMessage(Message msg) {
            System.out.println("我是handler Callback " + Thread.currentThread().getName());
            return true;
        }
    }
}