Android 异步消息机制 Handler机制 - 系列(1)

这是我的第一篇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的具体流程、各个成员的解析、以及对于一些涉及到的其它知识点,笔者会在接下来的文章中继续和大家一起学习。
学生所作,如有错误,敬请指正!谢谢!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值