SlideShare uma empresa Scribd logo
1 de 48
Baixar para ler offline
深入理解 Android 重难点解析


    主讲人——邓凡平
大纲
•   一 JNI 重难点分析
     1.1 注册方法的选择
     1.2 垃圾回收
•   二 init 重难点分析
     2.1 keywords.h 的有趣用法
     2.2 用好“ DllMain 函数”——客户端 Property 读取的实现
•   三 Android 常用类重难点分析
     3.1 RefBase 、 sp 和 wp
     3.2 题外话——无所不用其极
•   四 Binder 重难点分析
     4.1 时空穿越魔术揭秘
     4.2 Binder 和线程的关系
•   五 Audio 系统重难点分析
     5.1 AudioTrack & 方法论
     5.2 AudioFlinger 中的对象
     5.3 AudioPolicyService 实例
     5.4 audio_control_block_t 分析
     5.5 学习并实践 Desktop Check
大纲(接上)
•   六 Surface 系统重难点分析
     6.1 来之不易的 Activity
     6.2 乾坤大挪移——如何与 SurfaceFlinger 建立联系?
     6.3 生产者和消费者之间的纽带
     6.4 SurfaceFlinger 的工作流程分析
     6.5 Transaction 分析
     6.6 CameraService 中的严重 bug
     6.7 PageFlip 过程分析
一 JNI 重难点分析

1 JNI 是什么?
 Java Native Interface


2 JNI 在程序中有什么作用?
  白话:
  • Java 代码通过 JNI 调用 Native(C/C++) 写的函数
  • Native(C/C++) 的函数操作 Java 层的函数 ( 调用函数或者操作对象 )
1.1 注册方法的选择

什么是注册?
 Java 中定义的 native 函数如何找到 Native 层对应的函数?




                              如何关联这两个函数?
两种方法:
 1 静态法
 2 动态法
静态法:很简单,就是找根据一定的函数命名规则,在 so 库
中搜索对应的函数。

 native_init------Java_android_media_MediaScanner_native_1init


静态法标准步骤:
1. 先编写 Java 代码,然后编译生成 .class 文件
2. 使用 Java 的工具程序 javah ,如 javah –o output
   packagename.classname ,这样它会生成一个叫 output.h 的 JNI 层头文件。
   其中 packagename.classname 是 Java 代码编译后的 class 文件,而在生成的
   output.h 文件里,声明了对应的 JNI 层函数,只要实现里面的函数即可。
静态方法工作原理探析及其弊端
工作原理
• 当 Java 层调用 native_init 函数时,它会从对应的 JNI 库
  Java_android_media_MediaScanner_native_linit ,如果没有
  ,就会报错。如果找到,则会为这个 native_init 和
  Java_android_media_MediaScanner_native_linit 建立一个关
  联关系,其实就是保存 JNI 层函数的函数指针。以后再调用
  native_init 函数时,直接使用这个函数指针就可以了。


弊端 :
1. 需要编译所有声明了 native 函数的类。只有生成了 .class 文
   件后,才能交由 javah 工具。
2. 默认的 Native 函数名字巨长 ......
3. 第一次调用某个 native 函数的时候,需要搜索 so 库中对应
   的 Native 函数。(估计是用 dlsym 来获得 Native 函数的
   函数指针吧 !)
动态方法
亲 ,您们从前面静态方法的介绍中看到了什么 ?

native 函数和 JNI 层的函数 ,不就是找一函数指针嘛
?
“ 不找贵的 ,只找对的 ......”
关键数据结构: JNINativeMethod

                          如何注册?
Quick Question :
1 什么时候,在哪儿注册 JNINativeMethod 数组?
Answer :
在一个特殊的 native 函数中 ......
Quesiton :
这个特殊的 native 函数又是在什么时候,在哪儿注册的?
Answer:
鸡生蛋?蛋生鸡? ......


 当 Java 层通过 System.loadLibrary 加载完 JNI
 动态库后 ,紧接着会查找该库中一个叫
 JNI_OnLoad 的函数 ,如果有 ,就调用它 ,而
 动态注册的工作就是在这里完成的 。
1.2 垃圾回收

例子:




           可以在别的函数使用这个 save_thiz 吗?



 引用计数的作用呢 ?

 JNI 提供三种类型的引用 ,足够满足亲们的需求了 !
Local Reference :本地引用。在 JNI 层函数中使用的
  非全局引用对象都是 Local Reference 。它包括函数
    调
  用时传入的 jobject 、在 JNI 层函数中创建的
    jobject 。
  Local Reference 最大的特点就是,一旦 JNI 层函数返
  回,这些 jobject 就可能被垃圾回收。
 Global Reference :全局引用,这种对象如不主动释放,
 就永远不会被垃圾回收。

Weak Global Reference :弱全局引用,一种特殊的
Global Reference 。在运行过程中可能会被垃圾回收。
所以在程序中使用它之前,
需要调用 JNIEnv 的 IsSameObject 判断它是不是被回收了。
调用 NewStringUTF 创建一个 jstring 对象 ,它是 Local Reference 类型 。


             So easy ?
              Not Really !
       有可能内存不够用 ……
      强烈建议 ,及时回收 Local Ref……
      mEnv->DeleteLocalRef(pathStr);
JNI 最好的参考资料,一切尽在不言中… .
《 Java Native Interface Specification 》
1. 从网上下载 PDF
2. JDK 文档中也有 ( 可下载 chm 版的 ,查询方便 …
   …)
二 init 重难点分析

Android 对 init 进行了大规模改进……,但还是少不了要解
析配置文件 init.rc 。

所以, init 的破解关键在 init.rc 的解析
代码中,解析功能在 parser.c
2.1 keywords.h 的用法


                                声明一些 Action 函数
        定义 KEYWORD 宏 , 四个
        参数,却只用到第一个参
        数



                            使用 KEYWORD 宏 , 得到一个
                            枚举:
                            enum{
                            K_UNKNOWN,
                            K_class,
                            K_on
                            ……
                            }
两次 include keywords.h

                        第一次包含:得到枚举定义和一些函数




                 Interesting :include keywords.h two
            重新定义 KEYWROD 宏
                    times?
            四个参数全用上了
                    What do we get?

                             定义一个结构体数组
                             keyword_info


                        再次包含 keywords.h
                        实际上是以枚举定义的元素为数组索引,填
                        充 keyword_info 数组(用新的 KEYWORD
                        宏)
Result :




           明白了?奇技淫巧乎?
2.2 用好“ DllMain 函数”——客户端 Property 读取的实
  现

 Android 平台提供系统级别的属性管理和控制

类比 Windows 平台上的“注册表”:可以存储一些类似 key/value 的键值对。
作用:一般而言,系统或某些应用程序会把自己的一些属性存储在注册表中,
即使下次系统重启或应用程序重启,它还能够根据之前在注册表中设置的
属性,进行相应的初始化工作。
Dive into code




                   Android 想要做什么? --- (目的 )
                   1 属性区域是由 init 进程创建
                   2 希望其他进程也能快速读取属性区域里的内容


       Android 怎么做到? --- (方法 )
       1 属性区域创建于共享内存上
       2 客户端进程不知不觉得映射这块内存
                          这个变量由 bionic libc 库输出,有什么
                          用呢?
         利用了 gcc 的 constructor 属性,这个属性指明了一个
         __libc_prenit 函数,当 bionic libc 库被加载时,将自动调用这个
         __libc_prenit ,这个函数内部就将完成共享内存到本地进程的映射
         工作。
Dive into code




                 constructor 属性指示加载器加载该库后,首
                 先调用 __libc_prenit 函数。这一点和
                 Windows 上
                 动态库的 DllMain 函数类似
Any Questions about init?
四 Android 常用类重难点分析


代码中漫天可见的 RefBase 、 sp and wp 到底
 是
什么?
In my opinion :
1 Refbase 类似 MFC 的 CObject ,为 C++ 对象之始祖。
2 sp 非 smart pointer ,而是 strong pointer , wp 为 weak
pointer 。
3 三者协同组建 Android C++ 对象生命周期的管理和控制机能。

Let’s dive into code……
3.1 Sample One: 初识影子对象


 //A 没有任何自己的功能




 //sp , wp 对象是在 {} 中创建的,下面将先创建 sp ,然后创建 wp



                   // 大括号结束前,先析构 wp, 再析构 sp
Dive into Code

类 A 从 RefBase 中派生。使用的是 RefBase 构造函数
                 // 强引用计数,初始值为 0x1000000
                  // 弱引用计数,初始值为 0
                     // 该影子对象所指向的实际对象
                 mRefs 是 RefBase 的成员变量,类型
                 是 weakref_impl, 暂且称之为影子对象
     Quick Question :
    见到 mStrong 和 mWeak, 是否嗅到蛛丝马迹
   ?
  发现影子对象成员中有两个引用计数?一个强引用,
  一个弱引用。如果知道引用计数和对象生死有些许
  关联的话,就容易想到影子对象的作用了。
sp 的构造



 //mRefs 就是刚才 RefBase 构造函数中 new 出来的影子对象




         非调试版的:这几个函数将 do nothing!
    continue incStrong


    // 原子操作,影子对象的弱引用计数加 1
// 刚才增加了弱引用计数,再增加强引用计数


  // 下面函数为原子加 1 操作,并返回旧值。所以 c=0x1000000 ,而 mStrong 变为
  0x1000001

    sp 构造后的结果 :
              // 如果 c 不是初始值,则表明这个对象已经被强引用过一次了
    sp 的出生导致影子对象的强引用计数
/ 下面这个是原子加操作,相当于执行 refs->mStrong + ( -0x1000000 ) , 最终 mStrong=1

    加 1 ,弱引用计数加 1


         如果是第一次引用 ,则调用 onFirstRef ,
         这个函数很重要 ,派生类可以重载这个函
         数 ,完成一些初始化工作 。
wp 的构造


  wp 构造后的结果 :
  影子对象的弱引用计数将增加 1 ,所
  以现在弱引用计数为 2 ,而强引用计
  数仍为的 createWeak, 并且保存返回值到成员变量 m_refs 中
   // 调用 pA
            1
  wp 中有两个成员变量,一个保存实
  际对象,另一个保存影子对象 .
 // 调用影子对象的 incWeak ,将导致影子对象的弱引用计数增加 1

  sp 只有一个成员变量用来保存实际对
  象,但这个实际对象内部已包含了对
  应的影子对象
wp 的析构

// 把基类指针转换成子类(影子对象)的类型,这种做法有些违背面向对象编程的思想

// 原子减 1 ,返回旧值, c=2 ,而弱引用计数从 2 变为 1

如果 c 为 1 ,则弱引用计数为 0 ,这说明没有弱引用指向实际对象,
  wp 析构后,弱引用计数减 1 。但由
     // 调用影子对象的 decWeak ,由影子对象的基类实现
需要考虑是否释放内存

  于此时强引用计数和弱引用计数仍为
           OBJECT_LIFETIME_XXX 和生命
  1 ,所以没有对象被干掉,即没有释
           周期有关系 … ..

  放实际对象和影子对象占据的内存。
           比较难分析 … .
sp 的析构



            delete this 自杀行为没有把影子对象干掉
// 调用前影子对象的弱引用计数为 1 ,强引用计数为 0 ,调用结束后 c=1 ,弱引用计数为 0
            但我们还在 decStrong 中
// 注意,此时强弱引用计数都是 1 ,下面函数调用的结果是 c=1 ,强引用计数为 0

  // 这次弱引用计数终于变为 0 ,并且 mFlags 为 0 , mStrong 也为 0


   // 注意,实际数据对象已经被干掉了,所以 mRefs 也没有用了,但是 decStrong 刚
   进来
   // 的时候就保存 mRefs 到 refs 了,所以这里的 refs 指向影子对象


             //mFlags 为 0 ,所以会通过 delete this 把自己干掉
              // 注意,此时弱引用计数仍为 1
Sample 1 sum up:
RefBase 中有一个隐含的影子对象,该影子对象内部有强弱引用计数。
sp 化后,强弱引用计数各增加 1 , sp 析构后,强弱引用计数各减 1 。
wp 化后,弱引用计数增加 1 , wp 析构后,弱引用计数减 1 。

完全彻底地消灭 RefBase 对象,包括让实际对象和影子对象灭亡,
这些都是由强弱引用计数控制的,另外还要考虑 flag 的取值情况。
当 flag 为 0 时,可得出如下结论:

强引用为 0 将导致实际对象被 delete 。
弱引用为 0 将导致影子对象被 delete 。
生死魔咒 ----extendObjectLifetime

1 flags 为 0 ,强引用计数控制实际对象的生命周期,弱引用计数控制
 影子对象的生命周期。强引用计数为 0 后,实际对象被 delete 。所以
 对于这种情况,应记住的是,使用 wp 时要由弱生强,以免收到
segment fault 信号。
2 flags 为 LIFETIME_WEAK ,强引用计数为 0 ,弱引用计数不为 0 时,
        FOREVER 的值是 3 ,二进制表示是 B11 ,而
 实际对象不会被 delete 。当弱引用计数减为 0 时,实际对象和影
        WEAK 的二进制是 B01 ,也就是说 FOREVER 包
 子对象会同时被 delete 。这是功德圆满的情况。
        括了 WEAK 的情况
3 flags 为 LIFETIME_FOREVER ,对象将长生不老,彻底摆脱强弱引
 有什么用?
 用计数的控制。所以你要在适当的时候杀死这些老妖精,
 免得她祸害“人间”。
3.2 题外话——无所不用其极

   我的烦恼:
既然它的代码不多而且简单,那何不把它移植到台式机的开发环境下,
整一个类似的 RefBase 呢?步骤:
   1 RefBase,sp 和 wp :共两个文件, 1 千行左右的代码。 --
1 用 Visual Studio ,编译和调试代码。
     不多,真正参与分析的代码应该不到 400 行。
2 至于原子操作, Windows 平台上有很直接的 InterlockedExchangeXXX 与
  之对应。 极为复杂,打 log 也不方便,影响整个系统。——对
   2 判断
3 Linux 平台上,不考虑多线程的,打 log 实为下策。
     于这类逻辑复杂的代码 话,将原子操作换成普通的
  非原子操作
   冥思苦想……, any good ideas?
4 如果你够猛的话,用汇编来实现常用的原子操作。
  我的解决办法:
 Tips :
  1 直观想法,要是能够调试该多好!
 如果把破解代码看成是攻城略地的话,必须学会灵活多变,
  问题:部署 gdbserver?—— 太麻烦

 而且应力求破解方法日臻极致! 系,不如……
  2 生猛一点:代码多且简单 , 不存在依赖关
四 Binder 重难点分析
Binder....Binder...... 听烦了没?见恶心了没?

       OK,let’s
 有木有?有木有啊??             RTFSC......
  要是今天听了讲座,还没搞懂,哥伤不起啊 ...
Binder 本质:
 伤不起 .......

和 Socket , Pipe 一样,是一种 IPC 机制

  为什么觉得难?或者代码看得头疼 ...
  完全拜 Android 所赐,因为它把业务逻辑和通信逻辑混杂在一起了 ......
4.1 时空穿越魔术揭秘




ProcessState 创建的么重要的函数,放在这里 ...
               这 结果:
           // 打开 /dev/binder 设备
1 打开 /dev/binder 设备,这就相当于与内核的 Binder 驱动有了交互的通道。
               有木有看走眼的时候?

2 对返回的 fd 使用 mmap ,这样 Binder 驱动就会分配一块内存来接收数据。
由于 ProcessState 的惟一性,因此一个进程只打开设备一次。
     获得一个 ProcessState 实例
    // 通过 ioctl 方式告诉 binder 驱动,这个 fd 支持的最大线程数是 15 个

           调用 defaultServiceManager ,得
           到一个 IServiceManager
                     BIDNER_VM_SIZE 定义为 (1*1024*1024) -
                     (4096 *2) = 1M-8K
                    mmap 映射一块内存
defaultServiceManager 分析


                                            handle 值为 0




                                以 0 为变量,创建一
                                个 BpBinder
     // 真正的 gDefaultServiceManager
     是在这里创建的。




                  // 返回 BpBinder(handle) ,注意, handle 的值为 0
BpBinder 分析


                        //handle 是
                        0



     What is BpBinder ?
      BpBinder 和 BBinder 都是 Android 中
   // 另一个重要对象是 IPCThreadState ,我们稍后会详细讲解。

                            Sor
      与 Binder 通信相关的代表,它们都从 IBinder 类派
                        与

      生                         ry
                        bin

                                ,IB
                            d

  I have a question :               in
                            er 设

                                   der
   如果说 BpBinder 和通信有关 ,是否能看到
                                       家
                              备相



  类似 send,write 或者和 binder 设备交互的函数 ?
                                         族的
                                   关的

                                           代码
                                     代码

                                              中不
                                                能找
                                                   到
障眼法—— interface_cast



if (interface_cast == dynamic_cast ||
    interface_cast == static_cast)
{
 如何把 BpBinder* 类型转换成 IServiceManager* 类
型?
}
Binder 理解的重点:区分业务和通信

  BpBinder 和通信相关 ,
              梦回 MFC ?关键无比的宏
              !
  通过 interface_cast 转换成 IServiceManager
终于,业务和通信这两个对象搞到一起去了…
  So,how to “cast” Bpbinder* to
… IServiceManager*?
   有 DECLARE ,就有 IMPLEMENT……
这几个是 ServiceManager 所
通过 DECLARE 和 IMPLEMENT 这一对媒婆做
提供的业务函数
到的… .
注意,这里有两个对象… .
不是一家人,不进一家门…… .




思考一下:
1 BpServiceManager 与 BpBinder 结合,参与 Binder 通信
2 BnServiceManager 直接从 BBinder 派生,参与 Binder 通信




  mRemote 指向 BpBinder
as we said before:
BpBinder 等 IBinder 家族中找不到和 binder 设备通信的代码 ,
那么 ,通信层是如何完成通信工作的呢 ?




  Dive into code
转载请求数据的数据
Be very careful:   包

1 addService 做为业务层的函数,打包请求数据后 ......
2 交给通信层函数来处理
对于客户端来说,业务层和通信层的分界线在这里 .....
请亲们务必在一个高于代码的层次来看待这个问题 ......
 remote 返回 BpBinder ,调用它的
  transact 函数
BpBinder 的 transcat 分析




解惑:
 为什么 IBinder 家族的代码中没有发现和 binder 设备交互的痕迹?
原来 IPCThreadState 类对mHandle 的值为 0 ,其余几个参数由外面传入
                    我们又一次隐藏了通信的细节 ......
调用 IPCThreadState 函数
的 transact
真相揭秘——如何完成真正的 binder 通信

 如此看来, IPCThreadState 是关键 传递给 /dev/binder 设
                  的值为 0 将请求数据 ......
    // 注意, handle 通过 ioctl,代表了通信的目的端
                   备,驱动中会等待服务端的回复。


                       利用线程本地存储机制,做到线程范围内
                       的 IPCThreadState 对象的 Singleton
   //mIn 和 mOut 是两个 Parcel 。它们代表接收和发送缓冲区
        IPCThreadState 负责具体的通信工作,
        即同时为 BpBinder 和 BBinder 服务
              客户端:处理回复
       // 发请求,等回复 ...So easy.....
              服务端:处理请求
executeCommand 分析
             如果服务端退出,则驱动发送讣告通知


           服务端接收到请求时候,处理
           BR_TRANSCATION 分支


        驱动判断是不是劳力不足,主动请求增加 IPC 线程参与
        binder 通信
                  还记得:服务端同时从服务接口和 BBinder 派
                  生吗?这个 cookie 由驱动返回,所以实际上驱
                  动保存了服务端的对象
                       BBinder 的 transact 最终会调用
                       onTransact 。该函数被子类重载。所以,
                       服务端业务和通信层的解耦不是非常明显
                       。
4.2 Binder 和线程的关系
Question:
Question & Answer
关于方法论:
 愚公的“碎石击壤” VS 李冰的“积薪烧之”
        —— 周爱民《大道至简》


 日复一日机械的工作带给人们的恶果:让人无一例外
 地忘记最初的理想。
    —— 韩寒《 1988 ,我想和这个世界谈谈》
Thanks all of you!

Mais conteúdo relacionado

Mais procurados

Hash map导致cpu100% 的分析
Hash map导致cpu100% 的分析Hash map导致cpu100% 的分析
Hash map导致cpu100% 的分析wang hongjiang
 
Java程序员面试之葵花宝典
Java程序员面试之葵花宝典Java程序员面试之葵花宝典
Java程序员面试之葵花宝典yiditushe
 
Spring 2.0 技術手冊第六章 - Hibernate 與 Spring
Spring 2.0 技術手冊第六章 - Hibernate 與 SpringSpring 2.0 技術手冊第六章 - Hibernate 與 Spring
Spring 2.0 技術手冊第六章 - Hibernate 與 SpringJustin Lin
 
少年科技人雜誌 2015 年八月
少年科技人雜誌 2015 年八月少年科技人雜誌 2015 年八月
少年科技人雜誌 2015 年八月鍾誠 陳鍾誠
 
Spring 2.0 技術手冊第一章 - 認識 Spring
Spring 2.0 技術手冊第一章 - 認識 SpringSpring 2.0 技術手冊第一章 - 認識 Spring
Spring 2.0 技術手冊第一章 - 認識 SpringJustin Lin
 
Javascript share
Javascript shareJavascript share
Javascript shareXu Mac
 
Spring 2.0 技術手冊第四章 - Spring AOP
Spring 2.0 技術手冊第四章 - Spring AOPSpring 2.0 技術手冊第四章 - Spring AOP
Spring 2.0 技術手冊第四章 - Spring AOPJustin Lin
 
Java面试宝典
Java面试宝典Java面试宝典
Java面试宝典ma tao
 
Java面试笔试题大汇总
Java面试笔试题大汇总Java面试笔试题大汇总
Java面试笔试题大汇总yiditushe
 
Ecmascript
EcmascriptEcmascript
Ecmascriptjay li
 
为啥别读HotSpot VM的源码(2012-03-03)
为啥别读HotSpot VM的源码(2012-03-03)为啥别读HotSpot VM的源码(2012-03-03)
为啥别读HotSpot VM的源码(2012-03-03)Kris Mok
 
C++中级培训胶片
C++中级培训胶片C++中级培训胶片
C++中级培训胶片ff1
 
潜力无限的编程语言Javascript
潜力无限的编程语言Javascript潜力无限的编程语言Javascript
潜力无限的编程语言Javascriptjay li
 
Lua/LuaJIT 字节码浅析
Lua/LuaJIT 字节码浅析Lua/LuaJIT 字节码浅析
Lua/LuaJIT 字节码浅析Xiaozhe Wang
 
Java SE 7 技術手冊第六章草稿 - 何謂繼承?
Java SE 7 技術手冊第六章草稿 - 何謂繼承?Java SE 7 技術手冊第六章草稿 - 何謂繼承?
Java SE 7 技術手冊第六章草稿 - 何謂繼承?Justin Lin
 
如何設計電腦 -- 還有讓電腦變快的那些方法
如何設計電腦  -- 還有讓電腦變快的那些方法如何設計電腦  -- 還有讓電腦變快的那些方法
如何設計電腦 -- 還有讓電腦變快的那些方法鍾誠 陳鍾誠
 
千呼萬喚始出來的Java SE 7
千呼萬喚始出來的Java SE 7千呼萬喚始出來的Java SE 7
千呼萬喚始出來的Java SE 7javatwo2011
 

Mais procurados (20)

Hash map导致cpu100% 的分析
Hash map导致cpu100% 的分析Hash map导致cpu100% 的分析
Hash map导致cpu100% 的分析
 
Java程序员面试之葵花宝典
Java程序员面试之葵花宝典Java程序员面试之葵花宝典
Java程序员面试之葵花宝典
 
Spring 2.0 技術手冊第六章 - Hibernate 與 Spring
Spring 2.0 技術手冊第六章 - Hibernate 與 SpringSpring 2.0 技術手冊第六章 - Hibernate 與 Spring
Spring 2.0 技術手冊第六章 - Hibernate 與 Spring
 
少年科技人雜誌 2015 年八月
少年科技人雜誌 2015 年八月少年科技人雜誌 2015 年八月
少年科技人雜誌 2015 年八月
 
Spring 2.0 技術手冊第一章 - 認識 Spring
Spring 2.0 技術手冊第一章 - 認識 SpringSpring 2.0 技術手冊第一章 - 認識 Spring
Spring 2.0 技術手冊第一章 - 認識 Spring
 
Javascript share
Javascript shareJavascript share
Javascript share
 
Spring 2.0 技術手冊第四章 - Spring AOP
Spring 2.0 技術手冊第四章 - Spring AOPSpring 2.0 技術手冊第四章 - Spring AOP
Spring 2.0 技術手冊第四章 - Spring AOP
 
Java面试宝典
Java面试宝典Java面试宝典
Java面试宝典
 
Sun java
Sun javaSun java
Sun java
 
LLVM introduction
LLVM introductionLLVM introduction
LLVM introduction
 
Java面试笔试题大汇总
Java面试笔试题大汇总Java面试笔试题大汇总
Java面试笔试题大汇总
 
Ecmascript
EcmascriptEcmascript
Ecmascript
 
为啥别读HotSpot VM的源码(2012-03-03)
为啥别读HotSpot VM的源码(2012-03-03)为啥别读HotSpot VM的源码(2012-03-03)
为啥别读HotSpot VM的源码(2012-03-03)
 
Java 網路程式
Java 網路程式Java 網路程式
Java 網路程式
 
C++中级培训胶片
C++中级培训胶片C++中级培训胶片
C++中级培训胶片
 
潜力无限的编程语言Javascript
潜力无限的编程语言Javascript潜力无限的编程语言Javascript
潜力无限的编程语言Javascript
 
Lua/LuaJIT 字节码浅析
Lua/LuaJIT 字节码浅析Lua/LuaJIT 字节码浅析
Lua/LuaJIT 字节码浅析
 
Java SE 7 技術手冊第六章草稿 - 何謂繼承?
Java SE 7 技術手冊第六章草稿 - 何謂繼承?Java SE 7 技術手冊第六章草稿 - 何謂繼承?
Java SE 7 技術手冊第六章草稿 - 何謂繼承?
 
如何設計電腦 -- 還有讓電腦變快的那些方法
如何設計電腦  -- 還有讓電腦變快的那些方法如何設計電腦  -- 還有讓電腦變快的那些方法
如何設計電腦 -- 還有讓電腦變快的那些方法
 
千呼萬喚始出來的Java SE 7
千呼萬喚始出來的Java SE 7千呼萬喚始出來的Java SE 7
千呼萬喚始出來的Java SE 7
 

Destaque

移动互联网中的Camera
移动互联网中的Camera移动互联网中的Camera
移动互联网中的CameraBin Shao
 
音乐歌词同步
音乐歌词同步音乐歌词同步
音乐歌词同步Bin Shao
 
Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient
Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficientTh 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient
Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficientBin Shao
 
Memory management for_android_apps
Memory management for_android_appsMemory management for_android_apps
Memory management for_android_appsBin Shao
 
Drug degradation
Drug degradationDrug degradation
Drug degradationAmeenah
 
The Six Highest Performing B2B Blog Post Formats
The Six Highest Performing B2B Blog Post FormatsThe Six Highest Performing B2B Blog Post Formats
The Six Highest Performing B2B Blog Post FormatsBarry Feldman
 

Destaque (7)

移动互联网中的Camera
移动互联网中的Camera移动互联网中的Camera
移动互联网中的Camera
 
音乐歌词同步
音乐歌词同步音乐歌词同步
音乐歌词同步
 
Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient
Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficientTh 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient
Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient
 
Ts 2992
Ts 2992Ts 2992
Ts 2992
 
Memory management for_android_apps
Memory management for_android_appsMemory management for_android_apps
Memory management for_android_apps
 
Drug degradation
Drug degradationDrug degradation
Drug degradation
 
The Six Highest Performing B2B Blog Post Formats
The Six Highest Performing B2B Blog Post FormatsThe Six Highest Performing B2B Blog Post Formats
The Six Highest Performing B2B Blog Post Formats
 

Semelhante a 深入理解Andorid重难点

Ecma script edition5-小试
Ecma script edition5-小试Ecma script edition5-小试
Ecma script edition5-小试lydiafly
 
GC算法和IE中JS内存泄露
GC算法和IE中JS内存泄露GC算法和IE中JS内存泄露
GC算法和IE中JS内存泄露xwcoder
 
Javascript primer plus
Javascript primer plusJavascript primer plus
Javascript primer plusDongxu Yao
 
Java Script 引擎技术
Java Script 引擎技术Java Script 引擎技术
Java Script 引擎技术bigqiang zou
 
安卓中的设计模式举例 by hjm1fb
安卓中的设计模式举例 by hjm1fb安卓中的设计模式举例 by hjm1fb
安卓中的设计模式举例 by hjm1fbAlbert Hong
 
第1章 入门
第1章 入门第1章 入门
第1章 入门Runa Jiang
 
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509tidesq
 
ES5 introduction
ES5 introductionES5 introduction
ES5 introductionotakustay
 
Nosql及其主要产品简介
Nosql及其主要产品简介Nosql及其主要产品简介
Nosql及其主要产品简介振林 谭
 
OpenCV 2.4.2在codeblocks 10.05的環境設定
OpenCV 2.4.2在codeblocks 10.05的環境設定OpenCV 2.4.2在codeblocks 10.05的環境設定
OpenCV 2.4.2在codeblocks 10.05的環境設定菌 細
 
ios 5 arc完全指南
ios 5 arc完全指南ios 5 arc完全指南
ios 5 arc完全指南lambokini
 
Programming python - part 1
Programming python - part 1Programming python - part 1
Programming python - part 1Che-Cheng Hsu
 
软件工程 第十一章
软件工程 第十一章软件工程 第十一章
软件工程 第十一章浒 刘
 

Semelhante a 深入理解Andorid重难点 (20)

getPDF.aspx
getPDF.aspxgetPDF.aspx
getPDF.aspx
 
Exodus2 大局观
Exodus2 大局观Exodus2 大局观
Exodus2 大局观
 
Jni文档
Jni文档Jni文档
Jni文档
 
Ecma script edition5-小试
Ecma script edition5-小试Ecma script edition5-小试
Ecma script edition5-小试
 
GC算法和IE中JS内存泄露
GC算法和IE中JS内存泄露GC算法和IE中JS内存泄露
GC算法和IE中JS内存泄露
 
Javascript primer plus
Javascript primer plusJavascript primer plus
Javascript primer plus
 
Java Script 引擎技术
Java Script 引擎技术Java Script 引擎技术
Java Script 引擎技术
 
Borland C++Builder 進階課程
Borland C++Builder 進階課程Borland C++Builder 進階課程
Borland C++Builder 進階課程
 
安卓中的设计模式举例 by hjm1fb
安卓中的设计模式举例 by hjm1fb安卓中的设计模式举例 by hjm1fb
安卓中的设计模式举例 by hjm1fb
 
第1章 入门
第1章 入门第1章 入门
第1章 入门
 
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509
 
ES5 introduction
ES5 introductionES5 introduction
ES5 introduction
 
N bear
N bearN bear
N bear
 
Inside VCL
Inside VCLInside VCL
Inside VCL
 
Nosql及其主要产品简介
Nosql及其主要产品简介Nosql及其主要产品简介
Nosql及其主要产品简介
 
OpenCV 2.4.2在codeblocks 10.05的環境設定
OpenCV 2.4.2在codeblocks 10.05的環境設定OpenCV 2.4.2在codeblocks 10.05的環境設定
OpenCV 2.4.2在codeblocks 10.05的環境設定
 
ios 5 arc完全指南
ios 5 arc完全指南ios 5 arc完全指南
ios 5 arc完全指南
 
Programming python - part 1
Programming python - part 1Programming python - part 1
Programming python - part 1
 
C++基础
C++基础C++基础
C++基础
 
软件工程 第十一章
软件工程 第十一章软件工程 第十一章
软件工程 第十一章
 

Último

最近找了黑客修改成绩,真的成功了!黑客修改成绩,黑客改成绩,入侵教务系统,国外大学成绩修改 破解教务系统,改GPA分数黑客入侵教务系统黑客修改大学成绩改分...
最近找了黑客修改成绩,真的成功了!黑客修改成绩,黑客改成绩,入侵教务系统,国外大学成绩修改 破解教务系统,改GPA分数黑客入侵教务系统黑客修改大学成绩改分...最近找了黑客修改成绩,真的成功了!黑客修改成绩,黑客改成绩,入侵教务系统,国外大学成绩修改 破解教务系统,改GPA分数黑客入侵教务系统黑客修改大学成绩改分...
最近找了黑客修改成绩,真的成功了!黑客修改成绩,黑客改成绩,入侵教务系统,国外大学成绩修改 破解教务系统,改GPA分数黑客入侵教务系统黑客修改大学成绩改分...黑客 接单【TG/微信qoqoqdqd】
 
二年级音乐教育全年教学计vv二年级音乐教育全年教学计划 2024_25.docx
二年级音乐教育全年教学计vv二年级音乐教育全年教学计划 2024_25.docx二年级音乐教育全年教学计vv二年级音乐教育全年教学计划 2024_25.docx
二年级音乐教育全年教学计vv二年级音乐教育全年教学计划 2024_25.docxLUCHENSOONMoe
 
Rancangan Pengajaran Tahunan Bahasa Cina TAHUN 5
Rancangan Pengajaran Tahunan Bahasa Cina TAHUN 5Rancangan Pengajaran Tahunan Bahasa Cina TAHUN 5
Rancangan Pengajaran Tahunan Bahasa Cina TAHUN 5ssuser4cf6f01
 
6年级美术全年教学计划 Semakan KSSR Pendidikan Seni Visual Tahun 6.docx
6年级美术全年教学计划 Semakan KSSR Pendidikan Seni Visual Tahun 6.docx6年级美术全年教学计划 Semakan KSSR Pendidikan Seni Visual Tahun 6.docx
6年级美术全年教学计划 Semakan KSSR Pendidikan Seni Visual Tahun 6.docxAnonymous0fCNL9T0
 
1111111111一年级音乐教育全年教学计划 2024_25 (1).docx
1111111111一年级音乐教育全年教学计划 2024_25 (1).docx1111111111一年级音乐教育全年教学计划 2024_25 (1).docx
1111111111一年级音乐教育全年教学计划 2024_25 (1).docxLUCHENSOONMoe
 
我可是超级厉害的黑客,轻轻一戳就能入侵一个大学网站修改成绩单呢!👍【微信tytyqqww】
我可是超级厉害的黑客,轻轻一戳就能入侵一个大学网站修改成绩单呢!👍【微信tytyqqww】我可是超级厉害的黑客,轻轻一戳就能入侵一个大学网站修改成绩单呢!👍【微信tytyqqww】
我可是超级厉害的黑客,轻轻一戳就能入侵一个大学网站修改成绩单呢!👍【微信tytyqqww】黑客 接单【TG/微信qoqoqdqd】
 

Último (6)

最近找了黑客修改成绩,真的成功了!黑客修改成绩,黑客改成绩,入侵教务系统,国外大学成绩修改 破解教务系统,改GPA分数黑客入侵教务系统黑客修改大学成绩改分...
最近找了黑客修改成绩,真的成功了!黑客修改成绩,黑客改成绩,入侵教务系统,国外大学成绩修改 破解教务系统,改GPA分数黑客入侵教务系统黑客修改大学成绩改分...最近找了黑客修改成绩,真的成功了!黑客修改成绩,黑客改成绩,入侵教务系统,国外大学成绩修改 破解教务系统,改GPA分数黑客入侵教务系统黑客修改大学成绩改分...
最近找了黑客修改成绩,真的成功了!黑客修改成绩,黑客改成绩,入侵教务系统,国外大学成绩修改 破解教务系统,改GPA分数黑客入侵教务系统黑客修改大学成绩改分...
 
二年级音乐教育全年教学计vv二年级音乐教育全年教学计划 2024_25.docx
二年级音乐教育全年教学计vv二年级音乐教育全年教学计划 2024_25.docx二年级音乐教育全年教学计vv二年级音乐教育全年教学计划 2024_25.docx
二年级音乐教育全年教学计vv二年级音乐教育全年教学计划 2024_25.docx
 
Rancangan Pengajaran Tahunan Bahasa Cina TAHUN 5
Rancangan Pengajaran Tahunan Bahasa Cina TAHUN 5Rancangan Pengajaran Tahunan Bahasa Cina TAHUN 5
Rancangan Pengajaran Tahunan Bahasa Cina TAHUN 5
 
6年级美术全年教学计划 Semakan KSSR Pendidikan Seni Visual Tahun 6.docx
6年级美术全年教学计划 Semakan KSSR Pendidikan Seni Visual Tahun 6.docx6年级美术全年教学计划 Semakan KSSR Pendidikan Seni Visual Tahun 6.docx
6年级美术全年教学计划 Semakan KSSR Pendidikan Seni Visual Tahun 6.docx
 
1111111111一年级音乐教育全年教学计划 2024_25 (1).docx
1111111111一年级音乐教育全年教学计划 2024_25 (1).docx1111111111一年级音乐教育全年教学计划 2024_25 (1).docx
1111111111一年级音乐教育全年教学计划 2024_25 (1).docx
 
我可是超级厉害的黑客,轻轻一戳就能入侵一个大学网站修改成绩单呢!👍【微信tytyqqww】
我可是超级厉害的黑客,轻轻一戳就能入侵一个大学网站修改成绩单呢!👍【微信tytyqqww】我可是超级厉害的黑客,轻轻一戳就能入侵一个大学网站修改成绩单呢!👍【微信tytyqqww】
我可是超级厉害的黑客,轻轻一戳就能入侵一个大学网站修改成绩单呢!👍【微信tytyqqww】
 

深入理解Andorid重难点

  • 1. 深入理解 Android 重难点解析 主讲人——邓凡平
  • 2. 大纲 • 一 JNI 重难点分析 1.1 注册方法的选择 1.2 垃圾回收 • 二 init 重难点分析 2.1 keywords.h 的有趣用法 2.2 用好“ DllMain 函数”——客户端 Property 读取的实现 • 三 Android 常用类重难点分析 3.1 RefBase 、 sp 和 wp 3.2 题外话——无所不用其极 • 四 Binder 重难点分析 4.1 时空穿越魔术揭秘 4.2 Binder 和线程的关系 • 五 Audio 系统重难点分析 5.1 AudioTrack & 方法论 5.2 AudioFlinger 中的对象 5.3 AudioPolicyService 实例 5.4 audio_control_block_t 分析 5.5 学习并实践 Desktop Check
  • 3. 大纲(接上) • 六 Surface 系统重难点分析 6.1 来之不易的 Activity 6.2 乾坤大挪移——如何与 SurfaceFlinger 建立联系? 6.3 生产者和消费者之间的纽带 6.4 SurfaceFlinger 的工作流程分析 6.5 Transaction 分析 6.6 CameraService 中的严重 bug 6.7 PageFlip 过程分析
  • 4. 一 JNI 重难点分析 1 JNI 是什么? Java Native Interface 2 JNI 在程序中有什么作用? 白话: • Java 代码通过 JNI 调用 Native(C/C++) 写的函数 • Native(C/C++) 的函数操作 Java 层的函数 ( 调用函数或者操作对象 )
  • 5. 1.1 注册方法的选择 什么是注册? Java 中定义的 native 函数如何找到 Native 层对应的函数? 如何关联这两个函数?
  • 6. 两种方法: 1 静态法 2 动态法 静态法:很简单,就是找根据一定的函数命名规则,在 so 库 中搜索对应的函数。 native_init------Java_android_media_MediaScanner_native_1init 静态法标准步骤: 1. 先编写 Java 代码,然后编译生成 .class 文件 2. 使用 Java 的工具程序 javah ,如 javah –o output packagename.classname ,这样它会生成一个叫 output.h 的 JNI 层头文件。 其中 packagename.classname 是 Java 代码编译后的 class 文件,而在生成的 output.h 文件里,声明了对应的 JNI 层函数,只要实现里面的函数即可。
  • 7. 静态方法工作原理探析及其弊端 工作原理 • 当 Java 层调用 native_init 函数时,它会从对应的 JNI 库 Java_android_media_MediaScanner_native_linit ,如果没有 ,就会报错。如果找到,则会为这个 native_init 和 Java_android_media_MediaScanner_native_linit 建立一个关 联关系,其实就是保存 JNI 层函数的函数指针。以后再调用 native_init 函数时,直接使用这个函数指针就可以了。 弊端 : 1. 需要编译所有声明了 native 函数的类。只有生成了 .class 文 件后,才能交由 javah 工具。 2. 默认的 Native 函数名字巨长 ...... 3. 第一次调用某个 native 函数的时候,需要搜索 so 库中对应 的 Native 函数。(估计是用 dlsym 来获得 Native 函数的 函数指针吧 !)
  • 8. 动态方法 亲 ,您们从前面静态方法的介绍中看到了什么 ? native 函数和 JNI 层的函数 ,不就是找一函数指针嘛 ? “ 不找贵的 ,只找对的 ......” 关键数据结构: JNINativeMethod 如何注册?
  • 9. Quick Question : 1 什么时候,在哪儿注册 JNINativeMethod 数组? Answer : 在一个特殊的 native 函数中 ...... Quesiton : 这个特殊的 native 函数又是在什么时候,在哪儿注册的? Answer: 鸡生蛋?蛋生鸡? ...... 当 Java 层通过 System.loadLibrary 加载完 JNI 动态库后 ,紧接着会查找该库中一个叫 JNI_OnLoad 的函数 ,如果有 ,就调用它 ,而 动态注册的工作就是在这里完成的 。
  • 10. 1.2 垃圾回收 例子: 可以在别的函数使用这个 save_thiz 吗? 引用计数的作用呢 ? JNI 提供三种类型的引用 ,足够满足亲们的需求了 !
  • 11. Local Reference :本地引用。在 JNI 层函数中使用的 非全局引用对象都是 Local Reference 。它包括函数 调 用时传入的 jobject 、在 JNI 层函数中创建的 jobject 。 Local Reference 最大的特点就是,一旦 JNI 层函数返 回,这些 jobject 就可能被垃圾回收。 Global Reference :全局引用,这种对象如不主动释放, 就永远不会被垃圾回收。 Weak Global Reference :弱全局引用,一种特殊的 Global Reference 。在运行过程中可能会被垃圾回收。 所以在程序中使用它之前, 需要调用 JNIEnv 的 IsSameObject 判断它是不是被回收了。
  • 12. 调用 NewStringUTF 创建一个 jstring 对象 ,它是 Local Reference 类型 。 So easy ? Not Really ! 有可能内存不够用 …… 强烈建议 ,及时回收 Local Ref…… mEnv->DeleteLocalRef(pathStr);
  • 13. JNI 最好的参考资料,一切尽在不言中… . 《 Java Native Interface Specification 》 1. 从网上下载 PDF 2. JDK 文档中也有 ( 可下载 chm 版的 ,查询方便 … …)
  • 14. 二 init 重难点分析 Android 对 init 进行了大规模改进……,但还是少不了要解 析配置文件 init.rc 。 所以, init 的破解关键在 init.rc 的解析 代码中,解析功能在 parser.c
  • 15. 2.1 keywords.h 的用法 声明一些 Action 函数 定义 KEYWORD 宏 , 四个 参数,却只用到第一个参 数 使用 KEYWORD 宏 , 得到一个 枚举: enum{ K_UNKNOWN, K_class, K_on …… }
  • 16. 两次 include keywords.h 第一次包含:得到枚举定义和一些函数 Interesting :include keywords.h two 重新定义 KEYWROD 宏 times? 四个参数全用上了 What do we get? 定义一个结构体数组 keyword_info 再次包含 keywords.h 实际上是以枚举定义的元素为数组索引,填 充 keyword_info 数组(用新的 KEYWORD 宏)
  • 17. Result : 明白了?奇技淫巧乎?
  • 18. 2.2 用好“ DllMain 函数”——客户端 Property 读取的实 现 Android 平台提供系统级别的属性管理和控制 类比 Windows 平台上的“注册表”:可以存储一些类似 key/value 的键值对。 作用:一般而言,系统或某些应用程序会把自己的一些属性存储在注册表中, 即使下次系统重启或应用程序重启,它还能够根据之前在注册表中设置的 属性,进行相应的初始化工作。
  • 19. Dive into code Android 想要做什么? --- (目的 ) 1 属性区域是由 init 进程创建 2 希望其他进程也能快速读取属性区域里的内容 Android 怎么做到? --- (方法 ) 1 属性区域创建于共享内存上 2 客户端进程不知不觉得映射这块内存 这个变量由 bionic libc 库输出,有什么 用呢? 利用了 gcc 的 constructor 属性,这个属性指明了一个 __libc_prenit 函数,当 bionic libc 库被加载时,将自动调用这个 __libc_prenit ,这个函数内部就将完成共享内存到本地进程的映射 工作。
  • 20. Dive into code constructor 属性指示加载器加载该库后,首 先调用 __libc_prenit 函数。这一点和 Windows 上 动态库的 DllMain 函数类似
  • 22. 四 Android 常用类重难点分析 代码中漫天可见的 RefBase 、 sp and wp 到底 是 什么? In my opinion : 1 Refbase 类似 MFC 的 CObject ,为 C++ 对象之始祖。 2 sp 非 smart pointer ,而是 strong pointer , wp 为 weak pointer 。 3 三者协同组建 Android C++ 对象生命周期的管理和控制机能。 Let’s dive into code……
  • 23. 3.1 Sample One: 初识影子对象 //A 没有任何自己的功能 //sp , wp 对象是在 {} 中创建的,下面将先创建 sp ,然后创建 wp // 大括号结束前,先析构 wp, 再析构 sp
  • 24. Dive into Code 类 A 从 RefBase 中派生。使用的是 RefBase 构造函数 // 强引用计数,初始值为 0x1000000 // 弱引用计数,初始值为 0 // 该影子对象所指向的实际对象 mRefs 是 RefBase 的成员变量,类型 是 weakref_impl, 暂且称之为影子对象 Quick Question : 见到 mStrong 和 mWeak, 是否嗅到蛛丝马迹 ? 发现影子对象成员中有两个引用计数?一个强引用, 一个弱引用。如果知道引用计数和对象生死有些许 关联的话,就容易想到影子对象的作用了。
  • 25. sp 的构造 //mRefs 就是刚才 RefBase 构造函数中 new 出来的影子对象 非调试版的:这几个函数将 do nothing! continue incStrong // 原子操作,影子对象的弱引用计数加 1
  • 26. // 刚才增加了弱引用计数,再增加强引用计数 // 下面函数为原子加 1 操作,并返回旧值。所以 c=0x1000000 ,而 mStrong 变为 0x1000001 sp 构造后的结果 : // 如果 c 不是初始值,则表明这个对象已经被强引用过一次了 sp 的出生导致影子对象的强引用计数 / 下面这个是原子加操作,相当于执行 refs->mStrong + ( -0x1000000 ) , 最终 mStrong=1 加 1 ,弱引用计数加 1 如果是第一次引用 ,则调用 onFirstRef , 这个函数很重要 ,派生类可以重载这个函 数 ,完成一些初始化工作 。
  • 27. wp 的构造 wp 构造后的结果 : 影子对象的弱引用计数将增加 1 ,所 以现在弱引用计数为 2 ,而强引用计 数仍为的 createWeak, 并且保存返回值到成员变量 m_refs 中 // 调用 pA 1 wp 中有两个成员变量,一个保存实 际对象,另一个保存影子对象 . // 调用影子对象的 incWeak ,将导致影子对象的弱引用计数增加 1 sp 只有一个成员变量用来保存实际对 象,但这个实际对象内部已包含了对 应的影子对象
  • 28. wp 的析构 // 把基类指针转换成子类(影子对象)的类型,这种做法有些违背面向对象编程的思想 // 原子减 1 ,返回旧值, c=2 ,而弱引用计数从 2 变为 1 如果 c 为 1 ,则弱引用计数为 0 ,这说明没有弱引用指向实际对象, wp 析构后,弱引用计数减 1 。但由 // 调用影子对象的 decWeak ,由影子对象的基类实现 需要考虑是否释放内存 于此时强引用计数和弱引用计数仍为 OBJECT_LIFETIME_XXX 和生命 1 ,所以没有对象被干掉,即没有释 周期有关系 … .. 放实际对象和影子对象占据的内存。 比较难分析 … .
  • 29. sp 的析构 delete this 自杀行为没有把影子对象干掉 // 调用前影子对象的弱引用计数为 1 ,强引用计数为 0 ,调用结束后 c=1 ,弱引用计数为 0 但我们还在 decStrong 中 // 注意,此时强弱引用计数都是 1 ,下面函数调用的结果是 c=1 ,强引用计数为 0 // 这次弱引用计数终于变为 0 ,并且 mFlags 为 0 , mStrong 也为 0 // 注意,实际数据对象已经被干掉了,所以 mRefs 也没有用了,但是 decStrong 刚 进来 // 的时候就保存 mRefs 到 refs 了,所以这里的 refs 指向影子对象 //mFlags 为 0 ,所以会通过 delete this 把自己干掉 // 注意,此时弱引用计数仍为 1
  • 30. Sample 1 sum up: RefBase 中有一个隐含的影子对象,该影子对象内部有强弱引用计数。 sp 化后,强弱引用计数各增加 1 , sp 析构后,强弱引用计数各减 1 。 wp 化后,弱引用计数增加 1 , wp 析构后,弱引用计数减 1 。 完全彻底地消灭 RefBase 对象,包括让实际对象和影子对象灭亡, 这些都是由强弱引用计数控制的,另外还要考虑 flag 的取值情况。 当 flag 为 0 时,可得出如下结论: 强引用为 0 将导致实际对象被 delete 。 弱引用为 0 将导致影子对象被 delete 。
  • 31. 生死魔咒 ----extendObjectLifetime 1 flags 为 0 ,强引用计数控制实际对象的生命周期,弱引用计数控制 影子对象的生命周期。强引用计数为 0 后,实际对象被 delete 。所以 对于这种情况,应记住的是,使用 wp 时要由弱生强,以免收到 segment fault 信号。 2 flags 为 LIFETIME_WEAK ,强引用计数为 0 ,弱引用计数不为 0 时, FOREVER 的值是 3 ,二进制表示是 B11 ,而 实际对象不会被 delete 。当弱引用计数减为 0 时,实际对象和影 WEAK 的二进制是 B01 ,也就是说 FOREVER 包 子对象会同时被 delete 。这是功德圆满的情况。 括了 WEAK 的情况 3 flags 为 LIFETIME_FOREVER ,对象将长生不老,彻底摆脱强弱引 有什么用? 用计数的控制。所以你要在适当的时候杀死这些老妖精, 免得她祸害“人间”。
  • 32. 3.2 题外话——无所不用其极 我的烦恼: 既然它的代码不多而且简单,那何不把它移植到台式机的开发环境下, 整一个类似的 RefBase 呢?步骤: 1 RefBase,sp 和 wp :共两个文件, 1 千行左右的代码。 -- 1 用 Visual Studio ,编译和调试代码。 不多,真正参与分析的代码应该不到 400 行。 2 至于原子操作, Windows 平台上有很直接的 InterlockedExchangeXXX 与 之对应。 极为复杂,打 log 也不方便,影响整个系统。——对 2 判断 3 Linux 平台上,不考虑多线程的,打 log 实为下策。 于这类逻辑复杂的代码 话,将原子操作换成普通的 非原子操作 冥思苦想……, any good ideas? 4 如果你够猛的话,用汇编来实现常用的原子操作。 我的解决办法: Tips : 1 直观想法,要是能够调试该多好! 如果把破解代码看成是攻城略地的话,必须学会灵活多变, 问题:部署 gdbserver?—— 太麻烦 而且应力求破解方法日臻极致! 系,不如…… 2 生猛一点:代码多且简单 , 不存在依赖关
  • 33. 四 Binder 重难点分析 Binder....Binder...... 听烦了没?见恶心了没? OK,let’s 有木有?有木有啊?? RTFSC...... 要是今天听了讲座,还没搞懂,哥伤不起啊 ... Binder 本质: 伤不起 ....... 和 Socket , Pipe 一样,是一种 IPC 机制 为什么觉得难?或者代码看得头疼 ... 完全拜 Android 所赐,因为它把业务逻辑和通信逻辑混杂在一起了 ......
  • 34. 4.1 时空穿越魔术揭秘 ProcessState 创建的么重要的函数,放在这里 ... 这 结果: // 打开 /dev/binder 设备 1 打开 /dev/binder 设备,这就相当于与内核的 Binder 驱动有了交互的通道。 有木有看走眼的时候? 2 对返回的 fd 使用 mmap ,这样 Binder 驱动就会分配一块内存来接收数据。 由于 ProcessState 的惟一性,因此一个进程只打开设备一次。 获得一个 ProcessState 实例 // 通过 ioctl 方式告诉 binder 驱动,这个 fd 支持的最大线程数是 15 个 调用 defaultServiceManager ,得 到一个 IServiceManager BIDNER_VM_SIZE 定义为 (1*1024*1024) - (4096 *2) = 1M-8K mmap 映射一块内存
  • 35. defaultServiceManager 分析 handle 值为 0 以 0 为变量,创建一 个 BpBinder // 真正的 gDefaultServiceManager 是在这里创建的。 // 返回 BpBinder(handle) ,注意, handle 的值为 0
  • 36. BpBinder 分析 //handle 是 0 What is BpBinder ? BpBinder 和 BBinder 都是 Android 中 // 另一个重要对象是 IPCThreadState ,我们稍后会详细讲解。 Sor 与 Binder 通信相关的代表,它们都从 IBinder 类派 与 生 ry bin ,IB d I have a question : in er 设 der 如果说 BpBinder 和通信有关 ,是否能看到 家 备相 类似 send,write 或者和 binder 设备交互的函数 ? 族的 关的 代码 代码 中不 能找 到
  • 37. 障眼法—— interface_cast if (interface_cast == dynamic_cast || interface_cast == static_cast) { 如何把 BpBinder* 类型转换成 IServiceManager* 类 型? }
  • 38. Binder 理解的重点:区分业务和通信 BpBinder 和通信相关 , 梦回 MFC ?关键无比的宏 ! 通过 interface_cast 转换成 IServiceManager 终于,业务和通信这两个对象搞到一起去了… So,how to “cast” Bpbinder* to … IServiceManager*? 有 DECLARE ,就有 IMPLEMENT…… 这几个是 ServiceManager 所 通过 DECLARE 和 IMPLEMENT 这一对媒婆做 提供的业务函数 到的… . 注意,这里有两个对象… .
  • 39. 不是一家人,不进一家门…… . 思考一下: 1 BpServiceManager 与 BpBinder 结合,参与 Binder 通信 2 BnServiceManager 直接从 BBinder 派生,参与 Binder 通信 mRemote 指向 BpBinder
  • 40. as we said before: BpBinder 等 IBinder 家族中找不到和 binder 设备通信的代码 , 那么 ,通信层是如何完成通信工作的呢 ? Dive into code
  • 41. 转载请求数据的数据 Be very careful: 包 1 addService 做为业务层的函数,打包请求数据后 ...... 2 交给通信层函数来处理 对于客户端来说,业务层和通信层的分界线在这里 ..... 请亲们务必在一个高于代码的层次来看待这个问题 ...... remote 返回 BpBinder ,调用它的 transact 函数
  • 42. BpBinder 的 transcat 分析 解惑: 为什么 IBinder 家族的代码中没有发现和 binder 设备交互的痕迹? 原来 IPCThreadState 类对mHandle 的值为 0 ,其余几个参数由外面传入 我们又一次隐藏了通信的细节 ...... 调用 IPCThreadState 函数 的 transact
  • 43. 真相揭秘——如何完成真正的 binder 通信 如此看来, IPCThreadState 是关键 传递给 /dev/binder 设 的值为 0 将请求数据 ...... // 注意, handle 通过 ioctl,代表了通信的目的端 备,驱动中会等待服务端的回复。 利用线程本地存储机制,做到线程范围内 的 IPCThreadState 对象的 Singleton //mIn 和 mOut 是两个 Parcel 。它们代表接收和发送缓冲区 IPCThreadState 负责具体的通信工作, 即同时为 BpBinder 和 BBinder 服务 客户端:处理回复 // 发请求,等回复 ...So easy..... 服务端:处理请求
  • 44. executeCommand 分析 如果服务端退出,则驱动发送讣告通知 服务端接收到请求时候,处理 BR_TRANSCATION 分支 驱动判断是不是劳力不足,主动请求增加 IPC 线程参与 binder 通信 还记得:服务端同时从服务接口和 BBinder 派 生吗?这个 cookie 由驱动返回,所以实际上驱 动保存了服务端的对象 BBinder 的 transact 最终会调用 onTransact 。该函数被子类重载。所以, 服务端业务和通信层的解耦不是非常明显 。
  • 47. 关于方法论: 愚公的“碎石击壤” VS 李冰的“积薪烧之” —— 周爱民《大道至简》 日复一日机械的工作带给人们的恶果:让人无一例外 地忘记最初的理想。 —— 韩寒《 1988 ,我想和这个世界谈谈》