切换到 Gradle:为啥我必须防止自定义视图被混淆?

Posted

技术标签:

【中文标题】切换到 Gradle:为啥我必须防止自定义视图被混淆?【英文标题】:Switching to Gradle: why do I have to keep custom views from being obfuscated?切换到 Gradle:为什么我必须防止自定义视图被混淆? 【发布时间】:2015-02-12 18:18:41 【问题描述】:

我正在将一个项目从 Ant 转移到 Gradle,但有些事情我就是想不通。


事实

在构建发布版 APK(即经过混淆处理)后,我注意到该应用严重崩溃。错误可以这样总结:

java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet]

调试(即非混淆)APK 工作正常,所以我猜它与我的 ProGuard/DexGuard 配置有关。

我尝试通过添加以下语句来保留类引用:

-keep class com.mypackage.MyCustomView

因此,发布版 APK 运行良好。然后我做了一些研究,并尝试了这个更具体的 ProGuard/DexGuard 配置:

-keep public class * extends android.view.View 
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
    public void set*(...);


-keepclasseswithmembers class * 
    public <init>(android.content.Context, android.util.AttributeSet);


-keepclasseswithmembers class * 
    public <init>(android.content.Context, android.util.AttributeSet, int);

这也有效,而且它与类无关


问题

我想知道:

    为什么我在使用 Ant 时不必处理? 显示该错误的确切原因是什么? (按照第一个问题的答案)

回答

@Blundell 的回答基本正确。原来我在build.gradle 配置中遗漏了一行:

android 
  ...
  buildTypes 
    debug 
      ...
    
    release 
        proguardFile getDefaultDexGuardFile('dexguard-release.pro') # <----- this line
        proguardFile 'dexguard-project.txt'
    
  

似乎该行实际上是强制性的,因为它是 ProGuard/DexGuard 的基本规则集。其实这是dexguard-release.pro文件的一部分:

-keepclassmembers !abstract class !com.google.ads.** extends android.view.View 
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
    public void set*(...);


-keepclassmembers !abstract class * 
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);


-keepclassmembers class * extends android.content.Context 
   public void *(android.view.View);

我发现文档在这方面有点含糊不清,我希望可以对其进行编辑以消除它可能存在的任何歧义。总而言之,我的错。

【问题讨论】:

如何/在哪里创建自定义视图? 必须保留构造函数,因为LayoutInflater 正在使用它们,但这不能仅从源的静态分析中推断出来。我想你真正的问题是为什么不使用曾经是 SDK 一部分的 ProGuard/DexGuard 默认配置?我的猜测是这是因为 Gradle 插件最近发生了变化,以及 runProguard 被重命名以及其他类似的东西。如果您使用 SDK 中的 ProGuard,请咨询 adt-dev Google 小组。如果您使用的是 DexGuard,请查看下载门户,最近有一些快速连续更新。 @Barend 我不确定why is the ProGuard/DexGuard default config that used to be part of the SDK not used 应该是什么意思。我想知道为什么 Ant 和 Gradle 有不同的行为,因为两者都使用相同版本的 DexGuard 并且都使用相同的配置文件。 @pskink 只是标准的自定义视图。我已经定义了所有可能的构造函数,它们只调用 super()。 @dextor 在$ANDROID_HOME/tools/proguard 中有一些随SDK 一起提供的默认配置,并且aapt 会即时生成一些额外的配置。一个或另一个可能不存在,但我不知道这一点,我只是在猜测。如果你在 Windows 上,它可能像“程序文件”中的空间一样愚蠢。 【参考方案1】:

很可能 Ant 使用了不同的配置文件,

对于 Gradle,您还需要明确声明您还想使用 Android proguard 配置文件,即使用多个规则文件,如下所示:

    proguardFile getDefaultProguardFile('proguard-android.txt')
    proguardFile 'your/sepcific/folder/proguard.cfg'

(我记得 Ant 从来没有使用过 SDK proguard 文件,以前建议复制所有配置)。

【讨论】:

不,我(仍然)使用相同的配置文件。现在唯一的区别是保留自定义视图。另外,我仔细检查了我的 build.gradle,它正确指向了我的配置文件(否则,其他东西也会停止工作)。 我用你的答案和这种行为的原因更新了 OP。

以上是关于切换到 Gradle:为啥我必须防止自定义视图被混淆?的主要内容,如果未能解决你的问题,请参考以下文章

如何防止键盘出现在自定义警报视图上

我可以将 UITapGestureRecognizer 添加到我的控制器 self.view,但不能添加到任何自定义视图。为啥?

IOS 为啥一个视图有2个自定义类?

切换到 XCode 7.0(.1) 后,自定义视图 (XIB) 在发布模式下崩溃

设置gradle默认缓存文件路径-(笔记)

为啥自定义缩放和翻转动画后视图会在任务栏下绘制?