可能看到○○,咱们天生了两个so库一个是nativecpp,另有一个是nativecpptwo(为什么要两个呢?咱们可能不断看下文) 这里也给出最症结的test.cpp代码。
可能看到,咱们界说了一个自界说task dynamicSo,它的奉行是正在afterEvaluate中界说的○,而且依赖于mergeDebugNativeLibs○▲,而stripDebugDebugSymbols就依赖于咱们天生的dynamicSoo!这一篇就够了!k8凯发天生赢家一触即发,抵达了一个插入操作。那么为什么要正在afterEvaluate中奉行呢?那是由于android插件是正在装备阶段中才天生的mergeDebugNativeLibs等职司▲▲,蓝本的gradle修建是不存正在如许一个职司的,于是咱们才必要正在装备完全体task之后,才举行的插入,咱们可能看一下gradle的人命周期。
许众完毕都采用了Tinker的完毕,既然咱们体系的classloader是如许▲▲,那么咱们正在合意的期间把这个交换掉不就可能了嘛○▲!当然bugly团队便是如许做的,不过笔者以为○,交换一个classloader明确对付一个普及利用来说,本钱照旧太大了○▲,况且兼容性危害也挺高的▲○,当然,另有许众体例,例如采用Relinker这个库自界说咱们加载的逻辑。
很简易▲,就一个native设施,打印一个log即可,咱们就可能正在java/kotin层举行设施挪用了,即:
动态加载,本来便是把咱们的so库正在打包成apk的期间剔除○▲凯发k8娱乐国际Android动态加载so!这一篇就够了!k8凯发天生赢家一触即发。,正在合意的期间通过汇集包下载的体例▲○,通过少少本事,正在运转的期间举行阔别加载的历程。这里涉及到下载器○○,另有下载后的版本管制等等确保一个so库被精确的加载等历程,正在这里,咱们不讲论这些辅助的流程,咱们看下怎样完毕一个最简易的加载流程。
遵循上文检索出来的两个so,咱们就可能正在项目中上传到自身的后端中,然后通过汇集下载到用户的手机上▲,这里咱们就演示一下即可,咱们就直接放正在data目次下面吧。
咱们的so库,实质便是一个elf文献,那么so库也适合elf文献的式子○,ELF文献由4部门构成,不同是ELF头(ELF header)、次第头外(Program header table)、节(Section)和节头外(Section header table)凯发k8娱乐国际。实质上,一个文献中不必定包括一齐实质,况且它们的名望也未必似乎所示如许就寝,惟有ELF头的名望是固定的,其余各部门的名望、巨细等新闻由ELF头中的各项值来决议。
看到这里的读者,坚信也也许了解动态加载so的办法了○▲,终末源代码可能正在SillyBoy▲○,当然也心愿诸位点赞呀!当然,有更好的完毕也接待评论○!!
那么,怎样把一个so库加载到咱们正本的apk中呢?这里是so蓝本的加载历程▲,可能看到,体系是通过classloader检索native目次是否存正在so库举行加载的▲,那咱们反射一下○○k8凯发天生赢家一触即发,把咱们自界说的path出席举行不就可能了吗▲?这里采用tinker雷同的思绪,正在咱们的classloader中出席so的检索旅途即可,例如:
咱们到这里○○,就也许处分so库的动态加载的合联题目了,那么另有人能够会问,项目中是会存正在众处System.load体例的○○,倘若加载的so还不存正在怎样办?例如还鄙人载当中○,本来很简易,这个期间咱们字节码插桩就派上用场了▲,只须咱们把System.load交换为咱们自界说的加载so逻辑,举行必定的逻辑照料就可能了○▲,嘿嘿,由于笔者之前就有写一个字节码插桩的库的先容,于是正在本次就不反复了,可能看Sipder▲○,同时也可能用其他的字节码插桩框架完毕,坚信这不是一个题目。
为了不冷饭热炒,嘿嘿▲○,固然我也可爱吃炒饭(手动狗头),这里咱们就不采用交换classloader的体例凯发k8娱乐国际Android动态加载so!这一篇就够了!k8凯发天生赢家一触即发,,而是采用跟relinker的思思,去举行加载!整体的可能看到sillyboy的完毕,本来就不依赖relinker跟tinker,由于我把症结的拷贝过来了▲,哈哈哈▲○,好啦○○,咱们看下怎样完毕吧!不外正在此这前▲,咱们必要明白少少前置常识。
这内部涉及到动态加载so的常识○,可能引荐大众一本书,叫做次第员的自我涵养-链接装载与库这里就画个初略图 。
要完毕so的动态加载○▲,那最最少是措施会本项目历程中涉及到哪些so吧!不必担忧▲,咱们gradle修建的期间○,就仍旧供给了相应的修建历程▲○,即修建的task【 mergeDebugNativeLibs】,正在这个历程中▲,会把一个project内部的全体native库举行一个收罗的历程○,紧接着task【stripDebugDebugSymbols】是一个符号外根除历程,倘若明白native开荒的朋侪很容易就领会,这便是一个削减so体积的一个历程▲,咱们不正在这里详述。于是咱们很容易思到,咱们只须正在这两个task中插入一个自界说的task,用于遍历和删除就可能完毕so的删除化了,于是就很容易写出如许的代码▲○。
对付一个普及的android利用来说▲▲,so库的占比凡是都是巨高不下的,由于咱们无可避免的正在开荒中遭遇种种各样必要用到native的需求,于是so库的动态化可能削减极大的包体积▲▲,自从2020腾讯的bugly团队宣布合于动态化so的合联作品后,仍旧过去两年了,合联作品,通过两年的检验,实质上so动态加载也短长常成熟的一项身手了,不过很可惜,很众公司都还没有这方面的涉略又或者说不领会从哪里入手举行,由于so动态本来涉及到下载,so版本管制,动态加载完毕等众方面,咱们没关系掷开这些特别的东西,从最实质的so动态加载启程吧!这里是本次的例子,我把它定名为sillyboy,接待pr另有后续点赞呀▲○!
咱们正在蓝本的检索旅途中,正在最前面,即数组为0的名望出席了咱们的检索旅途○,如许一来claaloader正在查找咱们仍旧动态化的so库的期间▲,就也许找到!
咱们修建一个native工程,然后正在内部编入如下实质○▲,下面是cmake凯发k8娱乐国际Android动态加载so!这一篇就够了!k8凯发天生赢家一触即发,。
那么咱们so中,倘若依赖于其他的so▲▲,那么这个新闻存正在哪里呢!?没错,它本来也存正在elf文献中,否则链接器怎样找嘛,它本来就存正在.dynamic段中,于是咱们只须找打dynamic段的偏移▲,就能到dynamic中▲,而被依赖的so的新闻▲▲,本来就存正在内部啦 咱们可能用readelf(ndk中就有toolchains目次后) 查看○▲,readelf -d nativecpptwo.so 这里的 -d 便是查看dynamic段的意义。
切实的项目历程中,应当要有校验操作,例如md5校验或者可能解压等等操作,这里不是中心▲,咱们就直接略过啦!
例如咱们要加载so3○▲,咱们就必要先加载so2,倘若so2存正在依赖▲,那咱们就先加载so1,这个期间so1就不存正在依赖项了,就不必要再挪用Linker去查找其他so库了。咱们最终计划便是,只须也许解析对应的elf文献,然后找偏移,找到必要的标的项(DT_NEED)就可能了○。
当d_tag的数值为DT_NEEDED的期间▲,就代外着依赖的共享对象文献,d_ptr透露所依赖的共享对象的文献名。看到这里读者们仍旧领会了倘若咱们领会了文献名,不就可能再用System.load去加载这个不就可能了嘛!不必交换classloader就也许担保被依赖的库先加载!咱们可能再总结一下这个计划的道理,如图:
日常的so库,例如不依赖其他的so的期间▲○,直接如许加载就没题目了,不过倘若存正在着依赖的so库的话○▲,就弗成了▲○!坚信大众正在看其他的博客的期间就能看到▲▲凯发k8娱乐国际Android动态加载s,是由于Namespace的题目▲。整体是咱们动态库加载的历程中,倘若必要依赖其他的动态库,那么就必要一个链接的历程对吧!这里的完毕便是Linker▲○,Linker 里检索的旅途正在创筑 ClassLoader 实例后就被体系通过 Namespace 机制绑定了,当咱们注入新的旅途之后,固然 ClassLoader 里的旅途加众了,不过 Linker 里 Namespace 仍旧绑定的旅途集中并没有同步更新○▲,于是映现了 libxxx.so 文献(目下的so)能找到▲,而依赖的so 找不到的景况。bugly作品○。