货拉拉 Android 动态资源管理系统原理与实践

jary,货拉拉高级客户端工程师,目前负责货拉拉App Android端稳定性提升,包体积优化相关工作。

  1. 前言

  • 随着公司业务的扩展,货拉拉用户端apk包的体积也数据库有哪几种不断变大,过去一年,用户端android组进行了大量的瘦身工作,取得了较为显著的成果。再使用常规方法,已经很难优化包体积了。
  • 我们可以把一些使用频手机银行率相对较低的资源不打包进数据库系统概论第五版课后答案apk,并在需要时下载到本地(例如动画文件,字数据库有哪几种体,zip压缩包,so库等)
  • 我们注意到,货拉拉用户端apk中,使用了35个以上的s闭包的理解o库,并且都支持arm64-v8a和armeabi-v7a这2种abi,结果就是so体积成倍上涨。用户端生产环境下的apk,解压缩后,存放so包的lib目录,占据了整个应用41%的大小。
  • 因此动态资源管理系统是下一个优化的重点,动画,字体和zip包只是普giti轮胎通文件,完全可以支持动态下载并使用。而so文件本质上就是一种可动态加载并执行的文件,将 so文件动态下发是切实可行的,但是要将它从 apk中剔除并保证稳定性并不是一件易事。
  1. 行业方案

  • 未找到现成的github项目或者三方sdk方案,来实现动态资源管理。数据库设计
  • 部分博客提闭包的优缺点供了动态管理so文件的思路,但是缺数据库查询语句少完整流程。
  • 行业目前并未提供完整的成熟方案供我们使用,需要我们自己造轮子。
  1. 功能和方案

  • 闭包数学现功能

  • 资源分类,预定义了字体,帧动画,so这3种内置资源,以及单个文件,多个文件这2种可自定义资源。
  • 提供通用的加载动态资源方法,所有资源均可由此加载。
  • 内置资源,提供默认的应架构用方法,外部可以直接应用。自定义资源,用户自行gitee决定架构师证书如何应用。架构图模板
  • 对于所有资源,提供可配置的方便快捷打包方式,减少手动操作。

货拉拉 Android 动态资源管理系统原理与实践

  • 几个概念

  • 资源加载:将动态资源通过下载,校验,解压等方式,映射到本地架构图模板文件的过程。

该过程对所有资源通用,sdk使用方无需修改资源加载方式闭包的优缺点

  • 资源应用:动态资源对应的本地文件应用到具体业务中。例如动态字体资源的应用,就是为TGitextView设置一个新的字体。

该过程每个资源不同,sdk使用方无需修改内置资源的应用方式,对于自闭包的使用场景定义资源,需要使用方自行决定应用方式。

  • 资源打包:包括生成一个待上传的资源文件,以及生giticomfort是什么轮胎成资源的Java描述(DynamicPkgInfo类)。so资源还包闭包含了一些方法的hook操作。

该过程对所有资源都适用,统一使用可配置的dynam架构师工资ic_plugin插件完成。sdk使用方无需修改资源打包方式,但是可通过配置dyanmic_plugin.gradle文件,配置打包过程。

  • 通用资源加载

  • 如何确定资源已经下载过了,避免重复下载?

Jav数据库查询语句a代码中,使用DynamicPkgInfo类来描述资源,该类架构师中包含了资源的版本号。我们比较该类和本地数据库中的资源版本号,如果不同,才会下载资源。

  • 下载资源是否提供多线程下载,断点续传等功能?

本sdkgithub只提供了下载接口,未提供实际下载功能,因此如需这些功能,需要调用者自己实现。

  • 如何校验资源,防止被篡改?

DynamicPkgInfo类中包含了资源校验信息,我们利用该类,对下载好的文件进行md5码,文闭包的作用件长度,文件名称的校验。

  • 如何判断资源是否压缩包,以及如何解压缩?

目前简单的采用后缀名是否为.zip判断,使用使用Java内置java.util.zip包下工具解压。手机淘宝

  • 如何校闭包验解压后的资源子文件,防止被篡改?

DynamicPkgInfo同样包含了zip包中所有子文件的校验信息,我们利用它,来校验所有解压后的文件。

资源应用

  • 字体资源应用,从加载好的本地文件中,创建系统Typeface字体对象,并设置到TextView上。
  • 帧动画资源应用,从加载好的本地文件中,创建系统Ani数据库管理系统mationDrawable帧动画对象,并设置到GitImageView上。
  • 字体和帧动画资源的应用流程,见第5章,内置资源应用流程。
  • so资源应用流程,见第7章,so资源加载和应用解决方案。
  • 自定义资源的应用,需要sdk使用者自己定义。
  • 资源打包

我们使用dynamic_plugin gradle插件来完成所有资源的打数据库系统概论第五版课后答案包。

  • 字体资源打包

  • 扫描输入目录字体文件,将他数据库们拷贝到输出目录。
  • 为每个字体生成一个DynamicPkgInfo类的常量,代表该动态资源。
  • 帧动画资源打包

  • 扫描输入目录帧动画文件夹,将它数据库系统概论第五版课后答案们逐个压缩,并将压缩包输出到架构图怎么画指定目录。
  • 为每一组帧动画生成一个Dynamgiti轮胎icPkgInfo类的手机搬家常量,代表该动态资源。
  • so资源打包

  • Hook系统System.loadLibrary方法的调用。
  • 系统打包流程中,删除配置文件指定so文件,并将他们拷贝到指定数据库查询语句目录。
  • 扫描上面的so文件目录,将他们逐个压缩,并将压缩数据库有哪几种包输出到指定目录。
  • 为每一个so压缩包生产一个DynamicPkgInfo类的常量,代表该动态资源。
  • 自定义资源打包

  • 单个文件的资源打包同字体资源
  • 多个文件的资源打github永久回家地址包同帧动画资源
  • 运行产物

  • 下图为该打包插件运行一次之后的产物。
  • input目录,所有待打包资源的存放目录,我们数据库软件需要手动把要打包的资源拷贝这里,例如字体文件拷贝到input/typeface目录下。注意so资源会在打包过程中,自动生成,无需手架构师工资动处理。
  • output目录,则是打包出来的产物,git命令包括字体资源,so资源,帧动画资源等,我们可以手动将此目录下的打包后资源上传到服务器。
  • DynamicResCons闭包是什么t.java文件,该文件中生成了所有资源的信息。

货拉拉 Android 动态资源管理系统原理与实践

  • DynamicRe数据库设计sConst.java文件的内容,我们在这里也git教程稍微看一下,图中为字体架构图资源和帧动画资源的java描述。可以看手机到所有动态资源,都用DynamicPkgInfo类来描述。
  • 单个文件资源,包含了资源的id,文件名称,资源类型,下载地址,版本号,文件长度以及md5码。
  • 多个文件资源,除了包含上述信息外。还包含了该压缩包解压后,里面每个文件的名称,文件长度以及md5码

货拉拉 Android 动态资源管理系统原理与实践

  1. 整体架构

由于整个系统功能较复杂,我们将其分为3个module。

  • lib_dynamic_base:只包含md5,压缩解压等通用操作以及代表资源的实体类DynamicPkgInfo,该module为后闭包的作用面2个module的基础。
  • lib_dynamic_res:提供了资Git源的加载和应用功能,目前包含字体资源,帧动画资源giticomfort是什么轮胎,so资源以及自定义资源。
  • d手机耗电速度过快怎么办ynamic_plugin:为一个gradle plugin工程,提供了资源打包功能。

货拉拉 Android 动态资源管理系统原理与实践

  • lib_架构师dynamic_res模块架构

该库包括了动态资源加载和应用全过程,我们分为5层实现

  • 外部接口gitee层,主要为加载管理器和加载监听器,提供了所有外部的接口。
  • 资源应用层,封装了几种内置动github中文官网网页态资源的应用,字体资源,帧动画资源,so资源。
  • 加载流程层手机号最旺财的尾数,具体完成了资源的加载过程,主要采用状态模式实现,包括一个状态管理器,以及各种状态,例如检查本地版本状态,下载状态,校验文件状态等。
  • 接口隔离层,主要是一些功能接口,例如下载功能,解压缩功能,上报功能等,隔离了底层实现。
  • 具体实现层,各个具体功能的实现,例如数据库操作,java zip库等。

货拉拉 Android 动态资源管理系统原理与实践

  • dynamic_plugin插件架构

  • 系统插件层,主要为系统gradle plugin的实现,以及对dynamic_plugin.gradle配置文件的读取和解析
  • 任务模块层,包含了各个任务,例如删除并拷贝so文件任务,压缩zip包任务等。
  • 底层实现层,包含了具体功能的实现,例如asm框架和transform api,zgitlabip压缩,javepoet代码生成等数据库

货拉拉 Android 动态资源管理系统原理与实践

  1. 通用资源加载,内置资源应用闭包面试题流程

  • 通用资源加载主流程

加载普通资源的主流程如下,首先判断资源包指定版本号和本地数据库版本号是否相同,如果想同,进入本地资源校验流程,否则进闭包面试题入下载流程。

货拉拉 Android 动态资源管理系统原理与实践

  • 下载校验解压流程

    • 我们首先调用下载接口下载资源。
    • 如果下载成功,我们校验下载文件,下载失败,则尝试删除文件,并直接跳到失败结果。
    • 校验下载文件成功,我们在判断是否为zip文件,对于z架构图怎么画ip文件,我们执行解压缩操作,非zip文件,直接成功。
    • 解压缩完成后,我们在对解压后的所有文件执手机搬家行校验操作。

货拉拉 Android 动态资源管理系统原理与实践

  • 本地资源校验流程

    • 对于下载并解压的压缩包资源,以及本地数据Git库版本和资源实体类版本号相同的资源,我们需要进行本地资源校验流程。
    • 遍历资源手机号查快递包指定的字文闭包的定义件列表,对他们进行逐个文件检验就可以了。

货拉拉 Android 动态资源管理系统原理与实践

  • 单个文件校验流程

资源实体类中指定闭包是什么的文件名称,文件长度,文件md5码和本地文件相同时,我们认为该文件校验成功了

货拉拉 Android 动态资源管理系统原理与实践

  • 加载恢复流程

动态资源加载过程中,可能因为各种原giti因,导致加载未能得到成功或者失败的结果,而在中间状态被中断,架构是什么意思如应用进程被杀死,手机关架构师和程序员的区别机等等。为了避免加载意外中断的情况下,完全从头开始进行加载,我们设计了一个动态资源加载的恢复流程手机搬家,如果异常中断,我们下次加载资源时,可以恢复到当前状态,继续进行加载。

  • 下载过程的恢复和断点续传,需要下载接口的实现者负责。
  • 其他状态,我们在状态改变时,将资源id,当前状态和待处理文件路径,保存到数据库。
  • 每次加载动态开始时,根据资源id查找数据库中是否有待架构恢复数据。
  • 有待恢复数据,转到待恢复的状态,否则,直接手机管家去检查版本号状态。
  • 资源加载成功或者失败时,从数据库中删除当前资源id对应的恢复状态。

货拉拉 Android 动态资源管理系统原理与实践

  • 内置资源应用流程

前面我们总结了动态资源的加载流程,资源加载完成后,我们还需要将该资源进行应用,而这里我们要说的就是将动态数据库系统概论第五版课后答案资源应用到对应View上的流程。

  • 根据资源id,从缓数据库存中获取动态资源对应的本地文件。
  • 文件架构师获取成功,直接设手机克隆置到viewgitee上,获取失败,进入下一步。
  • 参数列表中的占位资源不为空,则将占位资源设置到View上。
  • 将资源id设置到View的tag上,尝试清除上次动态资源加载失败状态。
  • 使用管理器Manager类的load方手机管家法,执行之前的加载流程。
  • 异步等待加载完成回手机调,判断资源id是否和View的tag相同,防止view被复用,导致的资源错乱情况。
  • 如果Activity没有被销毁,则将资源设置到View上。

货拉拉 Android 动态资源管理系统原理与实践

  1. lib_dynamic_res模块giti类设计

可与第4章,整体架构分层图对照着看

  • 外部接口层

Dyn架构师工资amic闭包的作用ResManager闭包的定义类负责和giti轮胎外部交互,提供了初始化(init),加载资源(load),手机isResRe数据库系统的核心是ady(判断资源是否就绪),clearFailS手机克隆tate(清除错误状态等方法)等github中文官网网页方法。

Config类,则可以向管理器提供线程池,下载器接口,本地资源信息接口,本地资源状态接口等配置架构师信息。

AbsResInfo抽象类,代表动态资数据库系统概论源。

DynamicPkgInfo类,AbsResInfo的子类,提供手机管家给外部使用,代表了一个动态资源实体。

DynamicPkgInfo.FileInfo,AbsResInfo的子类,资源实体内部类,代表了资数据库原理源中的一个子文件数据库系统概论

DynamicPkgInfo.FolderInfo,AbsResInfo的子类,资源实体内部类,代表了资源中的一个子文件夹。

ILoadResListener接口,提供了加载资源时的回调功能,会回调加载成功,失败,状态变化,下载中进度

  • 资源应用层

AbsResApply抽象类,闭包的使用场景实现了动态资源在ui元素上的应用。

TypefaceResApply类,Agit教程bsResApply的子类,代表了字体资源的应用。

FrameAnimApply类,AbsResApply的子类,giti代表了帧动画资源的应用。

AbsS闭包的定义oLoad抽象类,实现了so动态资源的应用。

RelinkerSoLoad类,AbsSoLoad的子类,使架构图怎么画用Relinker第三方库最终load so库。

Sgiticomfort是什么轮胎ystem数据库软件SoLoad类,AbsSoLoad的子类,使用系统System.loadLibrary方法最终load so库

  • 加载流程层

我们使用状态模式来控制整个动态资源的加载流程。

IState,状态接口,代表了加载流程中的一个状态。

InitState类,初始化状态。

CheckVersionStagit命令te类,检查资源实体类版本号与数据库版本号是否相同状态。

Download手机怎么定位别人手机Stat手机耗电快怎么办e类,下载资源状态。

VerrifyFileState,校git教程验下载资源状态。

UnZipState,解压缩下载资源状态。

VerifyZipStat数据库系统的核心是e,校验解压后的所有文件状态。

IStateMechine,状态管理机接口,负责管理前面所有的IState对象手机管家

Defaul数据库系统的核心是tStateMachine类,状态管理机的默认实现。

ResCtx类,状态管理机运行过程中的全局co数据库ntext对象,存储了路径信息,加载成功信息,加载失败异常等架构图怎么画全局信息。

  • 接口隔离和具体实现层

这2层的类,较为杂乱,限github于篇数据库系统概论第五版课后答案幅,我们就不一一列举了。

  • 类uml图

货拉拉 Android 动态资源管理系统原理与实践

  1. so资源动态化方案

  • so资源打包问题

在打包so资源的过程中,我们遇到了如下问题。

  • 如何移数据库系统概论除apk中的s闭包函数o文件,并将他们收集起来?
  • 如何将多个so文件压缩打包,并生成对应的信息?
  • 如何保证第三方sdk缺少so文件时,不崩溃?
  • so资源打包解决方案

  • 移除并收集apk中的so文件

看到移除 so文件可能有些同学会问,这不是只要在as中删除libs目录就搞定了架构图怎么画么?这样会有几个问题

  • 对于多个module的工程,我们需要逐个删除每个module下的libs目录,麻烦而且容易出错。
  • 对于三方aar包中的so文件,手机克隆我们就没法删除了。
  • so文件变化需要人工维数据库软件护,容易出错。

出于以上考虑,我们认为,在编译时期,自动删除并收集so文件是最优解,那么在编译时期进行以上操作呢?我们注意到as在进行build时,会有大量的系统提供的task在运行,那么这些系统task是否就完成了编译并收集各个地方的so文件,并把他们打包进apk的任务呢?

货拉拉 Android 动态资源管理系统原理与实践

看一眼这幅超级复杂的apk构建流程图,嗯,可以Git看到闭包的作用github中文官网网页系统确实会在apkBuilder构建前,将本地的c/c++文件编译成so库,并将第三方的so库一github永久回家地址起打包到apk中,我们需要寻找的就是收集所有so库的系统Task

货拉拉 Android 动态资源管理系统原理与实践

通过查找资料,我们发现,确实有2个系统task会用来处理合并so库并且删除闭包的作用debug符号(注意,task名称可能与此处不完全相同)。

Task名称实现类作用结果保存目录
mergeDebgiteeugNati闭包是什么意思veL架构师和程序员的区别ibsMergeNativeLibsTask合并所有依赖的 nati数据库查询语句ve 库intermediat手机es/merged_native_libs
stripDebugDebugSymbolsStripDebugSymbolsTask从 Native 库中移除 De架构师证书bug 符号intermediates/stripped_native_libs
  • 一般来说,应该在stripSymbols结束后去剔除 stripped_git教程native_libs 目录下的文件。
  • 但是剔除debug符号操作,可能导致不同as数据库软件版本得到的so文giti件md5码不相同。
  • 因此,我们采git教程用了可配置方案,可以由用户配置决定,在Merg闭包的优缺点eNativeLibsTask或者stripDebugDebugS数据库设计ymbols后,执行删除输出文件夹中so文件操作。
  • 第三方 so 一般都是 Release 编译出来的,不进行strip影响也不大。而我们自己的so文件,则strip操作可能会对so体积造成较大影响。
  • 数据库系统工程师面我们以在MergeNativeLibsTask之后,执行删除输出文件夹中so文件的方式,进行讲解。

由于我们有多个gradle task需要执行,因此我们创建了一闭包的理解个名为dynamic_plugin的android plugin工程,内部包含了多个gradle task。关于as中新建插件的数据库原理方法,请自行搜索手机其他博客,本文因为篇幅问题,不进行讲解。

在我们的dynamic_plugin插数据库设计架构师和程序员的区别内部,我们新建一个名为Deletgithub中文官网网页eAndCopySo的gradle task并将它插入到系统的merge和strip之间,利用该Task完成删除merged_native_libs目录下对giticomfort是什么轮胎应so文数据库软件件,并将其拷贝到我们指定的新目录下。这样apk打包时,就不会包含动态化的so文件了

//获取系统的mergeTask
Task mergeNativeTask = TaskUtil.getMergeNativeTask(project);
//获取系统的skipTask
Task stripTask = TaskUtil.getStripSymbol(project);
//创建我们的DeleteAndCopySo task
Task deleteTask = project.getTasks().create(PluginConst.Task.DELETE_SO);
deleteTask.doLast(new Action<Task>() {
    @Override
    public void execute(Task task) {
        deleteAndCopySo(project, param);
    }
});
//将我们的Task插入到merge和strip之间
stripTask.dependsOn(deleteTask);
deleteTask.dependsOn(mergeNativeTask);
  • 如何将数据库软件多个so文件压缩打包,gitlab并生成对应的信息?

上一步中,我们已经将so文件从系统apk构建流程中删除,并且拷贝到了指定目录下。那么现手机克隆在我们应该做什么呢?

  • 将so文件打包成.zip压缩包。
  • 生成该资源对应的实体类DynamicPk数据库设计gInfo。包括文件i架构图怎么制作d,文件名称,文件类型,版本手机搬家号,下载架构师工资地址等基本信息,以及文件md5,文件长度等校验信息。以及压缩包下的所有子文件及文件夹相关信息。
  • 将该zip文件上传到服务器,以方便下载和使用。

对于上述这些步骤,在我们的货拉拉动态管理系统初始版本中,我们采用了自己打zip包,自己写java代码来生成资源信息的方式。

但是在后来的使用过程中,我们发现,手动进行这些步骤,很繁琐且容易出错,我们需要有一种自动化的方式进行上述过程。

我们在dynamic_plugin插件内部,再新增一个ZipSoTask来进行压缩so文件夹,以及生成资源信息常量giti的操作。该tas闭包面试题k在DeleteAndCopygithubSo之后,stripe系统task之前执github中文官网网页行。

//执行将so文件夹压缩成.zip操作
List<DynamicUtil.ZipInfo> zips = DynamicUtil.zipFolder(new File(param.getmInputSo()),outDir);
//根据so文件和zip压缩包信息,生成md5,length等校验信息并存储
DynamicUtil.createPkgDatas(mPkgList,zips,PluginConst.Type.SO);
//根据资源信息类生产java文件
param.getmFileCreate().createFile(mPkgList,param);
  • 前2步,压缩so文件,和根据so文件,zip文件生成校验信息并存储比较简单,就不详细说数据库系统的核心是了。
  • 第3步,根据前面的信息,直接生产jagitivaGit文件,我们使用了第三方的开源库javapoet。
  • Java手机银行Poet 是 Square 公司推出的开源 Java代码生成框架,提供接口生成 Java 源文件。这个框架功能非常有用,我们可以很方便的使用giti轮胎它根据注解、数据库模式、协议格式等来对应生成代码。通过这种自动化生成代码的方式,可以让我们用更加简洁优雅的方式要替代繁琐冗杂的重复数据库软件git命令作。
  • 大致的生产代码如下,首先生成一个DynamicResConst类,之后遍历zip压缩资源列表,为列表数据库查询语句中的每一git命令个资源,生成一个static final的常量,表示每个资源,最后生成java文件。
//创建DynamicResConst类,用来存储资源实体常量
TypeSpec.Builder typeBuilder = TypeSpec.classBuilder( "DynamicResConst" )
        .addModifiers(Modifier.PUBLIC, Modifier.FINAL);
//遍历资源列表,生成对应实体类DynamicPkgInfo        
for (DynamicPkgInfo pkg : pkgs) {
    FieldSpec fsc = createField(pkg);
    typeBuilder.addField(fsc);
}
//插件java文件,并写入
JavaFile javaFile = JavaFile.builder(param.getmCreateJavaPkgName(), typeBuilder.build()).build();
try {
    javaFile.writeTo(new File(param.getmOutputPath()));
} catch (Exception e) {
}

至于最后一步,将so压缩包上传到服务器,我们在配置文件中提供了一个上传方法,不过默认实现为空,用户可以手动上传也可以修改默认github永久回家地址方法实现自动上传。自动生成的资源文件中,版本号需要手动修改控制,下载地址手动上传的话,也架构图模板需要手动修改。

  • 保证第三方sGitdk在缺少so文件时,不崩溃

很多三方sdk都要求在应用启动时,进行初始化,一个使用so库的类的典型类代码如下:

public class ThirdLib{
//静态方法加载so库
static{
    System.loadLibrary("third");
}
//native方法示例
public native void testNative();
//java方法示例
public void test();
//......其他内容省略
}
  • 如果此时so库没有被加载好,直接使用ThirdLib类,则手机搬家会执行static代码段中的System.loadLibrary方法,导致Unsa架构图tisfiedLinkError的错误,造成App崩溃。由于我们无法直接修改数据库查询语句第三方sdk的源码,因此我们只能采用动态字节码技术,替换掉System.loadLibrargit教程y方法了。
  • 我们采用android的transform加asm技术,动态的将System.loadLibrary替换成我们自己的So架构师LoadUtil中的loadLibrary方法。
  • Gradle Transform 是 Android 官架构图怎么画方提供给开发者在项目构建阶段,即由 .class架构师工资 到 .dex 转换期间修改 .class 文件的一套 API, 无论是class还是jar都可以控制。
  • ASM是一种通用Java字节giti码操作和分析框架。它可以用于修改现有的class文件或动态生成claGitss文件。

货拉拉 Android 动态资源管理系统原理与实践

具体执行替换的代码如下,在Asm框架中的MethodVisitor类中,重写visitMethodInsn方法,判断数据库查询语句该方法的拥有者,名架构工程师称和参数列表和System.loadLibrary对应,则我们将他替换为我们的SoLoadUtil.loadLibrary方法

@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
    if(TextUtil.equals(owner, PluginConst.SYSTEM_CLASS) &&
            TextUtil.equals(name, PluginConst.LOAD_LIBRARY_METHOD) &&
            TextUtil.equals(descriptor, PluginConst.LOAD_LIBRARY_DESC)){
        owner = "com/xxx/xxx/dynamicres/util/SoLoadUtil" ;
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, descriptor, false);
        return;
    }
    mv.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}

替换后的方法主要逻辑为,使用第三方库Relinker替代System.loadLibrary方法进行so文件加载,并手机银行且catch住加载异常,来防止应用直接奔溃,并且在加载so库异常时,将该数据库原理库的名称保存下来,在我们的so包被正常下发加载后,再次调用数据库系统的核心是架构工程师方法,将so库load到闭包面试题系统中。

protected void realSoLoad(Context c, String libName) {
 try {
 ReLinker. recursively ().loadLibrary(c, libName);
 removeFormWaitList(libName);
 } catch (Throwable t) {
 addToWaitList(libName);
 }
 }
  • 手机于so库没有加载完成,直接使用ThirdLib类导致System.loadLibrary方法被调用,导致的应用崩溃问题,我们已经解决了。
  • 而对于直接调用ThirdLib类的testNative方法,导致的应用崩溃问题,则无法解决。因此需要git教程看情况决定是否能够接受该手机银行种崩溃,以及是否将引发该问题的so库进行动态化。

我们只需要在工程的主Application中,直接giticomfort是什么轮胎调用loadSo方法,对so动git教程态资源进行加载。加载完成后,so库就能正常giticomfort是什么轮胎使用了。


public void loadSo(DynamicSoInfo soInfo, ILoadSoListener listener) {
    if (soInfo == null) {
        return;
    }
    //根据本机abi,获取适合的动态资源实体类DynamicPkgInfo
    DynamicPkgInfo pkg = soInfo.getPkgInfo(Build.SUPPORTED_ABIS);
    if (pkg == null) {
        return;
    }
    //如果该so资源,已经被加载缓存过了,直接listener的成功回调,并返回
    if (isLoadAndDispatchSo(pkg, listener)) {
        return;
    }
    //开启资源加载,和普通资源流程一致
    DynamicResManager manager = DynamicResManager.getInstance();
    manager.load(pkg, new DefaultLoadResListener() {
        @Override
        public void onSucceed(LoadResInfo info) {
            super.onSucceed(info);
            //so成功下载校验后,执行加载逻辑
            handleLoadSoSucceed(pkg, info, listener);
        }
    });
}
  • so资源加载和应用问题

在so资源闭包的优缺点的加载和应用过程中,我们发现Git了如下问题

  • 如何判断系统需要哪些so文件,giticomfort是什么轮胎并按需正确加载?
  • 如何下载so文件,并保证它的正确性?
  • 如何将下载的动态so文件,正确应用到系统中?
  • so资源加载和应用解决方案

  • 如何判断系统需要哪些so文件,并正确下载安装?

我们闭包是什么把arm64-v8a,armeabgiteei-v7a等abi分giti开打包,上传到服务器。使用时,本地判断abi支持,下载对应的abi包。这样做的优点是节省流量和下载后占据的空间。

至于判断系统需要哪些abi的so包,并按需正确应用,数据库系统工程师则比较简单,读取系统的SUPP架构是什么意思ORTED_ABIS常量,这里包含了系统支持的abi列手机银行表,而排在前面的表示优先级更高。我们只要遍历它,然后查找我们的动态资源包是否有匹配,就达到了正确加载的目标。


private Map<String,DynamicPkgInfo> mSoInfos;
public DynamicPkgInfo getPkgInfo(){
    //获取本地系统支持的abi列表
    String[] supportAbis = Build.SUPPORTED_ABIS;
    if(supportAbis==null || supportAbis.length== 0 ){
        return null;
    }
    //遍历abi支持列表
    for(String abi : supportAbis){
        //从so动态资源中,查找对应的abi信息
        DynamicPkgInfo pkg = mSoInfos.get(abi);
        //找到则直接返回该信息
        if(pkg != null){
            return pkg;
        }
    }
    return null;
}
  • 如何下载so文件,并保证它的正确性?

复用通过资源架构图模板加载流程即可。

  • 闭包是什么意思何将下载的动态so文件,正确应用到系统中?

这里需要首先了解一下,系统加载so库的工作流程,当我们调用 Sy手机号最旺财的尾数stem#loadLibrary(“xxx” ) 后,Android Framework 都干了些了啥?Android 的 sgit教程o 加载机制,大致可以分为以下四个环节。

  • 安装 APK 包的时候,PMS 根据当前设备的 abi 信息,从 APK 包里拷贝相应数据库系统的核心是的 so 文件。
  • 启动 APP 的时候, Android Framework 创建应用的 ClassLoader 实例,并将当前应用相关的所架构图怎么画有 so 文件所在目录注数据库查询语句入到当前 ClassLoader 相关字段。
  • 调用 System.loadLibr数据库设计ary(“xxx”), framework 从当前上下文 Classgithub永久回家地址Loader 实例(或者用户指定)的目录数组里查找并加载名为 libxxx.so 的文件。
  • 调用 so 相关架构工程师 JNI 方法。

而我们这里,由于so文件不存在于apk架构是什么意思当中,而是需要动态下载,所以我们显然不能直手机耗电快怎么办接使用系统的System.loadLibrary方法加载so文件。

而动态加载so的方法,在热修复和插件化框架中,已经比较成熟了,我们参考了市github面上的开源框架后,选择了腾讯的Tinker框架的加载方案,即使用反射classloader 将 so 包的路径写入 nativeLibraryPathElements 数组最前面,其流程图和解释如下图所示 。注意,此方法不同的android版本将有不同的实现。下面示例代码基于android9数据库系统工程师.0版本。


        private static void install(ClassLoader classLoader, File soFolder) throws Throwable {
 Field pathListField = findField(classLoader, "pathList" );
            Object dexPathList = pathListField.get(classLoader);
            Field nativeLibraryDirectories = findField(dexPathList, "nativeLibraryDirectories" );
            List<File> libDirs = (List<File>) nativeLibraryDirectories.get(dexPathList);
            libDirs.add(0, soFolder);
            Field systemNativeLibraryDirectories =
                    findField(dexPathList, "systemNativeLibraryDirectories" );
            List<File> systemLibDirs = (List<File>) systemNativeLibraryDirectories.get(dexPathList);
            Method makePathElements =
                    findMethod(dexPathList, "makePathElements" , List.class);
            libDirs.addAll(systemLibDirs);
            Object[] elements = (Object[]) makePathElements.
                    invoke(dexPathList, libDirs);
            Field nativeLibraryPathElements = findField(dexPathList, "nativeLibraryPathElements" );
            nativeLibraryPathElements.setAccessible(true);
            nativeLibraryPathElements.set(dexPathList, elements);
        }
  • pathList变量:DexPathList类的实例。
  • nativeLibraryDirectories列表:包含了本App数据库设计自带so文件的手机搬家查找路径(如data/githubapp/包名/lib/arm64)
  • systemNativeLibraryDirectories闭包函数列表:包含系统sgiteeo文件查找路径(如system/lib64)
  • makegitlabPathElements:系统使用此方法,为所有so文件,生成对应的 NativeLibraryElement对象
  • nativeLib架构师证书raryPathElements数组:系统用来存储所有的so文件路径

当外界调用System.loadLibrary方法时,系统最终会调用到DexPathList类的findLibrary方法,该方法会在nativeLibraryPathEle手机耗电快怎么办ments数组中查找对应的路径,我们将自己的so加入到nativGiteLibra手机ryPathElements最前面,由此达到动态加入so的目标。

  1. so资源数据库原理动态化的tips

  • 为何要使用Relinker加载So文件

    • 假如我们有giticomfort是什么轮胎2个so文件,libA.so架构是什么意思 和 libB.so,libA依赖libB,则当我们调用System数据库软件.loadLibrgiti轮胎ary(“libA”) 的时候,android framework 会通过上面提到的调用链最终通过 dlo闭包面试题pen 加载 libA.so 文件,并接着通过其依赖信息,自动使用 dlopen 加载 libB.so。
    • 在 Android N 以前,只要将 libA.so 和 libB.so 所在的文件目录路径都注入到当前 ClassLoader 的 nativeLibraryPathElements 里,则在加载 so 插件的时候,这两个架构师证书文件闭包的优缺点都能正常被找到。
    • 从 N 开始,libA.so 能正常加载,而 libB.so 会出现加载失败错误。
    • 因为Android N数据库软件ative 用来链接 so 库的 Linker.cpp dlopen 函数 的具体实现变化比较大(主要是引入了 Namespace 机制):以往的实现里,Linker 会在 ClassLoder 实例的 nativeLibraryPathElement手机耗电快怎么办s 里的所有路径查找相应的 so 文件。
    • 更新之后,Lin数据库系统概论第五版课后答案ker 里检索的路径在创建 ClassLoa手机银行der 实例后就被系统通过 Namesp架构图怎么画ace 机制绑定了,当我们注入新的路径之后,虽然 ClassLoader 里的路径增加了,但是 Linker 里 Namespace 已经绑定的路径集合并没有同步更架构工程师新,所以出现了 libA.so 文件能找到,而 libB.so 找不到的情况。
    • 至于 Namespace 机制的工作原理了,可以简单认为是一个以 Cla架构ssLoader 实例 HashCode 为 Key 的 Map,Native 层通过 ClassLoader 实例获取 Map 里存放的 Value(也就是 so 文件路径集合)。

解决该问题有如下几种思路:

  • 自定义 System.loadLibrary,加载 SO 前,先解析 SO 的依赖信息,再递归加载其依赖的 SO 文件,这是开源库soLoader的解决方案。
  • 自定义 Linker,完手机管家全自己控制 SO 文件的检索逻辑 ,这是开源库Relinder的解决方案。
  • 替换 Clas闭包的使用场景sLoader 。

本着不重复造轮子的原则,项目中使用了Relinker开源库,用来加载so文件。

  • so库依赖分析工具

想要把 so 动态化技术应用到 APK 的瘦身项目中来,除了分析哪些 so 文件体积占比比较大之外,最好的做法是将其依赖的所有 so 文件一定挪到插件包里。怎么了解 APK 里所有 so 文件具体的依赖信息呢闭包是什么意思?这里推荐一款 Google 开源的 APK 解析工具android-classyshark,除了提供分析 APK dex/so 依手机克隆赖信息之外,它还提供了 GUI 可视化界面,非闭包的理解常适合快速上手。

货拉拉 Android 动态资源管理系统原理与实践

  1. so动态化流程

  • so资源应用流程

  • 获取系统支持abi列表,根据该列表,找到合适的so动态资源实体类。
  • 如果该资源已经被加载缓存,则直接回调加载成功。
  • 否则,开始资源通用加载流程,并异步等待资源加载成功(流程见第5章)。
  • 再次判断下载校验后的资源,是否支持本机abi。
  • 将so包路径加入De手机耗电快怎么办xPathList的数组手机头部。
  • 遍历等待加载so列表,尝试加载所有so文件,并将成功加闭包面试题载的so文件,移除该列表。
  • 将资源架构图id和本地路径加入缓存,防止so被重复加载。
  • 回调加载完成监听器。

货拉拉 Android 动态资源管理系统原理与实践

  • SoLoadUtil.loadLibrary方法流程

从上一章我们知道,我们会使用transform api加asm框架,将系统的System.loadLibrary方法替换成我们的SoloadUt数据库原理il.loadLibrary方法。我们替换系统方法的目的。一个是为了保证so库不存在时,程序不崩溃,另外一个就是so库下载校验完成后,能自动完成之前失败的加载,为此,我们设计了如下流程。

  • 其他方法调用到我们的SoloadUtil时,我们判断我们的加载系统是否初始数据库系统工程师化完成
  • 已完成,则调用Relinkder库尝试加载so文件,未完成则将该so库加入待加载队列中。
  • 如果Relinker加载so文件成功,我们从待加载队列中移除so,并且完成本次加载。
  • 否则我们依然将so文件加入待加载队列中。
  • 根据上面的so加载流程,当so手机号查快递动态资源真正下载github架构图怎么制作验完成后,我们会遍历待加载队列,并完成所有之前未成功的so库加载。

货拉拉 Android 动态资源管理系统原理与实践

  1. dynami手机耗电快怎么办c_plugin插件流程

  • 整体流程

前面我们已经分析了通用资源加载,内置资源应用,完成了动态闭包的优缺点资源管理系统的主要部分。只剩下资源打包部分了,而所有资源的打包操作,都由dyanm数据库系统概论第五版课后答案ic_plugin插件来完成。为了完成打包功能,手机号最旺财的尾数我们决定在这个dynamic_plugin插件内部,新建3个T手机淘宝ask。

  • Hook System.loadL闭包是什么意思ibrary方法的Transform数据库系统概论第五版课后答案Task。
  • 系统打包流程中,删除并拷贝so文件的DeleteAndCopySo数据库系统工程师T手机管家ask。
  • 压缩so资源和其他多个文件资源(例如帧动画)的ZipResTask。
  • 为每个动态资源生成其对应的DynamicPkgInfo常量的功能,仅实现为一个普通方手机耗电快怎么办法。

所以主流程也就出来了

  • 读取并解析dynami闭包的理解c_plugin.gra手机号最旺财的尾数dle配置文件。
  • 根据配置信息,决定是否将3个t数据库查询语句ask加入任务队列。
  • 启动任务队列。

货拉拉 Android 动态资源管理系统原理与实践

  • Tra手机耗电快怎么办nsf架构图模板ormTask流程

该task流程,主要就是通过tranform api和asm框架的使用,我们在其中架构图怎么制作加入了扫描cl闭包是什么ass范围的可配置项。

  • 等待asm框架扫描class。
  • 手机淘宝断该class名称是否在我们配置的替换列表中,如果不在,就直接返回。
  • 创建ClassVisitor和MethodVisitor,等待asm框架扫闭包的优缺点描每个方法。
  • 如果该方法闭包的优缺点的名称,参数列表和调用者,都和System.loadLibrary方法相符合。
  • 我们替换为自己的Soload闭包是什么意思Util.loadLibrary数据库系统的核心是方法。

货拉拉 Android 动态资源管理系统原理与实践

  • DeleteAndCopySoTask流程

  • 根据配置文件,找到系统的merge和strip task。
  • 将我们的task插入到2个系统task之间,并等待系统回调我们的doLast方闭包的作用法。
  • 遍历系统的mergeTask的输出目录,判断该so文件是否在我们配置的待扫描列表中。
  • 如果配置了需要拷贝so文件,则我们将它拷贝到指定位置。
  • 如果配置了需要删除so文件,则我们将该so文件删除。

货拉拉 Android 动态资源管理系统原理与实践

  • ZipResTask流程

  • 拷贝字体文件,将文件信息加入资源列表。
  • 压缩帧动画文件,将压缩后的文件信息加入资源列表。
  • 压缩so文件,将压缩后的文件架构图模板信息加入资源列表。
  • 压缩zip文件夹下文件,将压缩后Git的文件信息加入资源列表。
  • 遍历资源文件,为其生成相应的资源实体类DynamicPkgInfo。

货拉拉 Android 动态资源管理系统原理与实践

  1. dynamic_plugin插件类设计

可以与第数据库系统概论第五版课后答案4章,整数据库系统概论体架构图结合起来看。

  • 系统插件层

DynamicPlugin类,实现了系统gradle插件的plugin接口,为我们整个插件的入口,主要解析数据库设计配置文件,并按照配置文件创建task信息。

DynamicParam类,提供了存储并解析dyanmi数据库管理系统c_plugin配置文件的方闭包函数法。

  • 任务模块层

ITask接口,架构师证书代表了一个我们定义的任务。

DeleteAndCopySoTas数据库查询语句k,删除并拷贝so文件任务。

TransfomrTask,替换系统System.loadLibrary方法git命令任务。

ZipResTask,压缩so和其他文件,并生成对应的java资源实体类方法。

  • 底层实现层

SystemL手机怎么定位别人手机oadClassVisitor类,Asm框架的class访问类。

SystemLoadMethodVisitor类,Asm框架的method访问类,用于替换System.lo数据库设计adLibrary方法。

JavaFileCreate类,使用javapoet框架产生jav架构图怎么画a文件。

其他辅助类,在此省略

  • 类uml图

货拉拉 Android 动态资源管理系统原理与实践

  1. dynamigit命令c_config.gradle配置文件

该配置文件主要包含了配置dynamic_pluggiteein插件运行步骤,插件闭包数学输入输出路径,so文件扫描路径架构图等信息。


dynamic_config = [
        //是否执行替换System.loadlibrary操作
  is_replace_load_library: false,
        //是否执行替换System.load操作
  is_replace_load : false,
        //是否执行删除so文件操作
  is_delete_so : false,
        //是否执行将so文件拷贝到其他目录操作
  is_copy_so : false,
        //是否执行将动态资源打包,并生成java文件操作
  is_zip_res : false,
        //是否执行将so文件打包,并生成java文件操作
  is_zip_so : false,
        //是否自动上传所有资源,上传方法为dynamic_upload
  is_upload_res : false,
        //插件是否工作在Release模式下
  is_release_type : isReleaseBuildType(),
        //是否打印debug日志
  is_debug_log : true,
        //自动创建java文件时的包名
  create_java_pkg_name : 'com.test' ,
]
 /**
 * 配置要删除和拷贝的so文件
 * map的key为压缩包名称,值为压缩包包含的so文件列表
 * key为debug_all_test时,会压缩所有so包
 */
dynamic_scan_so_map = [
        guang_dong : [ 'libpajf.so' , 'libpajf_av.so' , 'libsqlite.so' ],
]
dynamic_so_config = [
        //so文件忽略列表,该表中的文件,不会被扫描。不在该列表中的文件都会被扫描
 // (dynamic_scan_so_map为空时,本列表才生效)
  ignore_so_files: [],
        //so文件扫描abi目录,不在该目录下的so将不被扫描
  scan_so_abis : [ "arm64-v8a" , "armeabi-v7a" ],
        //拷贝出来的so文件夹前缀,ignore_so_files生效时使用
  so_input_prefix: 'test' ,
]
dynamic_lib_list = [
        //只有该列表中的包名,才会执行替换System.loadlibrary操作
 //输入debug_all_test,则会替换所有System.loadLibrary方法,用于测试
  scan_load_library_pkgs : [],
        //在该列表中的包名或者类名,不会执行替换System.loadlibrary操作,和上面的配置可以同时生效
  ignore_load_library_pkgs: [],
]
 //该配置不要改动内容,需要改变路径的,直接改变对应的方法内容即可
dynamic_dir = [
        //产生文件的输出目录
  output : createOrGetOutputPath(),
        //字体资源输入目录
  typeface_input : createOrGetInputTypafacePath(),
        //帧动画资源输入目录
  frame_anim_input: createOrGetInputFrameAnimPath(),
        //so文件资源输入目录
  so_input : createOrGetInputSoPath(),
        //zip包输入目录
  zip_input : createOrGetInputZipPath()
]
 //该配置项,配置了android 2个gradle task的名称
 //主工程的mergeNativeLibs合并所有依赖的 native 库
 //主工程的stripDebugSymbols从 Native 库中移除 Debug 符号。
dynamic_task = [
        //自定义的task运行哪里
 //true为mergeNativeLibs之后,stripDebugSymbols之前
 //false为stripDebugSymbols之后,package之前
  isTaskRunAfterMerge : true,
        //debug状态下,mergeNativeLibs的task名称
  debugMergeNativeLibs : "mergeDebugNativeLibs" ,
        //release状态下,mergeNativeLibs的task名称
  releaseMergeNativeLibs : "mergeReleaseNativeLibs" ,
        //debug状态下,stripDebugSymbols的task名称
  debugStripDebugSymbols : "stripDebugDebugSymbols" ,
        //release状态下,stripDebugSymbols的task名称
  releaseStripDebugSymbols: "stripReleaseDebugSymbols" ,
        //debug状态下,系统打包task名称
  debugPackage : "packageDebug" ,
        //release状态下,系统打包task名称
  releasePackage : "packageRelease" ,
        //debug状态下,mergeNativeLibs的输出目录
  debugNativeOutputPath : " $ { projectDir }/app/build/intermediates/merged_native_libs/debug/out/lib" ,
        //release状态下,mergeNativeLibs的输出目录
  releaseNativeOutputPath : " $ { projectDir }/app/build/intermediates/merged_native_libs/release/out/lib" ,
        //debug状态下,,stripDebugSymbols的输出目录
  debugStripOutputPath : " $ { projectDir }/app/build/intermediates/stripped_native_libs/debug/out/lib" ,
        //release状态下,,stripDebugSymbols的输出目录
  releaseStripOutputPath : " $ { projectDir }/app/build/intermediates/stripped_native_libs/release/out/lib" ,
]
 //该闭包可以自动将文件上传到服务器,参数列表为资源id,资源文件路径
 //我们可以再次执行上传服务器操作,并返回对应的url。
 //当然也可以不实现上传操作,并自己手动上传资源。
dynamic_upload = {
 id, path ->
 println( "dynamic_upload id $ { id } ,path $ { path }" )
        return 'http://url'
 }
  1. 优化效果

通过引入动态资源管理系统,并将一gitee键报警sdk相关的so文件和其他普通资源架构师证书动态化后,货拉拉用户端的包体积减少了8M,从54git命令M变为了46M。后继将会继续Git尝试进行其他so文件的动态化。

货拉拉 Android 动态资源管理系统原理与实践

  1. 参考文献

www.jianshu.com/p/260137fdf…

mp.weix架构工程师in.qq.com/s/X58f闭包是什么意思K02im…

发表回复

提供最优质的资源集合

立即查看 了解详情