设为首页 - 加入收藏   
您的当前位置:首页 > 娱乐 > 插件化工程R文件瘦身技妙筹划 正文

插件化工程R文件瘦身技妙筹划

来源:头一无二网 编辑:娱乐 时间:2024-10-16 18:39:24

 

原问题:插件化工程R文件瘦身技妙筹划 | 京东云技术团队

随着营业的插件程R筹划发展及版本迭代,客户端工程中不断削减新的化工营业逻辑、引入新的文件资源,随之而来的瘦身成果便是装置包体积变大,前期各个营业模块经由无用资源删减、技妙大图缩短或者转上云、插件程R筹划AB 试验营业逻辑下线或者其余伎俩在着落包体积上取患了未必的化工成果。

在瘦身的文件历程中咱们关注到了 R 文件瘦身的意见,当初京东 APP 是瘦身反对于插件化的,有营业插件工程、技妙宿主工程,插件程R筹划对于营业插件包文件妨碍合成,化工发现除了老例的文件资源及代码外,R 类文件约莫占包体积的瘦身 3%~5% 左右,对于宿主工程包文件妨碍合成,技妙R 类文件占比也有 3% 左右。咱们先后在对于 R 类文件瘦身的可行性及业界开源名目妨碍调研后,探究出了一套适用于插件化工程的 R 文件瘦身技妙筹划。

事实根基 —R 文件

R 文件也便是咱们同样平凡使掷中每一每一打交道的 R.java 文件,在 Android 开辟规范中咱们需要将运用中用到的资源分说放入特意命名的资源目录中,外部化运用资源以便对于其妨碍径自呵护。

外部化运用资源后,咱们可在名目中运用 R 类 ID 来拜候这些资源,且 R 类 ID 具备仅有性。

public class MainActivity extends BaseActivity {

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

}

在 android apk 打包流程中 R 类文件是由 aapt(Android Asset Packaing Tool)工具打包天生的,在天生 R 类文件的同时对于资源文件妨碍编译,天生 resource.arsc 文件,resource.arsc 文件至关于一个文件索引表,运用层代码经由 R 类 ID 可能拜候到对于应的资源。

R 文件瘦身的可行性合成

同样平凡开辟阶段,在主工程中经由 R.xx.xx 的方式援用资源,经由编译后 R 类援用对于应的常量会被编译进 class 中。

setContentView(2131427356);

这种变换叫做内联,内联是 java 的一种机制(假如一个常量被符号为 static final,在 java 编译的历程中会将常量内联到代码中,削减一次变量的内存寻址)。

非主工程中,R 类资源 ID 以援用的方式编译进 class 中,不会产生内联。

setContentView(R.layout.activity_main);

产生这种征兆的原因是 AGP 打包工具导致的。详尽细节,大师可能去查阅一下 android gradle plugin 在 R 文件上的解决历程。

论断:R 类 id 内联后挨次可运行,但并非所有的工程都市被动产生内联征兆,咱们需要经由技术伎俩在适量的机缘将 R 类 id 内联到挨次中,内联实现后,因为再也不依附 R 类文件,则可能将 R 类文件删除了,在运用个别运行的同时,达到包瘦身指标。

插件化工程 R 文件瘦身实战

拟订技妙筹划

当初京东 Android 客户端是反对于插件化的,全部插件化工程包罗公共库(是一个 aar 工程,用来寄存组件以及宿主共用的类以及资源)、营业插件(插件工程是一个自力的工程,编译产物可能运行在宿主情景中)、宿主(主工程,提供运行情景)。在插件化的历程中为了防御宿主以及插件资源矛盾,经由更正插件 packageId 保障了资源的仅有性。因为公共资源库、宿主是被良多营业依附,对于这两个名目妨碍改动评估影响波及比照多,插件平凡都是营业模块自行呵护,不存在被依附成果,以是先在营业插件模块妨碍 R 类瘦身事实。

对于营业插件工程打出的包妨碍反编译日后,发现 R 类 ID 无内联征兆,且 R 类文件具备未必的巨细,对于包内的 R 文件妨碍合成,发现 R 文件中仅包罗营业自身的资源,不包罗营业依附的公共资源 R 类。

public View onCreateView(LayoutInflater paramLayoutInflater, ViewGroup paramViewGroup, Bundle paramBundle) {

this.b = paramLayoutInflater.inflate(R.layout.lib_pd_main_page, paramViewGroup, false);

this.h = (PDBuyStatusView)this.b.findViewById(R.id.pd_buy_status_view);

this.f = (PageRecyclerView)this.b.findViewById(R.id.lib_pd_recycle_view);}

散漫对于业界开源项指标调研合成,试验拟订适宜京东商城的技妙筹划并优先在营业插件内实现 R 类 ID 内联并删除了对于应的 R 文件。

1. 经由 transform api 收集要解决的 class 文件

Transform 是 Android Gradle 提供的操作字节码的一种方式,它在 class 编译成 dex 以前经由一系列 Transform 解决来实现更正.class 文件。

@Override

public void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {

super.transform(transformInvocation);

// 经由TransformInvocation.getInputs()获取输入文件,有两种

// DirectoryInpu以源码方式退出编译的目录结构及目录下的文件

// JarInput以jar包方式退出编译的所有jar包

allDirs = new ArrayList<>(invocation.getInputs().size());

allJars = new ArrayList<>(invocation.getInputs().size());

Collection<TransformInput> inputs = invocation.getInputs();

for (TransformInput input : inputs) {

Collection<DirectoryInput> directoryInputs = input.getDirectoryInputs();

for (DirectoryInput directoryInput : directoryInputs) {

allDirs.add(directoryInput.getFile());

}

Collection<JarInput> jarInputs = input.getJarInputs();

for (JarInput jarInput : jarInputs) {

allJars.add(jarInput.getFile());

}

}

}

2. 对于收集到的.class 文件散漫 ASM 框架妨碍合成解决

ASM 是一个操作 Java 字节码的类库,经由 ASM 咱们可能不便对于.class 文件妨碍更正。

优先识别 R 类文件,经由 ClassVisitor 拜候 R.class 文件,读取文件中的动态常量,妨碍临时变量存储:

@Overridepublic FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { //R类中收集 public static final int 对于应的变量 if (JDASMUtil.isPublic(access) && JDASMUtil.isStatic(access) &&JDASMUtil.isFinal(access) &&JDASMUtil.isInt(desc)) { jdRstore.addInlineRField(className, name, value); } return super.visitField(access, name, desc, signature, value);}

非 R 类文件,经由 MethodVisitor 识别到代码中的 R 类援用,获取援用对于应的值,妨碍 id 值替换:

@Override

public void visitFieldInsn(int opcode, String owner, String name, String desc) {

if (opcode == Opcodes.GETSTATIC) {

//owner:包名;name:详尽变量名;value:R类变量对于应的详尽id值

Object value = jdRstore.getRFieldValue(owner, name);

if (value != null) {

//调用该api实现值替换

mv.visitLdcInsn(value);

return;

}

}

super.visitFieldInsn(opcode, owner, name, desc);

}

* 注:以上代码仅为全副表今世码,非正式插件代码。

在营业模块引入 R 类瘦身插件后,营业模块功能可个别运行,且插件包巨细均有 3%~5% 区别水平的削减。

公共资源 R 类 ID 内联

因为在京东 android 客户端代码中,更多的资源文件会集在公共资源库中,相对于的公共库天生的 R 类文件也更大,对于编译后的 apk 包内容妨碍合成后,公共资源库的 R 类文件占比高达 3%。

公共库追寻宿主一起打包,在宿主打包历程中引入 R 类瘦身插件,打包后的 apk 有清晰的减小,手机装置 apk 后启动首页个别展现无成果,但在关上某些营业插件时,会有颇为闪退征兆,解体规范为 R.x resource not found。对于解体原因合成如下:营业插件代码中运用了公共库中的 R 类资源、插件打包流程自力于宿主打包,在插件打包的历程中仅实现为了营业模块 R 类的内联,并无思考到公共资源 R 类的内联,基于上述原因当宿主打包历程实现 R 类文件删除了瘦去世后,咱们在运行某营业插件的历程中,人造就会报公共资源 R 类找不到的成果从而产生解体。

为了解决这个成果一开始的妄想构想是削减白名单机制,keep 住所有被营业模块运用的公共资源,但很快这个想法就被倾覆,公共资源存在自身便是愿望各个营业模块间接援用这全副资源,而不是自己界说,假如 keep 住的话,未必有很大一全副的资源无奈删减,瘦身的成果会大打折扣。

既然保存的妄想并不适量,那就将公共资源 R 类 id 也内联到代码中去。前面提到京东是反对于插件化的,全部插件化妄想是基于 aura 平台实现的,咱们向 aura 团队妨碍了咨询,而后 get 到了新的妄想切入点。

aura 平台在插件化的历程中已经由 aapt2 引入了公共资源 id 牢靠的能耐,在该能耐下,已经界说的公共资源 id 会不断牢靠 (各个营业插件中援用的公共资源 id 不同),且公共资源库中已经有的资源不可被其余模块重复界说,否则会拆穿困绕以前已经界说好的资源,基于上述的服从以及规定,咱们对于以前的 R 文件瘦身 gralde plugin 功能妨碍美满,将公共资源的 R 类 id 内联到名目中。

运用 appt2 的 - stable-ids 以及 - emit-ids 两个参数实现固化资源 id 的功能,并将将固化后的 ids 文件命名为 shared_res_public.xml 存储在公共资源库中,营业插件依附公共资源库,在打包编译的历程中 aura 会将 shared_res_public.xml 复制到营业工程临时编译文件夹 intermediates 下的指定位置并退出营业模块的打包历程中,其文件内容名目如下:

更正 R 文件瘦身 gradle plugin 代码,从指定位置读取并识别这全副公共资源,遵照 <name,id> 的方式妨碍变量存储,并在后续历程中对于营业模块中的公共资源全副妨碍 id 替换。

public Map<String, String> parse() throws Exception {

if (in == null) {

return null;

}

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

DocumentBuilder builder = factory.newDocumentBuilder();

Document doc = builder.parse(in);

Element rootElement = doc.getDocumentElement();

NodeList list = rootElement.getChildNodes();

return resNode;

}

}

R 类资源 id 内联全副代码如下:

public void visitFieldInsn(int opcode, String owner, String name, String desc) {

if (opcode == Opcodes.GETSTATIC) {

//优先从营业模块R类资源中查找

Object value = jdRstore.getRFieldValue(owner, name);

if (value != null) {

mv.visitLdcInsn(value);

return;

}

//从公共R类资源中查找

value = getPublicRFileValue(name);

if (value != null) {

mv.visitLdcInsn(value);

return;

}

}

super.visitFieldInsn(opcode, owner, name, desc);

}

该妄想美满后,散漫商详营业插件妨碍了验证,在商详及宿主均实现 R 文件内联瘦去世后,商详模块营业功能可个别运用,无颇为征兆。

思考到 R 文件内联瘦身 gradle plugin 是在打包编译阶段引入的,咱们也统计了一下引入该插件日后对于打包时长的影响,数据如下:

散漫数据来看,引入 R 文件瘦身插件后对于部份打包时长并无清晰影响。

至此,基于京东商城探究的插件化工程 R 文件瘦身 gradle plugin 就开辟实现,当初已经在全副营业插件模块妨碍了线上验证,在功能上线日后咱们也实时的妨碍清晰体审核以及用户反映的跟进,暂无颇为成果。尽管环抱 R 文件瘦身缩减包体积这个指标,开辟职员有林林总总的技妙筹划,上述妄想不未必适用于所有的客户端开辟系统,此外后续也将环抱包瘦身这一常态事务建树一系列的相干工具,退出使命之中的各个阶段,功能、实用的操作包体积的削减,如大师在瘦身方面有相干倡讲以及想法也招待大师来一起品评辩说。

参考文章:

Gradle Plugin:

https://docs.gradle.org/current/userguide/custom_plugins.html

Gradle Transform:

https://developer.android.com/reference/tools/gradle-api/7.0/com/android/build/api/transform/Transform

APK 构建流程:

https://developer.android.com/studio/build/index.html?hl=zh-cn#build-process

作者:耿蕾 田立异 源头:京东云开辟者社区

返回搜狐,魔难更多

责任编纂:

热门文章

3.751s , 11260.6171875 kb

Copyright © 2024 Powered by 插件化工程R文件瘦身技妙筹划,头一无二网  

sitemap

Top