R 文件优化插件:Binary XML file in layout Error inflating class

2024-06-15 06:36

本文主要是介绍R 文件优化插件:Binary XML file in layout Error inflating class,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

场景一:构造函数缺失

问题

自定义布局(FlagmentLayout)加载自定义属性失败,导致广告显示异常,甚至是闪退~

InflateException 在 Android 中我们遇到的通常发生在自定义 View 创建中,动态加载布局一般使用 View view = View.inflate(context, int resource, ViewGroup root);,View.inflate 方法具有多个重载函数。

这里日志大概意思是:
在布局文件 layout/jiy 的第 7 行,存在一个自定义 view com.xphxqk.bmerth 加载失败。

Caused by:
android.view.InflateException:
Binary XML file line #7 in com.primer.hello:layout/jiy:
Error inflating class com.xphxqk.bmerth

这里日志大概意思是:
java.lang.reflect 可知此处属于反射,说的是反射调用异常

Caused by: java.lang.reflect.InvocationTargetException

为什么存在反射相关呢?
根据下面的日志可知,动态加载布局最中是通过反射创建对象(XML 文件里面的 view 是如何初始化的?)。

Constructor.newInstance
… …
LayoutInflater.createView
… …
LayoutInflater.inflate
… …

错误日志

 android.view.InflateException: Binary XML file line #7 in com.primer.hello:layout/jiy:Binary XML file line #7 in com.primer.hello:layout/jiy: Error inflating class com.xphxqk.bmerthCaused by: android.view.InflateException: Binary XML file line #7 in com.primer.hello:layout/jiy:Error inflating class com.xphxqk.bmerthCaused by: java.lang.reflect.InvocationTargetExceptionat java.lang.reflect.Constructor.newInstance0(Native Method)at java.lang.reflect.Constructor.newInstance(Constructor.java:343)at android.view.LayoutInflater.createView(LayoutInflater.java:858)at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1010)at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:965)at android.view.LayoutInflater.rInflate(LayoutInflater.java:1127)at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088)at android.view.LayoutInflater.rInflate(LayoutInflater.java:1130)at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088)at android.view.LayoutInflater.rInflate(LayoutInflater.java:1130)at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088)at android.view.LayoutInflater.inflate(LayoutInflater.java:686)at android.view.LayoutInflater.inflate(LayoutInflater.java:538)at android.view.LayoutInflater.inflate(LayoutInflater.java:485)at com.JBruhU.OewGelzz.GFAvHQl.iPclUCgYxLa.OAOQTuGm.OOooo(SourceFile:13)at com.JBruhU.OewGelzz.GFAvHQl.iPclUCgYxLa.OAOQTuGm.OooOO0(SourceFile:22)at com.JBruhU.OewGelzz.GFAvHQl.MXGbW.VRyBu.<init>(SourceFile:9)at com.JBruhU.OewGelzz.GFAvHQl.MXGbW.VkNMUXIrzzB.<init>(SourceFile:3)at com.JBruhU.OewGelzz.GFAvHQl.MXGbW.VkNMUXIrzzB.<init>(SourceFile:2)at com.JBruhU.OewGelzz.GFAvHQl.MXGbW.VkNMUXIrzzB.<init>(SourceFile:1)at com.JBruhU.OewGelzz.GFAvHQl.iPclUCgYxLa.OAOQTuGm.<init>(SourceFile:1)at OOO0o0o0.o0OOO0OO.getMsgViewByOpentype(SourceFile:19)at OOO0o0o0.o0OOO0OO.dispatchNativeAd(SourceFile:259)at oOo0oo.OOOooo0.OOooO0O0(Unknown Source:4)at oOo0oo.O0oOOo.O0o0o(Unknown Source:380)at oOo0oo.O0oOOo$ooo0OOOO.run(Unknown Source:8)at o0o0oo0.O0o0O0oo.o0OOO0OO(Unknown Source:15)at oOo0oo.O0oOOo.O0ooo0(Unknown Source:57)at com.pokfzu.doljxp$o0OOO0OO.run(Unknown Source:46)at android.os.Handler.handleCallback(Handler.java:938)at android.os.Handler.dispatchMessage(Handler.java:99)at android.os.Looper.loopOnce(Looper.java:233)at android.os.Looper.loop(Looper.java:344)at android.app.ActivityThread.main(ActivityThread.java:8191)at java.lang.reflect.Method.invoke(Native Method)at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:584)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1034)Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lorg/xqq/w/R$styleable;at com.xphxqk.bmerth.<init>(SourceFile:5)... 37 moreCaused by: java.lang.ClassNotFoundException: org.xqq.w.R$styleable... 38 more

看到这个错误隐约记得之前遇到过,但是一时间想不起来,直到后来记起才就此记录一笔。

以 FragmentLayout 为例,查看源码我们知道他有四个重载的构造函数,什么时候使用那个构造函数比较合适我们可能不清楚,但是我们在自定义 View 时候通常会加入新的属性 attrs,那么会使用到构造函数2️⃣,在函数里获取属性attrs值再进一步操作。

@RemoteView
public class FrameLayout extends ViewGroup {//构造函数 1️⃣public FrameLayout(@NonNull Context context) {super(context);}//2️⃣public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}//3️⃣public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs,@AttrRes int defStyleAttr) {this(context, attrs, defStyleAttr, 0);}//4️⃣public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs,@AttrRes int defStyleAttr, @StyleRes int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);}//other ... ...
}

通过反射创建一个类,反射创建传给构造函数的参数不一致(在类内找不到合适的构造函数),那么会发生反射调用异常,也就是上述的InvocationTargetException异常,最终是对象创建失败!

在以前遇到的案例中:
就是这种构造函数缺失(反射找不到合适的构造函数创建实例)导致自定义 View 创建失败,布局动态加载失败。

那么为什么会缺失构造函数?

  • 场景一:自定义 view 时,漏写对象的构造函数(比如你使用 View.inflate 加载布局,可是没写构造函数2️⃣)
  • 场景二:开启混淆,在打包混淆过程构造函数被移除(之前遇到过的就是这种情况)

解决

  • 场景一:自然是补充构造函数(重写)
  • 场景二:通过 keep 规则保持自定义 view 类内的构造函数在混淆过程中不被移除(例如:-keep class com.primer.view.*{*;}

还没完,下面开始才是本次遇到的场景三,新方向,新问题~

场景二:R 文件优化插件

问题

纳闷了,在我的 AGP4+ 项目打包运行正常,在 AGP7+ 项目打包运行闪退!

看混淆映射 mapping.txt 文件,自定义 view 类的构造函数都在,没有混淆被移除,所以可以确定不是因构造函数缺失导致的闪退。

查看 class 文件通常能看到这两个东西:

  • <init> 是实例构造函数(new 等非静态初始化)
  • <cinit> 是类构造器函数(静态属性等初始化)

在这里插入图片描述

源代码

以下分析的是混淆之后的包,所以类名称和部分方法名可能不一样,都是混淆后的。

这就是那个自定义布局,我们自定义了新的属性 attr,所以用到下面这个构造函数,并且在函数里获取自定义属性值。

自定义两个属性

<resources><declare-styleable name="RatioFrameLayout"><attr name="frameratio" format="float" /><attr name="orientation" format="integer" /></declare-styleable>
</resources>

动态加载布局,反射创建实例是被调用的构造函数

public RatioFrameLayout(Context context, AttributeSet attrs) {this(context, attrs, 0);TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RatioFrameLayout);mRatio = typedArray.getFloat(R.styleable.RatioFrameLayout_frameratio, 0f);mScreenOrientation = typedArray.getInt(R.styleable.RatioFrameLayout_orientation, 0);typedArray.recycle();
}

分析

AGP4+

AGP4+ 上打的包,查看 smile obtainStyledAttributes(AttributeSet,int[]) 方法,方法调用传入的第二个参数v1 是一个整型数组这是符合要求的,并且数组是有值的,这两值正是对应自定义属性 RatioFrameLayout 的两个属性的属性ID,这是完全正确的,运行正常。

在这里插入图片描述

属性 ID 在打包过程中 build 目录下可以查看,在包体全局搜索也能找到。

在这里插入图片描述
在这里插入图片描述

通过 smile 了解 R 文件引用:

  • obtainStyledAttributes(attrs, R.styleable.RatioFrameLayout);

方法的第二个参数是 int[] 整型数组,查看上述的 smile 也能知道,所以 R.styleable.RatioFrameLayout 本质上它的值就是一个 int[]

  • typedArray.getInt(R.styleable.RatioFrameLayout_orientation, 0);
  • typedArray.getFloat(R.styleable.RatioFrameLayout_frameratio, 0f);

一开始我不知道 R.styleable.RatioFrameLayout_frameratio 到底是一个什么样的值?通过日志打印结合 smile 了解到这就是属性 RatioFrameLayout 的索引,实际上对应的就是属性数组 typedArray 的索引,通过索引确定位置获取属性值。

AGP7+

在 AGP7+ 发生了什么?

obtainStyledAttributes 方法的第二个参数 v1 怎么不是数组的具体值?而是指向:从 org.xqq.w.R 类获取值。难道从这个 R 类获取不到值吗?

在这里插入图片描述

查看日志和代码,这个两个属性确实是存在的(混淆后的)

在这里插入图片描述

只是这个属性存在包名的 R 类下,而不是另外一个不确定的包下 org.xqq.w

在这里插入图片描述

这指定是有问题,这个类的 R 类就没有内容。所以第二个参数 v1 执行的是类下的某个属性,并未获取到具体的值?

所以,我们大胆猜测:
是否是因为没有获取到第二个参数 R.styleable.RatioFrameLayout 的值,导致传入参数异常,反射创建实例失败?

在这里插入图片描述

解决方案一

查看包体,既然属性值是真实存在的(在包名的 R 类下),并且我们知道 R.styleable.RatioFrameLayout 的值本质就是一个整型数组 int[],那么我们是否可通过别的方式获取到 RatioFrameLayout 两个属性的值再组成一个数组传给 obtainStyledAttributes 就可以了?

经验证,方案缺失可行!

    public RatioFrameLayout(Context context, AttributeSet attrs) {this(context, attrs, 0);//闪退代码:R.styleable.RatioFrameLayout 没有值//TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RatioFrameLayout);//修改后:分别获取 frameratio、orientation 的属性ID,重组成整型数组//注:开启资源混淆时,需要 keep 属性 frameratio、orientation//如,需配置微信资源混淆白名单 "R.attr.orientation","R.attr.orientation"int id1 = context.getResources().getIdentifier("frameratio", "attr", context.getPackageName());int id2 = context.getResources().getIdentifier("orientation", "attr", context.getPackageName());int[] attrsId = {id1, id2};LogUtil.d("RatioFrameLayout", " id1 = " + id1 + "  id2 = " + id2);TypedArray typedArray = context.obtainStyledAttributes(attrs, attrsId);int a1 = R.styleable.RatioFrameLayout_frameratio;int a2 = R.styleable.RatioFrameLayout_orientation;LogUtil.d("RatioFrameLayout", " a1 = " + a1 + "  a2 = " + a2);mRatio = typedArray.getFloat(a1, 0f);mScreenOrientation = typedArray.getInt(a2, 0);typedArray.recycle();}

在这里插入图片描述

再次闪退

本以为万事大吉,准备下班

谁知道还能遇到闪退,测试反馈说小米激励视频广告闪退~

闪退日志

其他日志基本与上述一致,主要区别在于闪退来自小米广告 sdk 内部

Constructor.newInstance
… …
LayoutInflater.inflate
… …
com.miui.zeus.mimo.sdk.p4.a(SourceFile:26)
… …
com.miui.zeus.mimo.sdk.ad.reward.view.RewardTemplateP2View.a(SourceFile:2)

com.miui.zeus.mimo.sdk.view.DownloadBtnView.(SourceFile:19)

 FATAL EXCEPTION: mainProcess: com.primer.hello, PID: 23659java.lang.RuntimeException: Unable to start activity ComponentInfo{com.primer.hello/com.miui.zeus.mimo.sdk.ad.reward.RewardVideoAdActivityNewPE}: android.view.InflateException: Binary XML file line #119 in com.primer.hello:layout/mimo_reward_template_p_2: Binary XML file line #119 in com.primer.hello:layout/mimo_reward_template_p_2: Error inflating class <unknown>at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3308)at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3457)at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2044)at android.os.Handler.dispatchMessage(Handler.java:107)at android.os.Looper.loop(Looper.java:224)at android.app.ActivityThread.main(ActivityThread.java:7562)at java.lang.reflect.Method.invoke(Native Method)at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)Caused by: android.view.InflateException: Binary XML file line #119 in com.primer.hello:layout/mimo_reward_template_p_2: Binary XML file line #119 in com.primer.hello:layout/mimo_reward_template_p_2: Error inflating class <unknown>Caused by: android.view.InflateException: Binary XML file line #119 in com.primer.hello:layout/mimo_reward_template_p_2: Error inflating class <unknown>Caused by: java.lang.reflect.InvocationTargetExceptionat java.lang.reflect.Constructor.newInstance0(Native Method)at java.lang.reflect.Constructor.newInstance(Constructor.java:343)at android.view.LayoutInflater.createView(LayoutInflater.java:858)at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1010)at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:965)at android.view.LayoutInflater.rInflate(LayoutInflater.java:1127)at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088)at android.view.LayoutInflater.rInflate(LayoutInflater.java:1130)at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088)at android.view.LayoutInflater.rInflate(LayoutInflater.java:1130)at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088)at android.view.LayoutInflater.rInflate(LayoutInflater.java:1130)at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088)at android.view.LayoutInflater.inflate(LayoutInflater.java:686)at android.view.LayoutInflater.inflate(LayoutInflater.java:538)at android.view.LayoutInflater.inflate(LayoutInflater.java:481)at com.miui.zeus.mimo.sdk.p4.a(SourceFile:26)at com.miui.zeus.mimo.sdk.p4.a(SourceFile:23)at com.miui.zeus.mimo.sdk.ad.reward.view.RewardTemplateP2View.a(SourceFile:2)at com.miui.zeus.mimo.sdk.ad.reward.RewardTemplatePType$5.newTemplateView(SourceFile:1)at mimo_1011.NCall.IV(Native Method)at com.miui.zeus.mimo.sdk.ad.reward.RewardVideoAdActivityNewPE.onCreate(Unknown Source:18)at android.app.Activity.performCreate(Activity.java:7893)at android.app.Activity.performCreate(Activity.java:7880)at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1307)at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3283)at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3457)at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2044)at android.os.Handler.dispatchMessage(Handler.java:107)at android.os.Looper.loop(Looper.java:224)at android.app.ActivityThread.main(ActivityThread.java:7562)at java.lang.reflect.Method.invoke(Native Method)at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lorg/xqq/w/R$styleable;at com.miui.zeus.mimo.sdk.view.DownloadBtnView.<init>(SourceFile:19)... 37 moreCaused by: java.lang.ClassNotFoundException: org.xqq.w.R$styleable

查看源码,同是obtainStyledAttributes方法这里,引用指定的 R 类经也是之前错误的 org.xqq.w.R。这就很奇怪了

在这里插入图片描述
在这里插入图片描述

上述的解决方案只能修改自己的源码解决问题,但这里是第三方代码按照上述方案修改不切实际,还不清楚是否还有很多这样的危险代码,难道都要手动一一修改吗?

后来怀疑是否有其他地方(插件)干预 R 类的生成或引用?

突然,我想起来了!我们打包项目里缺失使用了 R 文件优化的插件

  • AGP4+ 项目使用的是:字节 Shrink-r-plugin 插件
  • AGP7+ 项目使用的是:滴滴 Booster-transform-r-inline 插件

之前做 AGP4+ 升级到 AGP7+ 时遇到 shrink 插件问题,似乎是不支持 AGP7,所以找了滴滴的这个插件作为替代品。

插件的主要功能是:

  • R文件常量内联,R文件瘦身
  • 无用Resource资源检查
  • 无用assets检查
  • 内联资源索引,例如R$id、R$layout、R$string等字段

解决方案二

不出意外,经测试,移除 booster-transform-r-inline 插件的引用打包运行一切正常了!

//apply
//classpath "com.didiglobal.booster:booster-gradle-plugin:$booster_version"
//classpath "com.didiglobal.booster:booster-transform-r-inline:$booster_version"

这篇关于R 文件优化插件:Binary XML file in layout Error inflating class的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1062692

相关文章

小白也能轻松上手! 路由器设置优化指南

《小白也能轻松上手!路由器设置优化指南》在日常生活中,我们常常会遇到WiFi网速慢的问题,这主要受到三个方面的影响,首要原因是WiFi产品的配置优化不合理,其次是硬件性能的不足,以及宽带线路本身的质... 在数字化时代,网络已成为生活必需品,追剧、游戏、办公、学习都离不开稳定高速的网络。但很多人面对新路由器

Spring Boot Maven 插件如何构建可执行 JAR 的核心配置

《SpringBootMaven插件如何构建可执行JAR的核心配置》SpringBoot核心Maven插件,用于生成可执行JAR/WAR,内置服务器简化部署,支持热部署、多环境配置及依赖管理... 目录前言一、插件的核心功能与目标1.1 插件的定位1.2 插件的 Goals(目标)1.3 插件定位1.4 核

MySQL深分页进行性能优化的常见方法

《MySQL深分页进行性能优化的常见方法》在Web应用中,分页查询是数据库操作中的常见需求,然而,在面对大型数据集时,深分页(deeppagination)却成为了性能优化的一个挑战,在本文中,我们将... 目录引言:深分页,真的只是“翻页慢”那么简单吗?一、背景介绍二、深分页的性能问题三、业务场景分析四、

Linux进程CPU绑定优化与实践过程

《Linux进程CPU绑定优化与实践过程》Linux支持进程绑定至特定CPU核心,通过sched_setaffinity系统调用和taskset工具实现,优化缓存效率与上下文切换,提升多核计算性能,适... 目录1. 多核处理器及并行计算概念1.1 多核处理器架构概述1.2 并行计算的含义及重要性1.3 并

java使用protobuf-maven-plugin的插件编译proto文件详解

《java使用protobuf-maven-plugin的插件编译proto文件详解》:本文主要介绍java使用protobuf-maven-plugin的插件编译proto文件,具有很好的参考价... 目录protobuf文件作为数据传输和存储的协议主要介绍在Java使用maven编译proto文件的插件

MyBatisPlus如何优化千万级数据的CRUD

《MyBatisPlus如何优化千万级数据的CRUD》最近负责的一个项目,数据库表量级破千万,每次执行CRUD都像走钢丝,稍有不慎就引起数据库报警,本文就结合这个项目的实战经验,聊聊MyBatisPl... 目录背景一、MyBATis Plus 简介二、千万级数据的挑战三、优化 CRUD 的关键策略1. 查

python删除xml中的w:ascii属性的步骤

《python删除xml中的w:ascii属性的步骤》使用xml.etree.ElementTree删除WordXML中w:ascii属性,需注册命名空间并定位rFonts元素,通过del操作删除属... 可以使用python的XML.etree.ElementTree模块通过以下步骤删除XML中的w:as

浏览器插件cursor实现自动注册、续杯的详细过程

《浏览器插件cursor实现自动注册、续杯的详细过程》Cursor简易注册助手脚本通过自动化邮箱填写和验证码获取流程,大大简化了Cursor的注册过程,它不仅提高了注册效率,还通过友好的用户界面和详细... 目录前言功能概述使用方法安装脚本使用流程邮箱输入页面验证码页面实战演示技术实现核心功能实现1. 随机

XML重复查询一条Sql语句的解决方法

《XML重复查询一条Sql语句的解决方法》文章分析了XML重复查询与日志失效问题,指出因DTO缺少@Data注解导致日志无法格式化、空指针风险及参数穿透,进而引发性能灾难,解决方案为在Controll... 目录一、核心问题:从SQL重复执行到日志失效二、根因剖析:DTO断裂引发的级联故障三、解决方案:修复

SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志

《SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志》在SpringBoot项目中,使用logback-spring.xml配置屏蔽特定路径的日志有两种常用方式,文中的... 目录方案一:基础配置(直接关闭目标路径日志)方案二:结合 Spring Profile 按环境屏蔽关