Handler一直是面试官必问的面试题,当然,你每次面试之前都温习了相关的知识点,但当你回答Handler相关问题的时候,你又回答的不是很好,这样的局面是不是经常碰到,那咱扪心自问一句,你真的懂Handler吗,对于刚入门的同学,往往都不知道Handler到底是个啥东西,但对于有经验的同学,你也不一定能真正的懂Handler,所以此文对有经验的同学也可以过来看看哟。我们接下来会对Handler做一个详细的讲解,讲解分为:
Handler是一个消息分发对象。Handler是Android给我们提供用来更新UI的一套机制,也是一套消息处理机制,我们可以发消息,也可以通过它处理消息,其最大的作用就是线程切换。
在Android开发中多线程的应用中,将工作线程中需更新UI的操作信息传递到UI主线程,从而实现 工作线程对UI的更新处理,最终实现异步消息的处理,最根本的目的就是为了解决多线程并发的问题,如果在一个Activity中有多个线程,并且没有加锁,就会出现界面错乱的问题。但是如果对这些更新UI的操作都加锁处理,又会导致性能下降。处于对性能的问题考虑,Android给我们提供这一套更新UI的机制我们只需要遵循这种机制就行了。不用再去关系多线程的问题,所有的更新UI的操作,都是在主线程的消息队列中去轮训的
讲到Handler,都离不开Looper、MessageQueue、Message这三者和Handler之间的关系,面试过程中也经常问到这三者之间的关系,那么这三这于Handler之间到底有什么关系?
Looper
也称之为:消息泵,是MessageQueue的管理者,会不断从MessageQueue中取出消息,并将消息分给对应的Handler处理。每一个线程只有一个Looper,每个线程在初始化Looper之后,然后Looper会维护好该线程的消息队列,用来存放Handler发送的Message,并处理消息队列出队的Message。它的特点是它跟它的线程是绑定的,处理消息也是在Looper所在的线程去处理,所以当我们在主线程创建Handler时,它就会跟主线程唯一的Looper绑定,从而我们使用Handler在子线程发消息时,最终也是在主线程处理,达到了异步的效果。
那么有人会问,我创建Handler的时候从来都没创建Looper呀,这是因为在主线程中,ActivityThread会自动帮我们创建,这个知识点到后面会详细讲解到。
MessageQueue
也称之为:消息队列,其内部接口就是一个链表结构,存储的是Message,每个线程最多只有一个MessageQueue,MessageQueue通常都是由Looper来管理,而主线程创建时,会创建一个默认的Looper对象,而Looper对象的创建,将自动创建一个MessageQueue
Message
Handler接收和处理的消息对象,就是MessageQueue里面存放的对象,一个MessageQueu可以包括多个Message。当我们需要发送一个Message时,我们一般不建议使用new Message()的形式来创建,更推荐使用Message.obtain()来获取Message实例,因为在Message类里面定义了一个消息池,当消息池里存在未使用的消息时,便返回,如果没有未使用的消息,则通过new的方式创建返回,所以使用Message.obtain()的方式来获取实例可以大大减少当有大量Message对象而产生的垃圾回收问题。其主要字段用途如下
Handler
主要有一下方法
从子线程发送消息到主线程
public class MainActivity extends AppCompatActivity {
TextView btn;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn=findViewById(R.id.btn);
new Thread(new Runnable() {
@Override
public void run() {
//在子线程发送一个消息,不建议new Message() 建议用Message.obtain()
Message msg=new Message();
handler.sendMessage(msg);
}
}).start();
}
//在主线程创建Handler对象,重写handleMessage方法,这个方法就是接收并处理消息的方法
@SuppressLint("HandlerLeak")
private Handler handler=new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
//msg就是子线程发送过来的消息
}
};
}
发送一个空消息
handler.sendEmptyMessage(what);
同一消息不能发送两次
Message msg=new Message();
handler.sendMessage(msg);
handler.sendMessage(msg);
发送一个带有obj属性的消息
public class MainActivity extends AppCompatActivity {
TextView btn;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn=findViewById(R.id.btn);
new Thread(new Runnable() {
@Override
public void run() {
Message msg=new Message();
msg.obj="我是消息";
handler.sendMessage(msg);
}
}).start();
}
@SuppressLint("HandlerLeak")
private Handler handler=new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
String str= (String) msg.obj;
Toast.makeText(MainActivity.this,str,1000).show();
}
};
}
发送一个带有what标识的消息
public class MainActivity extends AppCompatActivity {
TextView btn;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn=findViewById(R.id.btn);
new Thread(new Runnable() {
@Override
public void run() {
Message msg1=new Message();
msg1.obj="我是消息1";
msg1.what=1;
handler.sendMessage(msg1);
Message msg2=new Message();
msg2.obj="我是消息2";
msg2.what=2;
handler.sendMessage(msg2);
}
}).start();
}
@SuppressLint("HandlerLeak")
private Handler handler=new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
String str1= (String) msg.obj;
Toast.makeText(MainActivity.this,"第一个消息是"+str1,1000).show();
break;
case 2:
String str2= (String) msg.obj;
Toast.makeText(MainActivity.this,"第二个消息是"+str2,1000).show();
break;
}
}
};
}
Handler还可以发送post一个Runnable
public class MainActivity extends AppCompatActivity {
TextView btn;
private Handler mHandler = new Handler();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn=findViewById(R.id.btn);
new Thread(new Runnable() {
@Override
public void run() {
//在子线程post一个Runnable对象
Log.e("TAG",Thread.currentThread().getName());
mHandler.post(new Runnable() {
@Override
public void run() {
//这里运行在主线程,可能有人很好奇为什么运行在主线程,我这不是在子线程中吗?
//当你使用Handler的时候,已经把线程切换过来了,Handler的线程是默认创建的
Log.e("TAG",Thread.currentThread().getName());
btn.setText("已点击");
}
});
}
}).start();
}
}
以上可以看出,Handler发送消息有两种方式,一个是直接Message,一个是通过post一个Runnable,那么这两个有什么区别呢,我们来稍微看一下源码
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
可以看出两个方法都是通过调用sendMessageDelayed(),post方法的底层调用sendMessageDelayed的时候,却是通过getPostMessage®来将Runnable对象来转为Message,我们再来看看getPostMessage®里面是怎么实现的
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
然后,looper进行消息循环,进行消息的分发,
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
如果回调不为空,handleCallback(msg) 找个方法就会执行,
private static void handleCallback(Message message) {
message.callback.run();
}
最终Runnable也是转化为一个Message,而这个Message只被一个变量赋值,就是Runnable的回调函数,Run方法里面的操作。
那么问题又来了,Goole工程师为什么要设计这两种方式?
我的总结是这样的,如果需要进行很多数据来传输,我们可以使用sendMessage,因为Meaage可以赋值很多丰富的对象,如果我们只需要进行某一个动作,可以用post Runnable,在run方法里面实现即可
在Android中Handler引发的内存泄露是非常常见的,也是面试的时候会问到的,那么为什么会泄露呢?在Android中非静态内部内持有外部类的引用会导致泄露,这句话什么意思呢?就是说在Handler处理消息的时候,Handler必须处理完所有消息才会与外部类解除引用关系,如果此时外部Activity需要提前被销毁了,而Handler因还未完成消息处理而继续持有外部Activity的引用。由于上述引用关系,垃圾回收器(GC)便无法回收Activity,从而造成内存泄漏。
下面就是解决方案,静态内部类+弱引用
public class MainActivity extends AppCompatActivity {
private TextView mTv;
private MyHandler myHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myHandler = new MyHandler(this);
}
//这样的写法会造成内存泄漏
//mHandler是MainActivity的非静态内部类的实例,它持有外部类的引用,我们知道handler的消息是在一个loop
//中不断的轮询处理消息,那么当MainActivity退出时,消息队列中还有没处理的消息或正在处理的消息,所以会造成内存泄漏
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
//这样写是正确的写法
static class MyHandler extends Handler{
//创建一个软引用
private WeakReference reference;
public MyHandler(Context context){
reference = new WeakReference(context);
}
@Override
public void handleMessage(Message msg) {
MainActivity mainActivity = (MainActivity) reference.get();
if(mainActivity != null){
//TODO------
mainActivity.mTv.setText("11");
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
myHandler.removeCallbacksAndMessages(null);
}
}
Handler是主要用于主线程更新UI,在Android中也是一套非常重要的UI更新机制,当然如果处理不当就会造成内存泄露,里面其实还设及到了很多知识点,就比如为什么要在主线程更新UI,子线程不能更新吗?后面我们会更深入研究它的源码。