美文网首页
Android消息机制

Android消息机制

作者: DrunkPian0 | 来源:发表于2017-10-14 18:33 被阅读16次

HandlerMessageQueueLooper构成了消息机制。

0x00 典型场景

Handler最典型的场景就是子线程跟主线程交互,因为View不是线程安全的,所以要把对View的操作都放在UI线程;但是子线程耗时操作怎么传递给主线程呢,那Handler就用来承载这个线程间通信的角色。但要注意的是,只要绑定了Looper,Handler就可以在任意线程之间进行通信,这是它与AsyncTask的区别。

我们的业务类APP由于很少做复杂计算,工作中用到Handler的情形并不是很多(尤其是线程间通信的场景),大部分是逻辑跳转和UI展示;最常用的网络操作,又由于已经封装好了各种网络框架,都会把网络请求自动放到子线程,然后把结果通过主线程回调[具体可以看下网络框架源码]。

所以首先要清楚,无论是uiHandler.post(runnable);还是 uiHandler.sendMessage(msg);都应该是放在子线程中的,否则就不存在线程间通信(postDelayed不用。它是用来延迟的,参数是runnable。)[Handler用法]。

0x01 理解

先说下我对Handler的理解:
我把handler理解成一个全局变量,是可以跨进程访问的;
Runnable或者Message就相当于一个callback,从UI线程抛给子线程,子线程把复杂操作(比如网络)处理完了之后,由handler通过sendMessage(或者msg.sendToTarget)抛回给主线程进行UI操作。

为什么ANDROID SDK会提示内部类的Handler实现为了防止内存泄漏要写成static的?今天下班的路上想了很久,查了下终于想通了:

是因为主线程的所有Activity/Service用了同一个looper和messagequeue,message持有了handler 的引用,handler作为内部类又持有了activity或service的引用,所以activity或service即便ondestroy了,也要等handlemessage执行完了才能释放内存。比如Handler postDelay了,那要等到delay结束。


Android消息机制

http://m.blog.csdn.net/jdsjlzx/article/details/8463428

0x02 Handler定义

From: Developer
A Handler allows you to send and process Message
and Runnable objects associated with a thread's MessageQueue
. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

Handler允许你发送/处理线程的MessageQueue相关的MessageRunnable对象。
每个Handler实例都与一个单独的线程和那个线程的消息队列相关联。当你创造一个新handler的时候,它会被绑定到创建它的那个线程的线程/消息队列上去--从那时起,它(handler)会传递messages和runnables到那个消息队列,并且在它们从消息队列出来的时候执行它们。

Handler主要有两个主要用途:

  1. Schedule一些messages和runnables在未来某个时间执行。
  2. 入队一个action,在另外一个不同的thread执行。

Scheduling messages通过以下这些函数实现:
post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long)
sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long)

post版本的函数允许你入队Runnable对象,这些对象会在收到的时候被消息队列调用。
sendMessage 版本的函数允许你入队a Message object containing a bundle of data that will be processed by the Handler's handleMessage(Message) method (requiring that you implement a subclass of Handler).

0x03 Handler用法

下面是sendMessage的方法。另外还有handler.post(new Runnable())的方法。

private Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
         super.handleMessage(msg);
         textView.setText("UI操作");
    }
};
@Override
protected void onCreate(Bundle savedInstanceState){
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       textView = (TextView) findViewById(R.id.mytv);
       new Thread(new Runnable() {
           @Override
           public void run() {
               //模拟耗时操作
               SystemClock.sleep(3000);
               handler.sendMessage(new Message());
           }
       }).start();
   }

Handler示意图

示意图
  1. Handler的sendMessage把消息对象加入到消息队列尾部,在handleMessage函数里可以对主线程做UI操作。

  2. Looper是一个「循环器」,不停地从消息队列头部取出消息对象。如果消息队列中没有消息对象中,Looper处于等待状态,有则取出。Looper调用Handler的handleMessage()方法对消息对象进行处理。
    关于Looper:

创建Handler对象必须先初始化一个Looper , 否则会出现如下错误:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
Activity在被创建的时候,框架会帮我们初始化一个Looper对象,因此在主线程中,我们不必去调用Looper.prepare()去初始化Looper对象。
在子线程里面显示一个Toast,Toast的show操作,需要通过windowmanager的 handler来处理,因此需要手动初始化Looper对象:
Looper.prepare();
Toast.makeText(getApplicationContext(), "备份完成", 1).show();
Looper.loop();

  1. MessageQueue:消息队列,但是它的内部实现并不是用的队列,实际上是通过一个单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);

发送消息

发送消息有几种方式,但是归根结底都是调用了sendMessageAtTime()方法。
子线程中通过Handler的post()方式或send()方式发送消息,最终都是调用了sendMessageAtTime()方法。
包括在子线程中调用Activity中的runOnUiThread()中更新UI,其实也是发送消息通知主线程更新UI,最终也会调用sendMessageAtTime()方法。
sendMessageAtTime()`方法的作用很简单,就是调用MessageQueue的enqueueMessage()方法,往消息队列中添加一个消息。

还记得吗,post用于Runnable,sendMessage用于Message。

handler.sendMessage(msg)和msg.sendToTarget()的区别

如果是obtain的message,message已经跟handler绑定了,就不需要使用handler的实例来sendMessage了。


sendToTarget

虽然Message的构造方法是公共的(可以通过New操作创建一个Message对象),但获取实例最好的方式还是Message.obtain()或者 Handler.obtainMessage() ,这两种方法提供的对象是消息池中那些已经创建但不再使用的对象。节约了内存资源。

获取消息

Looper中通过loop()方法,不断从MessageQueue中调用queue.next()获取消息。如果消息队列中没有消息对象中,Looper处于等待状态,有则取出。

image.png

通过覆写Handler类的handleMessage可以handler发出去的消息。


handleMessage

Ref:
https://lrh1993.gitbooks.io/android_interview_guide/content/android/basis/message-mechanism.html
https://www.youtube.com/watch?v=LJ_pUlWzGsc&t=454s
http://www.cnblogs.com/larrylawrence/p/3555709.html
https://zhuanlan.zhihu.com/p/29929031

相关文章

网友评论

      本文标题:Android消息机制

      本文链接:https://www.haomeiwen.com/subject/ufibyxtx.html