[置顶] 《Android内核剖析》笔记 第2章 Java基础&异步消息处理模型

  1. dex文件:是对标准的Java编译器编译出的class文件内部的各种函数表、变量表等进行优化后重新打包得到的一种文件格式;
  2. DexClassLoader:专门用来加载特殊的Class文件格式dex的类装载器;
  3. JNI(Java本地接口)调用机制
    1. 需要使用到JNI的场景主要包括:调用底层驱动、需要高效大量数据处理、复用某些已有代码;
    2. Java中调用C函数时,双方的名称需要有一个映射关系,一般为:C函数名=Java全路径包名、类名、方法名称的组合,中间以下划线分割;
      比如AssetManager中的init方法声明为:private native final void init();
      则该方法在C中对应的声明为:static void android_cotent_AssetManager_init(JNIEnv* env, jobject clazz);
      1. 映射关系并不是固定不变的,开发人员可以通过AndroidRuntime.registerNativeMethods()方法来定义具体映射格式;
      2. JNIEnv对象代表的是JVM所运行的环境,通过它可以访问JVM内部的各种对象;
      3. jobject代表的是调用该C函数的java对象,上面的事例中就是AssetManager对象;
      4. 若java方法中包括自己定义的参数,那这些参数将会一次放在以上两个参数后面;
      5. java和C对数据类型的定义不一样,也需要有映射关系,比如int对应jint、String[]对应jobjectArray,具体定义在jni.h文件中;
      6. 可以通过命令javah直接对java类生成C语言.h头文件,比如javah -d ~/ -jni com.test.XXX;
      7. 在java代码中实际调用本地C函数之前,需要将C语言代码编译为.so动态库,并使用System.loadLibrary()装载;
      8. Java代码中不能直接访问C中的变量,若需要访问只能通过get/set方法;
    3. C函数中调用Java方法一般用得比较少,技术实现上与上面类似;
4. 异步消息处理线程(该章节放在这里觉得非常奇怪,这部分内容已经涉及到Android很底层的东西了,和Java基础真没啥关系!)
  1. 一个通用的异步消息处理模型主要包括: 

    1. 有一个保存消息的队列MessageQueue,排队的策略为FIFO,即先进先出;
    2. 一个处于无限循环的线程,循环体中从消息队列中取消息,并回调相应的消息处理函数;
    3. 其他外部线程可以向该队列发送消息,消息队列本身要线程安全;
  2. Android系统中异步消息处理实现原理 

    1. 核心组件包括Handler、MessageQueue/Message、Looper;
    2. 理解线程局部变量ThreadLocal,其作用域为特定线程内,他在java多线程开发中被广泛用于数据共享和信息传递;
    3. 循环体Looper
      1. 若要想把一个普通线程变成异步消息处理线程,需要在Thread的run()方法中先调用Looper.prepare(),创建好一个消息队列MessageQueue,然后调用Looper.loop进入无限循环;
      2. loop无限循环中会调用MessageQueue.next()获取消息,若队列为空,则此时线程被挂起,处于wait状态;
        若有消息,则取出,并回调msg.target.dispatchMessage()完成消息分发,msg.target指的是发送该msg的handler,而消息处理的具体业务逻辑在handler.handleMessage()中实现;
      3. Message被处理完成后需要执行msg.recycle()进行回收,因为Message对象是使用数据池进行复用的,在新建消息时通过handler.obtainMessage()获取,而不是new一个;
    4. 消息队列MessageQueue
      1. 队列的底层数据结构为链表,每个Message对象中包含一个next变量;该队列的具体实现是在native C中完成的,不知道为啥不直接在java中完成?莫非一个小小的队列性能都能差别这么大?
      2. 核心方法为取出和发布消息,分别为next()、enqueueMessage();
      3. next()方法执行细节
        1. 调用native方法 nativePollOnce(int ptr, int timeoutMillis);
          第一个参数ptr实际上是一个C实现中的NativeMessageQueue数据对象,在执行时会被强制转换,该技巧在之前的JNI环节中C代码中如何使用持久对象有说明;
          若队列中没有消息,则将当前线程挂起至wait状态,当enqueueMessage()方法被调用时会被唤醒;
          若队列中有消息,则取出来赋值给当前java对象中的mMessages变量;
        2. 对取出的消息判断是否到了指定的执行时间,若到了则直接返回该Message;若未到执行时间则尝试取下一条消息;
          注意:这部分代码需要和enqueueMessage()方法中的发送消息都保持线程安全,即使用相同的同步锁,避免消息队列出现数据不一致;
        3. 若队列中取出的消息未到执行时间或没有消息,则判断当前 mPendingIdleHandlers是否有注册空闲回调函数;若有,顺序执行注册过的函数列表;
      4. enqueueMessage()方法执行细节
        1. 当前待发送消息与当前对象中的Message具体执行时间做比较,将当前待发送消息插入到正确的队列位置中,确保消息在队列中是按照执行时间顺序排列的;
          注意:这点在原书中并未说明,但却是MessageQueue的设计核心,因为直接关系到消息读取时的效率;
        2. 将待发送消息赋值给mMessages变量;
        3. 调用 nativeWake(int ptr);函数,将mMessages消息保存到C代码中的消息队列中,同时判断当前消息线程是否处于wait状态,是则唤醒;
      5. 以上涉及到的与native部分代码联动细节待深究;
    5. 业务逻辑代码继承Handler,开发者只需要和Handler打交道即可,无需知道Looper/MessageQueue;
      1. 重载 handleMessage()方法,实现消息处理的具体业务逻辑;
        如果你不喜欢继承Handler,那你可以选择实现Handler.Callback这个接口,里面的方法名完全一样;
      2. 通过 handler.obtainMessage();生成消息;
      3. 发送时执行 handler.sendXXX系列方法、或postXXX系列方法;
      4. Handler.dispatchMessage()方法代码解析:
        01 public void dispatchMessage(Message msg) {
        02         if (msg.callback != null) { //此处的callback其实就是附着在Message中的一个Runnable,消息一般为post系列方法发送
        03             handleCallback(msg); //其实等同于 message.callback.run();
        04         else {
        05             if (mCallback != null) { //此处的callback是实现Handler.Callback接口的实例对象
        06                 if (mCallback.handleMessage(msg)) { //调用Handler.Callback接口中的方法
        07                     return;
        08                 }
        09             }
        10             handleMessage(msg); //调用继承于Handler、重载过的handleMessage方法;
        11         }
        12     }

        看这段代码会不会很晕呢?我个人觉得在代码命名上面的确做得不咋地,2个callback,2个handleMessage方法让你彻底晕菜!




你可能感兴趣的:([置顶] 《Android内核剖析》笔记 第2章 Java基础&异步消息处理模型)