Android 线程间通信之Handler
一:前言
Android为了确保UI操作的线程安全,规定所有的UI操作都必须在主线程(UI线程)中执行,决定了UI线程中不能进行耗时任务,在开发过程中,需要将网络,IO等耗时任务放在工作线程中执行,工作线程中执行完成后需要在UI线程中进行刷新,因此就有了Handler进程内线程通信机制,当然Handler并不是只能用在UI线程与工作线程间的切换,Android中任何线程间通信都可以使用Handler机制。
二:使用Handler实现线程间通信
1.UI线程中使用Handler
UI线程中使用Handler非常简单,因为框架已经帮我们初始化好了Looper,只需要创建一个Handler对象即可,之后便可以直接使用这个Handler实例向UI线程发消息(子线程--->UI线程)
private Handler handler=new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
//处理消息
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_six);
}
}
这种方式会导致内存泄露。
我们通过Handler发送消息,在Message对象中会持有当前Handler对象的引用,在Java中非静态成员类、内部类、匿名类会持有外部对象的引用(这里在源码中有提到),而Looper是线程局部变量,其生命周期与UI线程相同,Looper持有MessageQueue的引用,MessageQueue持有Message的引用,当通过Handler发送一个延时消息未处理之前用户已经离开当前Activity,会导致Activity不能及时释放而内存泄漏。
解决思路:
1.官方推荐的一种:
private Handler handler=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
switch (msg.what){
case 1:
//处理子线程发过来的消息
Toast.makeText(SixActivity.this,(String)msg.obj,Toast.LENGTH_LONG).show();
Log.d("aa",(String) msg.obj);
break;
}
return false;
}
});
2.静态内部类
private MyHandler myHandler=new MyHandler(this);
private static class MyHandler extends Handler{
private WeakReference<Context> reference;
public MyHandler(Context context){
reference=new WeakReference<>(context);
}
@Override
public void handleMessage(@NonNull Message msg) {
//do something
if (reference.get()!=null){
if (msg.what==1){
Log.d("bb",(String) msg.obj);
}
}
}
}
子线程发送消息
new Thread(new Runnable() {
@Override
public void run() {
//Message message=new Message();//可以使用new Message来创建消息,但是一般不这样使用
Message message=Message.obtain();//来创建消息
message.obj="我是子线程消息";
message.what=1;
// 封装完数据发送给主线程
handler.sendMessage(message);
//第二种方式
Message message=Message.obtain();
message.obj="我是子线程静态消息";
message.what=1;
myHandler.sendMessage(message);
}
}).start();
主线程给子线程发送消息(UI线程--->子线程)
public class SixActivity extends AppCompatActivity {
// 在子线程中创建消息Handler
// 子线程创建方式
private Handler handler;
private Button btn;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_six);
new MyOneThread().start();
btn= findViewById(R.id.dian);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Message message=Message.obtain();
message.what=1;
message.obj="我是主线程的消息发送给子线程";
// 封装完数据发送给子线程
handler.sendMessage(message);
}
});
}
class MyOneThread extends Thread{
@Override
public void run() {
//在子线程中处理消息,子线程中处理消息,没有默认的Loop
//由于只有主线程成才默认的Looper.prepare(), Looper.loop();
//创建Looper
Looper.prepare();//如果不添加会报错
handler=new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case 1:
Log.d("aa",(String) msg.obj);
break;
}
}
};
//循环读取messageQueue
Looper.loop();//如果不添加读取不到消息
}
}
}
也可以使用这个方式来获取Looper
handler=new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case 1:
Log.d("aa",(String) msg.obj);
break;
}
}
};
子线程发送消息到子线程(子线程----->子线程)
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/* Message message=Message.obtain();
message.what=1;
message.obj="我是主线程的消息发送给子线程";
// 封装完数据发送给子线程
handler.sendMessage(message);*/
new Thread(new Runnable() {
@Override
public void run() {
Message message=Message.obtain();
message.obj="我是子线程发送到子线消息";
message.what=1;
handler.sendMessage(message);
}
}).start();
}
});
class MyOneThread extends Thread{
@Override
public void run() {
//在子线程中处理消息,子线程中处理消息,没有默认的Loop
//由于只有主线程成才默认的Looper.prepare(), Looper.loop();
//创建Looper
// Looper.prepare();
handler=new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case 1:
Log.d("aa",(String) msg.obj);
break;
}
}
};
//循环读取messageQueue
// Looper.loop();
}
}
使用Handler.post()直接更新ui
private Handler handler=new Handler();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_six);
btn= findViewById(R.id.dian);
new Thread(new Runnable() {
@Override
public void run() {
/* Message message=Message.obtain();
message.obj="我是子线程静态消息";
message.what=1;
handler.sendMessage(message);*/
handler.post(new Runnable() {
@Override
public void run() {
Log.d("aa","直接更新Ui");
btn.setText("我是更新的消息");
}
});
}
}).start();
}
- post和sendMessage本质上是没有区别的,只是实际用法中有一点差别
- post也没有独特的作用,post本质上还是用sendMessage实现的,post只是一中更方便的用法而已
面试解答:
1.Looper和Handler一定要处于一个线程吗?子线程中可以用MainLooper去创建Handler吗?
答:
(1)子线程中Handler handler = new Handler(Looper.getMainLooper());,此时两者就不在一个线程中
(2)可以的。
2.Handler的post方法发送的是同步消息吗?可以发送异步消息吗?
答:
用户层面发送的都是同步消息
不能发送异步消息
异步消息只能由系统发送。
3.Handler.post的逻辑在哪个线程执行的,是由Looper所在线程还是Handler所在线程决定的?
答:
由Looper所在线程决定的
最终逻辑是在Looper.loop()方法中,从MsgQueue中拿出msg,并且执行其逻辑,这是在Looper中执行的,因此有Looper所在线程决定。
4.Handler构造方法中通过Looper.myLooper();是如何获取到当前线程的Looper的?
答:
myLooper()内部使用ThreadLocal实现,因此能够获取各个线程自己的Looper
END:欲穷千里目,更上一层楼
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。