前两天 ,看一个demo 发现了里面有使用HandlerThread。心生好奇,自己到网上看了 看资料,又翻了一下开发文档。写出来 抛砖引玉。
我第一次看见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方法
看我划红线的地方。service服务。我们看一下 getService()方法
我们可以看见 返回了 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 其实可以应用于这些情况下面:
public Handler(Looper looper, Callback callback)
这个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;
}
}
}