征服你的面试官系列1---消息机制

教你如何征服面试官系列—–安卓消息处理机制

既然我们要讲消息处理了,就必要知道与消息处理相关的类

  • Looper
    • Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程:所谓Looper线程就是循环工作的线程
  • Handler
    • handler扮演了往MQ上添加消息和处理消息的角色(只处理由自己发出的消息)
  • Message
    • message又叫task,封装了任务携带的信息和处理该任务的handler

按照思路一点点来 常规的步骤如下

1.创建一个Handler对象: new Handler();

2.发消息 sendMessage();

3.处理消息 handleMessage();

当面试官问我们的时候,我们肯定不能按照上面这样说啊,起码说一下原理吧?

深入解析第一步:创建Handler的时候我们做了什么?

这个时候 我们应该看一下源码了;

在Handler的构造方法中我们发现了如下有价值的代码:

mLooper = Looper.myLooper();//关联当前线程的looper 
if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
 mQueue = mLooper.mQueue;//直接在当前线程关联的looper上插入消息
 mCallback = null;  //让mCallback为空 

通过构造方法呢,我们可以发现:

a:mLooper不能为空,否则会抛异常

b:非常重要的一点!直接把关联looper的MQ作为自己的MQ,因此它的消息将发送到关联looper的MQ上

c:Looper.myLooper();这个可能是不太懂的东西,所以需要研究一下,我为大家讲一下

深入解析第二步:Looper作为一个循环者,是怎样实现整体步骤的?

好吧,继续翻源代码

我们就找到了Loop.myLooper()这个方法,那么发现:

public static final Looper myLooper() {
    //在任意线程调用Looper.myLooper()返回的都是那个线程的looper
    return (Looper)sThreadLocal.get();
}

是不是发现ThreadLocal又不知道是啥了?解释一下啊:

//其实就是一个成员变量,可以理解为每一个线程中的Looper对象就是一个ThreadLocal
private static final ThreadLocal sThreadLocal = new ThreadLocal();

那我们继续,发现有以下方法:prepare();prepareMainLooper();loop(); private Looper();
这里边翻源码边给大家说明一下。

我们在这个过程中发现了构造方法是私有的private Looper();那么我怎么创建这个Looper对象呢?

  • 构造方法如下:

    private Looper() {
    //每创建一个Looper都会创建一个消息队列以及它所属的线程
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }
    

    每一个Looper都有一个消息队列

  • prepare()方法:

    public static final void prepare() {
      //ThreadLocal只能有一个
        if (sThreadLocal.get() != null) {
          //如果试图创建的话,就会抛出异常
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //将looper创建出对象,然后定义为ThreadLocal
        sThreadLocal.set(new Looper());
    }
    

    我们在prepare()里面找到了我们想要的答案,并且发现了,一个Thread只能有一个Looper对象

  • loop()方法: 抽取有用的

     public static final void loop() {
        Looper me = myLooper(); //得到当前线程的loop
        MessageQueue queue = me.mQueue;//得到当前loop的MQ
        .......
        while (true) {
            Message msg = queue.next(); // 从消息队列中取出msg
              //将真正的处理工作交给handler
            msg.target.dispatchMessage(msg);
              //回收
            msg.recycle();
            }
        }
    

    这个loop()被调用以后,looper线程就真正的开始工作了,不断取出消息,将这些消息通过dispatchMessage(msg)方法交给handler;在这里你可能会说,没有发现handler啊,怎么交给它的?那我们就去找找Message和Handler这两个类吧!

    • message类中

      public void setTarget(Handler target) {
          this.target = target;
      }
      
    • Handler类中 还是抽取有用的

      public void dispatchMessage(Message msg) {
      ........
         //如果没有callback接口  调用handlemessage方法
          handleMessage(msg);
      }
      

      所以到这儿应该理解了,底层调用了 handleMessage()方法,让我们具体实现;

第三步:对Looper做一个总结

  • 我们已经看完了Looper的源码,发现这个循环者(消息泵),工作原理是这样的
    • prepare()方法将looper创建出对象,然后定义为sThreadLocal,以及一个MessageQueue
    • Looper对象通过MessageQueue按照时间顺序来存放消息和事件
    • queue.next(); 取出消息 → msg.target.dispatchMessage(msg);提交给Handler。
    • 画个图来理解一下

深入解析第四步:谁给消息队列发消息呢?

前面已经提到过了啊,没错!就是Handler:添加消息和处理消息的角色(只处理由自己发出的消息),默认的构造方法:

public Handler() {
.....
    //默认关联当前线程的looper
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    //直接在当前线程关联的looper上插入消息
    mQueue = mLooper.mQueue;
    //让Callback为空
    mCallback = null;
}

所以!!!一个线程可以有多个Handler,但是只能有一个Looper! 画图理解

好吧!那我们来具体的分析一下如何发消息

  • 第一类:sendMessage(Message)类

    • 有4种直接发送Message对象的方法,稍微带大家看一下
  • 第二类:post(Runnable);

    • 有3中通过Runnable对象转换成Message对象,间接发消息的方法,以post为例,走一下源码
    • post(Runnable r)→getPostMessage(r)→interface Callback→handleMessage
    • 上个项目的RollViewPager里面

    其实以上两类是一种方式:Handler发消息的方式

  • 第三类:runOnUiThread

    • 咦?发现在Handler中没有,原来他在Activity中!

      public final void runOnUiThread(Runnable action) {
          //如果不是UI线程就post出去
          if (Thread.currentThread() != mUiThread) {
              mHandler.post(action);
          } else {
              //如果是UI线程就直接run
              action.run();
          }
      }
      
  • 特殊的更新UI的方式:View.post()

    public boolean post(Runnable action) {
        Handler handler;
        if (mAttachInfo != null) {
            handler = mAttachInfo.mHandler;
        } else {
            // Assume that post will succeed later
            ViewRoot.getRunQueue().post(action);
            return true;
        }
    
        return handler.post(action);
    }
    
  • 深入解析第五步:对Handler做一个总结

    • handler创建时会关联一个looper,默认的构造方法将关联当前线程的looper;
    • 通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),
    • 整个过程是异步的。
    • 画出整个流程图

    深入解析第六步:Message的一些细节

    • 消息类(Message)被存放在MessageQueue中,一个MessageQueue中可以包含多个Message对象

    • 不要用构造方法而是用Message.obtain(),消息池,复用,提高性能

    • message只需要携带简单的int信息,请优先使用Message.arg1和 Message.arg2来传递信息,这比用Bundle更省内存

    来了解一下AsyncTask,封装好的异步处理任务的工具

    1、是一个抽象类,如果想使用它,酒席须创建一个子类去继承它,继承时指定三个泛型参数
        Params  执行AsyncTask时需要传入的参数,可用在后台任务中使用 指定为void(不需要)
        Progress    如果需要在界面上显示当前的君度 指定为Integer(整型)
        Result  如果需要对结果进行返回 指定为Boolean(布尔型返回结果)
    
    2、四个方法
    onPreExecute()  开始之前的初始化,薄如显示一个对话框
    doInBackground(Params。。。)   处理所有的耗时任务 可以执行publicProgress(Progress..方法)
    onProgressUpdate(Progress..)    更新UI操作
    
    3、不要忘了 .execute();启动这个任务
    

    author:花落惊雨辰 谢谢大家观看 end;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值