Android 消息通信原理浅析

线程间通信原理

  • Android是单线程模型,好处是避免代码复杂和效率低
  • 主线程负责更新UI,工作线程不能更新UI
  • 工作线程用于执行耗时任务,避免主线程阻塞
  • 主线程中通过Looper遍历消息队列,有消息则处理,没有消息则休眠

Message类

  • 存放线程间通信所需的信息,包括代码

Handler类

  • Android通过handler类实现线程键消息的收发
  • 子线程更新UI需要用handler将数据发给主线程来更新UI
  • 线程与Handler是一对多的关系,一个线程可以对应多个Handler。但一个Handler只为一个线程服务
  • Handler发送消息
    • post方法:调用sendMessageDelayed() —》调用sendMessageAtTime方法—》
    • sendMessage方法:调用sendMessageDelayed()
  • Handler处理消息
    • Handler类通过dispatchMessage方法将消息队列中取出的消息分发给相关方法处理
    • dispatchMessage() –>handleCallBack() 或者handleMessage()
    • Callback:Callback是Handler类的内部接口,在Message.callback中使用
      • Handler有一个带有Callback的构造方法,通过该方法创建Handler对象,可以避免创建Handler的子类时,覆写handleMessage。

Looper类

  • Looper用于为指定线程创建并维护一个消息队列,用先进先出的方式从消息队列中取出消息,交给Handler处理。工作线程默认没有Looper和消息循环。一个线程只能有一个Looper。Android为主线程自动创建一个Looper。
  • 创建Looper
    • Looper由Looper.prepare方法创建,不能直接new。
    • prepare(boolean quitAllowed) 中利用sThreadLocal为每个线程保存一个Looper对象(通过set方法保存在Map集合中)
    • quitAllowed 的值:工作线程在创建时,必须设置quitAllowed为true,允许Looper.quit结束Looper对消息队列的遍历。但主线程的Looper在创建时,quitAllowed必须设置为false,即不允许主线程中的Looper退出。(查看Looper源码可知)
  • 遍历消息队列
    • Looper.loop方法用于遍历消息队列
    • loop源码:无限循环,遍历MessageQUeue队列,利用handler.dispatchMessage方法将消息发送给MessageQueue
  • 常见方法
    • Looper.myLooper():得到当前线程looper对象
    • getThread():得到looper对象所属线程
    • quit():结束looper循环,主线程不能调用本方法

MessageQueue类

  • 是一个持有Messages集合的类,被Looper分发。Message会发送到与Handler所绑定的MessageQueue中,通过Looper.myQueue()方法来检索当前线程的MessageQueue
  • MessageQueue的数据结构是先进先出的队列
  • 常用方法
  • enqueueMessage方法:enqueueMessage方法用链表实现消息入队列,并用无限循环遍历队列中的消息。
  • next方法:next用无限循环遍历消息队列,取出消息。

Android以下类封装了消息循环机制

  • HandlerThread类

    • HandlerThread继承自Thread类,与普通Thread的区别在于,它在创建一个线程的同时也创建了一个绑定该线程的消息循环,可以在当前线程中分发和处理消息。

    • 使创建HandlerThread

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      // 1.创建HandlerThread对象,并为子线程指定一个名称  
      HandlerThread handlerThread = new HandlerThread("ruancoder");
      // 2.调用start()。此时内部run()方法执行,会创建一个绑定当前线程的Looper对象
      handlerThread.start();
      // 3.获取HandlerThread的Looper
      Looper looper = handlerThread.getLooper();
      // 4.使用上面的Looper创建Handler
      Handler handler = new Handler(looper);

      使用方式(1)
      handler.post(new Runnable() {
      @Override
      public void run() {
      // sub thread
      }
      });
      使用方式(2)
      Handler handler = new Handler(looper) {
      @Override
      public void handleMessage(Message msg) {
      // sub thread
      }
      };
      // 1
      handler.sendEmptyMessage(MSG);
      // 2
      handler.sendMessage(msg);

      如果想让HandlerThread退出,可以调用HandlerThread的quit()或quitSafely()。

    • HandlerThread 优点

      1
      2
      3
      1.开发中如果多次使用类似new Thread(){}.start()这种方式开启子线程,会创建多个匿名线程,使得程序运行起来越来越慢,而HandlerThread自带Looper使他可以通过消息机制来多次重复使用当前线程,节省开支。

      2.Handler类内部的Looper默认绑定的是UI线程的消息队列,对于非UI线程如果需要使用消息机制,自己去创建Looper较繁琐,由于HandlerThread内部已经自动创建了Looper,直接使用HandlerThread更方便。

  • AsyncTask类

  • AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程

  • 重要的方法

    1
    2
    3
    4
    5
    6
    7
    (1) doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。
    (2) onPostExecute(Result) 相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回

    有必要的话你还得重写以下这三个方法,但不是必须的:
    (3) onProgressUpdate(Progress…) 可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
    (4) onPreExecute() 这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
    (5) onCancelled() 用户调用取消时,要做的操作

小额支持我写出更好的文章~