这是我的第一篇Handler机制文章,也是我秋招准备的第一战,共勉!
在过去的面试中多次被问到Handler机制,可见Handler机制在Android中的地位,所以我们很有必要好好的学习一下该机制,那就跟着笔者好好的整理一下Handler机制吧。
接下来我们就浅聊一下什么Handler机制,handler的用处,以及handler的简单使用。
Handler机制到底是什么?
按照笔者的理解,Handler机制就是Android用于规范线程内或者线程间发送消息、然后分发消息、处理消息的一套规则,而消息就是一种信号,它可以通知我们去完成某项任务。在handler机制下,我们将需要在该线程下完成的任务信号(消息)放到该线程的消息队列中进行排队等待处理,然后交由处理者在该线程的环境下执行具体任务。当然这里的排队是按照某种条件进行的,那么当我们需要加急处理的时候也是会有方法进行处理的。
然后打一个不太好的比喻:handler机制就好比我们寄快递,在哪寄、寄什么、寄去哪里由我们自己决定,当我们将包裹交给快递公司后,快递公司就会将你的包裹经过分拣,交给专门负责你目的地的部门,通过运输最终送到目的地,当然到达目的地后,怎么处理快递由我们自己决定。在这个过程中,我可能说我这个快递很急,不能等其它在我之前寄的快递送完再送,那么我们就可以加急,直接先处理我们的快递。
Handler机制的用处,也就是它能干什么?
通俗的讲,整个异步消息机制其实就是为了方便我们进行 通过消息驱动任务处理 模式的开发。
先放官方给的:There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
然后我简单按照我的理解翻译一下(哈哈~):
- 满足开发者延迟执行任务的需求(无法满足对于时间要求严格的任务需求)
- 在一个线程中将任务放到另一个线程中进行排队等待处理。(其实就是线程之间通信,或者说线程切换)
根据官方给出的大概用处,我们就能给出一些实际的用法:
- 子线程完成耗时操作后,在UI线程完成更新操作。
当我们在主线程时,但是我们需要去子线程获取数据,然后再将该数据更新到UI,那么我们要怎么在子线程完成数据获取时通知主线程更新UI呢?这里就能通过主线程的handler发送Runnable到主线程进行处理。
但是不知道大家有没有想过为什么,只能在UI绘制的线程去更改UI吗?按照笔者的理解:这是因为UI的绘制也是通过Hanlder进行驱动的,当我们在通过looper轮询到UI绘制的消息时,我们会开始处理UI绘制的任务,但是排在该任务后面的任务是不会执行的,除非这个任务完成,所以在某种意义上Handler起到了同步的作用。也就是说只有等上一个UI绘制的任务完成之后才会进行下一个UI绘制的任务,这样就不会冲突。但是如果我们不规定一定要在UI绘制的线程进行更新,那么多线程下可以出现多个UI绘制任务同时进行,那么UI的更新就会十分错乱。 - 延迟任务的实现
在实际开发过程中,我们可以会出现需要延迟完成的任务,那么我们可以通过handler发送延迟消息,那么在大于等于我们设置的延迟时间后,该任务才会被触发。 - 在不同线程中按照严格某种次序完成任务(同步)
当然同步我们有很多种方法,但是既然Android官方提供了这种方法,那我们为什么不用呢。
其实如果大家读过系统/三方框架的一些源码就会发现,Handler都会被频繁的用到,这也是为什么Android被称为被消息驱动的系统。但是由于笔者水平有限,这里就不多讲了免得误人子弟。
Handler机制的四大成员
Handler :消息的发送者以及处理者。一个线程可以存在多个hanlder,我们创建handler需要用到本线程的looper。
Message :消息。作为被传递消息的载体。
Looper :整个Handler的核心,它会不断的轮询消息队列,并且将获得的消息进行分发处理。与线程是一对一的关系。
MessageQueue :Handler发送的消息被存放在这里。与线程是一对一的关系。
简单使用实例
现在线程中调用looper.prepare();
looper.prepare();
再利用looper创建handler
Handler mhandler = new Handler(looper.mylooper()){
public void handlerMessage(Message msg){
//到looper轮询到消息后会到这里处理
...
}
};
调用looper.loop()开启循环
looper.loop();
使用handler发送消息
Message msg1 = Message.obtain();
msg1.what = MESSAGE_INT;
mhandler.sentMessage(msg1);
为了更好的理解使用,笔者在Activity中做了个小Dome
展示了两种使用:
- 主线程向子线程发送消息
- 子线程向主线程发送消息
public class MainActivity extends AppCompatActivity {
Handler mHandler;
Handler cThreadHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//在主线程中我们不需要进行looper.prepare()和looper.loop(),系统已经帮我们完成。
//创建主线程的Handler
mHandler = new Handler(getMainLooper()){
public void handleMessage(Message message){
TextView txt = findViewById(R.id.text);
txt.setText("简单使用Handler");
Toast.makeText(MainActivity.this,"主线程收到子线程消息可以继续工作", LENGTH_SHORT).show();
}
};
}
@Override
protected void onResume() {
super.onResume();
Button button = findViewById(R.id.button);
//在子线程中向主线程发送消息
button.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view) {
new Thread(()->{
Message msg = Message.obtain();
msg.what = 1;
mHandler.sendMessage(msg);
}).start();
}
});
Button button2 = findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view){
new Thread(()->{
// <- 子线程handler ->
Looper.prepare();
Looper clooper = Looper.myLooper();
cThreadHandler = new Handler(clooper){
@Override
public void handleMessage(@NonNull Message msg) {
Toast.makeText(MainActivity.this,"子线程收到主线程消息", LENGTH_SHORT).show();
}
};
clooper.loop();
//<- 子线程handler ->
}).start();
//由于looper的初始化需要一定的时间,所以我们在这里将线程休眠100ms,再调用子线程的handler发送消息。当然这种情况有更好的处理方案HandlerThread,后面的系列中我会介绍。
try{
sleep(100);
}catch (Exception e){
e.printStackTrace();
}
Message msg1 = Message.obtain();
msg1.what = 2;
cThreadHandler.sendMessage(msg1);
}
});
}
}
第一篇handler就到这了,如果只是想简单的了解和使用handler,这篇应该是够了,原本打算在一篇之中将所有内容写完,但是考虑到篇幅问题,所以接下来对于handler的具体流程、各个成员的解析、以及对于一些涉及到的其它知识点,笔者会在接下来的文章中继续和大家一起学习。
学生所作,如有错误,敬请指正!谢谢!