深入理解Andorid重难点课件

上传人:痛*** 文档编号:241563311 上传时间:2024-07-04 格式:PPT 页数:50 大小:1.50MB
返回 下载 相关 举报
深入理解Andorid重难点课件_第1页
第1页 / 共50页
深入理解Andorid重难点课件_第2页
第2页 / 共50页
深入理解Andorid重难点课件_第3页
第3页 / 共50页
点击查看更多>>
资源描述
文档文档深入理解深入理解Andorid重难点重难点1、合法而稳定的权力在使用得当时很少遇到抵抗。塞约翰逊2、权力会使人渐渐失去温厚善良的美德。伯克3、最大限度地行使权力总是令人反感;权力不易确定之处始终存在着危险。塞约翰逊4、权力会奴化一切。塔西佗5、虽然权力是一头固执的熊,可是金子可以拉着它的鼻子走。莎士比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函数的函数指函数的函数指针吧!针吧!)动态方法 亲,您们从前面静态方法的介绍中看到了什么?亲,您们从前面静态方法的介绍中看到了什么?nativenative函数和函数和JNIJNI层的函数,不就是找一函数指针嘛?层的函数,不就是找一函数指针嘛?“不找贵的,只找对的不找贵的,只找对的.”.”关键数据结构:JNINativeMethod如何注册?Quick Question:1 什么时候,在哪儿注册JNINativeMethod数组?AnswerAnswer:在一个特殊的native函数中.QuesitonQuesiton:这个特殊的native函数又是在什么时候,在哪儿注册的?Answer:Answer:鸡生蛋?蛋生鸡?.当当JavaJava层通过层通过System.loadLibrarySystem.loadLibrary加载完加载完JNIJNI动动态库后,紧接着会查找该库中一个叫态库后,紧接着会查找该库中一个叫JNI_OnLoadJNI_OnLoad的函数,如果有,就调用它,而动态注册的工作的函数,如果有,就调用它,而动态注册的工作就是在这里完成的。就是在这里完成的。1.2 垃圾回收例子:可以在别的函数使用这个save_thiz吗?引用计数的作用呢?引用计数的作用呢?JNIJNI提供三种类型的引用,足够满足亲们的需求了!提供三种类型的引用,足够满足亲们的需求了!Local Reference:本地引用。在JNI层函数中使用的非全局引用对象都是Local Reference。它包括函数调用时传入的jobject、在JNI层函数中创建的jobject。Local Reference最大的特点就是,一旦JNI层函数返回,这些jobject就可能被垃圾回收。Global Reference:全局引用,这种对象如不主动释放,就永远不会被垃圾回收。Weak Global Reference:弱全局引用,一种特殊的Global Reference。在运行过程中可能会被垃圾回收。所以在程序中使用它之前,需要调用JNIEnv的IsSameObject判断它是不是被回收了。调用调用NewStringUTFNewStringUTF创建一个创建一个jstringjstring对象,它是对象,它是Local ReferenceLocal Reference类型。类型。有可能内存不够用有可能内存不够用强烈建议,及时回收强烈建议,及时回收Local RefLocal RefmEnv-DeleteLocalRef(pathStr);mEnv-DeleteLocalRef(pathStr);So easySo easy?Not Really Not Really!JNI最好的参考资料,一切尽在不言中.Java Native Interface SpecificationJava Native Interface Specification1.1.从网上下载从网上下载PDFPDF2.2.JDKJDK文档中也有文档中也有(可下载可下载chmchm版的,查询方便版的,查询方便)二 init重难点分析Android对init进行了大规模改进,但还是少不了要解析配置文件init.rc。所以,init的破解关键在init.rc的解析代码中,解析功能在parser.c2.1 keywords.h的用法声明一些Action函数定义KEYWORD宏,四个参数,却只用到第一个参数使用KEYWORD宏,得到一个枚举:enumK_UNKNOWN,K_class,K_on两次include keywords.hInterestingInteresting:include keywords.h:include keywords.h two times?two times?What do we get?What do we get?第一次包含:得到枚举定义和一些函数重新定义KEYWROD宏四个参数全用上了定义一个结构体数组keyword_info再次包含keywords.h实际上是以枚举定义的元素为数组索引,填充keyword_info数组(用新的KEYWORD宏)Result:明白了?奇技淫巧乎?2.2用好“DllMain函数”客户端Property读取的实现Android平台提供系统级别的属性管理和控制类比Windows平台上的“注册表”:可以存储一些类似key/value的键值对。作用:一般而言,系统或某些应用程序会把自己的一些属性存储在注册表中,即使下次系统重启或应用程序重启,它还能够根据之前在注册表中设置的属性,进行相应的初始化工作。Dive into code这个变量由bionic libc库输出,有什么用呢?Android想要做什么?-(目的)(目的)1 属性区域是由init进程创建2希望其他进程也能快速读取属性区域里的内容Android怎么做到?-(方法)(方法)1 属性区域创建于共享内存上2 客户端进程不知不觉得映射这块内存 利用了gcc的constructor属性,这个属性指明了一个_libc_prenit函数,当bionic libc库被加载时,将自动调用这个_libc_prenit,这个函数内部就将完成共享内存到本地进程的映射工作。Dive into codeconstructor属性指示加载器加载该库后,首先调用_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+对象生命周期的管理和控制机能。Lets dive into codeLets dive into code3.1 Sample One:初识影子对象/A没有任何自己的功能/sp,wp对象是在中创建的,下面将先创建sp,然后创建wp/大括号结束前,先析构wp,再析构sp Dive into Code类A从RefBase中派生。使用的是RefBase构造函数 mRefs是RefBase的成员变量,类型是weakref_impl,暂且称之为影子对象/强引用计数,初始值为0 x1000000/弱引用计数,初始值为0/该影子对象所指向的实际对象 Quick QuestionQuick Question:见到见到mStrongmStrong和和mWeak,mWeak,是否嗅到蛛丝马迹是否嗅到蛛丝马迹?发现影子对象成员中有两个引用计数?一个强引用,一个弱引用。如果知道引用计数和对象生死有些许关联的话,就容易想到影子对象的作用了。sp的构造/mRefs就是刚才RefBase构造函数中new出来的影子对象 非调试版的:这几个函数将 do nothing!/原子操作,影子对象的弱引用计数加1 continue incStrong/刚才增加了弱引用计数,再增加强引用计数/下面函数为原子加1操作,并返回旧值。所以c=0 x1000000,而mStrong变为0 x1000001/如果c不是初始值,则表明这个对象已经被强引用过一次了/下面这个是原子加操作,相当于执行refs-mStrong+(-0 x1000000),最终mStrong=1如果是第一次引用,则调用如果是第一次引用,则调用onFirstRefonFirstRef,这,这个函数很重要,派生类可以重载这个函数,个函数很重要,派生类可以重载这个函数,完成一些初始化工作。完成一些初始化工作。sp构造后的结果:sp的出生导致影子对象的强引用计数加1,弱引用计数加1 wp的构造/调用pA的createWeak,并且保存返回值到成员变量m_refs中/调用影子对象的incWeak,将导致影子对象的弱引用计数增加1wp构造后的结果:影子对象的弱引用计数将增加1,所以现在弱引用计数为2,而强引用计数仍为1 wp中有两个成员变量,一个保存实际对象,另一个保存影子对象.sp只有一个成员变量用来保存实际对象,但这个实际对象内部已包含了对应的影子对象wp的析构/调用影子对象的decWeak,由影子对象的基类实现/把基类指针转换成子类(影子对象)的类型,这种做法有些违背面向对象编程的思想/原子减1,返回旧值,c=2,而弱引用计数从2变为1 如果c为1,则弱引用计数为0,这说明没有弱引用指向实际对象,需要考虑是否释放内存 OBJECT_LIFETIME_XXXOBJECT_LIFETIME_XXX和生命周期有和生命周期有关系关系.比较难分析比较难分析.wp析构后,弱引用计数减1。但由于此时强引用计数和弱引用计数仍为1,所以没有对象被干掉,即没有释放实际对象和影子对象占据的内存。sp的析构/注意,此时强弱引用计数都是1,下面函数调用的结果是c=1,强引用计数为0/mFlags为0,所以会通过delete this把自己干掉/注意,此时弱引用计数仍为1delete this自杀行为没有把影子对象干掉但我们还在decStrong中/调用前影子对象的弱引用计数为1,强引用计数为0,调用结束后c=1,弱引用计数为0/这次弱引用计数终于变为0,并且mFlags为0,mStrong也为0/注意,实际数据对象已经被干掉了,所以mRefs也没有用了,但是decStrong刚进来/的时候就保存mRefs到refs了,所以这里的refs指向影子对象Sample 1 sum up:RefBase中有一个隐含的影子对象,该影子对象内部有强弱引用计数。sp化后,强弱引用计数各增加1,sp析构后,强弱引用计数各减1。wp化后,弱引用计数增加1,wp析构后,弱引用计数减1。完全彻底地消灭RefBase对象,包括让实际对象和影子对象灭亡,这些都是由强弱引用计数控制的,另外还要考虑flag的取值情况。当flag为0时,可得出如下结论:强引用为0将导致实际对象被delete。弱引用为0将导致影子对象被delete。生死魔咒-extendObjectLifetimeFOREVER的值是3,二进制表示是B11,而WEAK的二进制是B01,也就是说FOREVER包括了WEAK的情况 有什么用?1 flags为0,强引用计数控制实际对象的生命周期,弱引用计数控制 影子对象的生命周期。强引用计数为0后,实际对象被delete。所以 对于这种情况,应记住的是,使用wp时要由弱生强,以免收到 segment fault信号。2 flags为LIFETIME_WEAK,强引用计数为0,弱引用计数不为0时,实际对象不会被delete。当弱引用计数减为0时,实际对象和影 子对象会同时被delete。这是功德圆满的情况。3 flags为LIFETIME_FOREVER,对象将长生不老,彻底摆脱强弱引 用计数的控制。所以你要在适当的时候杀死这些老妖精,免得她祸害“人间”。3.2 题外话无所不用其极我的烦恼:1 RefBase,sp和wp:共两个文件,1千行左右的代码。-不多,真正参与分析的代码应该不到400行。2 判断极为复杂,打log也不方便,影响整个系统。对于这类逻辑复杂的代码,打log实为下策。冥思苦想,any good ideas?我的解决办法:1 直观想法,要是能够调试该多好!问题:部署gdbserver?太麻烦2 生猛一点:代码多且简单,不存在依赖关系,不如既然它的代码不多而且简单,那何不把它移植到台式机的开发环境下,整一个类似的RefBase呢?步骤:1 用Visual Studio,编译和调试代码。2 至于原子操作,Windows平台上有很直接的InterlockedExchangeXXX与 之对应。3 Linux平台上,不考虑多线程的话,将原子操作换成普通的 非原子操作4 如果你够猛的话,用汇编来实现常用的原子操作。Tips:如果把破解代码看成是攻城略地的话,必须学会灵活多变,而且应力求破解方法日臻极致!四 Binder重难点分析Binder.Binder.听烦了没?见恶心了没?有木有?有木有啊?要是今天听了讲座,还没搞懂,哥伤不起啊.伤不起.Binder本质:和Socket,Pipe一样,是一种IPC机制为什么觉得难?或者代码看得头疼.完全拜Android所赐,因为它把业务逻辑和通信逻辑混杂在一起了.OK,lets RTFSC.4.1 时空穿越魔术揭秘获得一个ProcessState实例 调用defaultServiceManager,得到一个IServiceManager 这么重要的函数,放在这里.有木有看走眼的时候?BIDNER_VM_SIZE定义为(1*1024*1024)-(4096*2)=1M-8Kmmap映射一块内存/打开/dev/binder设备/通过ioctl方式告诉binder驱动,这个fd支持的最大线程数是15个 ProcessState创建的结果:1 打开/dev/binder设备,这就相当于与内核的Binder驱动有了交互的通道。2 对返回的fd使用mmap,这样Binder驱动就会分配一块内存来接收数据。由于ProcessState的惟一性,因此一个进程只打开设备一次。defaultServiceManager分析/真正的gDefaultServiceManager是在这里创建的。handle值为0以0为变量,创建一个BpBinder/返回BpBinder(handle),注意,handle的值为0 BpBinder分析/handle是0/另一个重要对象是IPCThreadState,我们稍后会详细讲解。What is BpBinder?BpBinder和BBinder都是Android中与Binder通信相关的代表,它们都从IBinder类派生 I have a questionI have a question:如果说如果说BpBinderBpBinder和通信有关,是否能看到和通信有关,是否能看到类似类似send,writesend,write或者和或者和binderbinder设备交互的函数?设备交互的函数?Sorry,IBinderSorry,IBinder家族的代码中不能找到任何家族的代码中不能找到任何与与binderbinder设备相关的代码设备相关的代码障眼法interface_castif(interface_cast=dynamic_cast|if(interface_cast=dynamic_cast|interface_cast=static_cast)interface_cast=static_cast)如何把如何把BpBinder*BpBinder*类型转换成类型转换成IServiceManager*IServiceManager*类型?类型?Binder理解的重点:区分业务和通信BpBinderBpBinder和通信相关,和通信相关,通过通过interface_castinterface_cast转换成转换成IServiceManagerIServiceManager这几个是ServiceManager所提供的业务函数 梦回MFC?关键无比的宏!有DECLARE,就有IMPLEMENTSo,how to“cast”Bpbinder*to IServiceManager*?终于,业务和通信这两个对象搞到一起去了通过DECLARE和IMPLEMENT这一对媒婆做到的.注意,这里有两个对象.不是一家人,不进一家门.mRemote指向BpBinder思考一下:1 BpServiceManager与BpBinder结合,参与Binder通信2 BnServiceManager直接从BBinder派生,参与Binder通信as we said before:as we said before:BpBinder BpBinder等等IBinderIBinder家族中找不到和家族中找不到和binderbinder设备通信的代码,设备通信的代码,那么,通信层是如何完成通信工作的呢?那么,通信层是如何完成通信工作的呢?Dive into code转载请求数据的数据包remote返回BpBinder,调用它的transact函数Be very careful:1 addService做为业务层的函数,打包请求数据后.2 交给通信层函数来处理对于客户端来说,业务层和通信层的分界线在这里.请亲们务必在一个高于代码的层次来看待这个问题.BpBinder的transcat分析调用IPCThreadState函数的transactmHandle的值为0,其余几个参数由外面传入解惑:为什么IBinder家族的代码中没有发现和binder设备交互的痕迹?原来IPCThreadState类对我们又一次隐藏了通信的细节.真相揭秘如何完成真正的binder通信如此看来,IPCThreadState是关键.利用线程本地存储机制,做到线程范围内的IPCThreadState对象的Singleton/mIn和mOut是两个Parcel。它们代表接收和发送缓冲区/注意,handle的值为0,代表了通信的目的端/发请求,等回复.So easy.通过ioctl将请求数据传递给/dev/binder设备,驱动中会等待服务端的回复。客户端:处理回复服务端:处理请求IPCThreadState负责具体的通信工作,即同时为BpBinder和BBinder服务executeCommand分析服务端接收到请求时候,处理BR_TRANSCATION分支还记得:服务端同时从服务接口和BBinder派生吗?这个cookie由驱动返回,所以实际上驱动保存了服务端的对象BBinder的transact最终会调用onTransact。该函数被子类重载。所以,服务端业务和通信层的解耦不是非常明显。如果服务端退出,则驱动发送讣告通知驱动判断是不是劳力不足,主动请求增加IPC线程参与binder通信4.2 Binder和线程的关系Question:Question&Answer关于方法论:愚公的“碎石击壤”VSVS 李冰的“积薪烧之”周爱民大道至简日复一日机械的工作带给人们的恶果:让人无一例外地忘记最初的理想。韩寒1988,我想和这个世界谈谈Thanks all of you!41、学问是异常珍贵的东西,从任何源泉吸收都不可耻。阿卜日法拉兹42、只有在人群中间,才能认识自己。德国43、重复别人所说的话,只需要教育;而要挑战别人所说的话,则需要头脑。玛丽佩蒂博恩普尔44、卓越的人一大优点是:在不利与艰难的遭遇里百折不饶。贝多芬45、自己的饭量自己知道。苏联
展开阅读全文
相关资源
相关搜索

最新文档


当前位置:首页 > 管理文书 > 施工组织


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!