谷歌云盘API与ProGuard不兼容(NPE)

26

目前,我正在经历一个使用Google Drive API的代码片段,在不引入ProGuard的情况下正常运行

然而,在引入ProGuard之后,我遇到了以下的运行时错误。

    at java.lang.Thread.run(Thread.java:856)
Caused by: java.lang.NullPointerException
    at com.google.api.client.util.Types.getActualParameterAtPosition(Types.java:329)
    at com.google.api.client.util.Types.getIterableParameter(Types.java:309)
    at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:546)
    at com.google.api.client.json.JsonParser.parse(JsonParser.java:350)
    at com.google.api.client.json.JsonParser.parseValue(JsonParser.java:586)
    at com.google.api.client.json.JsonParser.parse(JsonParser.java:289)
    at com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:76)
    at com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:71)
    at com.google.api.client.http.HttpResponse.parseAs(HttpResponse.java:491)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:456)
    at com.jstock.c.b.a(CloudFile.java:136)

请注意,崩溃发生在我的代码上(如果我使用mapping.txt回溯,则是com.jstock.c.b.a)。

// request is Files.List
FileList files = request.execute();

在我的proguard中,我认为加入以下两个关键指令可以防止崩溃发生:我告诉ProGuard永远不要触及jackson和Google库。

-keep class org.codehaus.** { *; }
-keep class com.google.** { *; }
-keep interface org.codehaus.** { *; }
-keep interface com.google.** { *; }

但这并不起作用。NPE仍然发生在Types.java

请注意,我另一个尝试是,我认为混淆进程导致NPE出现。因此,我尝试使用-dontobfuscate禁用它。但这一次,我将无法生成APK文件,并获得一个常见的错误消息:转换为Dalvik格式时失败,错误代码为1

以下是导致Google Drive API中出现NPE的ProGuard配置。

-optimizationpasses 1
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

# Comment out the following line, will cause popular "Conversion to Dalvik format failed with error 1"
##-dontobfuscate

-dontwarn sun.misc.Unsafe
-dontwarn com.google.common.collect.MinMaxPriorityQueue
-dontwarn javax.swing.**
-dontwarn java.awt.**
-dontwarn org.jasypt.encryption.pbe.**
-dontwarn java.beans.**
-dontwarn org.joda.time.**
-dontwarn com.google.android.gms.**
-dontwarn org.w3c.dom.bootstrap.**
-dontwarn com.ibm.icu.text.**
-dontwarn demo.**

# Hold onto the mapping.text file, it can be used to unobfuscate stack traces in the developer console using the retrace tool
-printmapping mapping.txt

# Keep line numbers so they appear in the stack trace of the develeper console 
-keepattributes *Annotation*,EnclosingMethod,SourceFile,LineNumberTable

-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService

-keep class android.support.v4.app.** { *; }
-keep interface android.support.v4.app.** { *; }
-keep class com.actionbarsherlock.** { *; }
-keep interface com.actionbarsherlock.** { *; }

# https://sourceforge.net/p/proguard/discussion/182456/thread/e4d73acf
-keep class org.codehaus.** { *; }
-keep class com.google.** { *; }
-keep interface org.codehaus.** { *; }
-keep interface com.google.** { *; }

-assumenosideeffects class android.util.Log {
  public static int d(...);
  public static int i(...);
  public static int e(...);
  public static int v(...);  
}

-keepclasseswithmembernames class * {
    native <methods>;
}

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

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

-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}

-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
    public static *** i(...);
}

-keepclasseswithmembers class com.google.common.base.internal.Finalizer{
    <methods>;
}

还有什么其他我可以尝试的吗?

我不确定这可能是由库的组合引起的。(虽然没有引入ProGuard的情况下运行得非常好)

enter image description here

如果我查看NPE崩溃位置(Types.getActualParameterAtPosition(Types.java:329))

private static Type getActualParameterAtPosition(Type type, Class<?> superClass, int position) {
    ParameterizedType parameterizedType = Types.getSuperParameterizedType(type, superClass);
    Type valueType = parameterizedType.getActualTypeArguments()[position];
    // this is normally a type variable, except in the case where the class of iterableType is
    // superClass, e.g. Iterable<String>
    if (valueType instanceof TypeVariable<?>) {
      Type resolve = Types.resolveTypeVariable(Arrays.asList(type), (TypeVariable<?>) valueType);
      if (resolve != null) {
        return resolve;
      }
    }
    return valueType;
}

我怀疑 Types.getSuperParameterizedType 返回 null。因此,我进一步查看了 Types.getSuperParameterizedType

public static ParameterizedType getSuperParameterizedType(Type type, Class<?> superClass) {
    if (type instanceof Class<?> || type instanceof ParameterizedType) {
    outer: while (type != null && type != Object.class) {
     Class<?> rawType;
     if (type instanceof Class<?>) {
       // type is a class
       rawType = (Class<?>) type;
     } else {
       // current is a parameterized type
       ParameterizedType parameterizedType = (ParameterizedType) type;
       rawType = getRawClass(parameterizedType);
       // check if found Collection
       if (rawType == superClass) {
         // return the actual collection parameter
         return parameterizedType;
       }
       if (superClass.isInterface()) {
         for (Type interfaceType : rawType.getGenericInterfaces()) {
           // interface type is class or parameterized type
           Class<?> interfaceClass =
               interfaceType instanceof Class<?> ? (Class<?>) interfaceType : getRawClass(
                   (ParameterizedType) interfaceType);
           if (superClass.isAssignableFrom(interfaceClass)) {
             type = interfaceType;
             continue outer;
           }
         }
       }
     }
     // move on to the super class
     type = rawType.getGenericSuperclass();
    }
    }
    return null;
}

在经过ProGuard处理后,可能导致 getSuperParameterizedType 返回null 的潜在根本原因是什么?

7个回答

38

以下的组合对我非常有效:

-keep class com.google.** { *;}
-keep interface com.google.** { *;}
-dontwarn com.google.**

-dontwarn sun.misc.Unsafe
-dontwarn com.google.common.collect.MinMaxPriorityQueue
-keepattributes *Annotation*,Signature
-keep class * extends com.google.api.client.json.GenericJson {
*;
}
-keep class com.google.api.services.drive.** {
*;
}

这为最近的一个Google Drive项目提供了一个可以与Proguard兼容的工作解决方案。

虽然不能全部归功于这个解决方案,但最初在这个链接这里找到。


谢谢。我会稍后再试一下,因为现在我没有开发机器。 - Cheok Yan Cheng
感谢您承认来源并包含指向我的网站的链接 :-) - SoftWyer
在-keepattributes下,我的签名丢失了。 - Phillip
1
不要保留所有的com.google,以下代码为我解决了所有崩溃问题:-keep class com.google.api.client.** { *; } - Jared Rummler

7

我不知道谷歌是否为他们的库准备了Proguard配置。打算试一下。 - Cheok Yan Cheng

1
最近GooglePlayServices更新很少。我不喜欢新的API。我遇到了同样的问题。
我无法使用proguard编译签名应用程序。来自Google的Proguard模板对我无效。
我向我的Proguard配置添加了这四行,现在可以工作了:
-dontwarn com.google.android.gms.**
-keep interface com.google.** { *; }
-keep class * extends com.google.api.client.json.GenericJson {*;}
-keep class com.google.api.services.drive.** {*;}

这很奇怪。之前的google-api-services-drive-v2版本编译没有任何问题。

我目前正在使用最新版本:google-api-services-drive-v2-rev47-1.12.0-beta.jar


1

首先,保留一个类并不意味着不去碰它。它的意思是不改变它的名称,并将其用作确定其他类是否被引用且可以删除的基础。

优化仍然会发生,这可能是您的问题所在。下一步我会尝试使用: -dontoptimize

这应该会忽略您的其他优化。

顺便说一句,我不确定您正在使用哪个版本的SDK。我正在使用15,20是最新的,项目中创建了一个proguard-project.txt文件。它使用的优化选项是:

-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*

如果关闭优化可以使其运行,那么关闭SDK执行的所有优化(这就是!所做的),也许会让您能够进行优化。

尝试仅使用-keepattributes *Annotation*,并禁用优化。没有效果。仍然发生相同的NPE:( dropbox.com/s/av2y9how104zsq1/proguard-project.txt dropbox.com/s/7yqvl652bua3gx5/project.properties - Cheok Yan Cheng
也许可以检查bin/proguard/obfuscated.jar,以查看getSuperParameterizedType()方法后面的代码实际上是什么。您可以进行视觉检查,以与上面的源代码进行比较。虽然不是直接的答案,但根据结果可能会有用。如果需要反编译器,也许可以使用此链接 - Jeff Palmer

0

Types.getSuperParameterizedType 方法依赖于泛型信息。在Java中,泛型被擦除。编译器只将它们添加为注释属性,JVM忽略它们,而ProGuard会将它们丢弃,除非您告诉它不要这样做。因此,这可能会有所帮助:

-keepattributes *Annotation*

虽然我已经看到了-keepattributes *Annotation*,EnclosingMethod,SourceFile,LineNumberTable,但我想今晚还是尝试一下-keepattributes *Annotation*。感谢您的澄清。 - Cheok Yan Cheng
尝试仅使用-keepattributes *Annotation*并禁用优化。没有运气。仍然发生相同的NPE :( https://www.dropbox.com/s/av2y9how104zsq1/proguard-project.txt https://www.dropbox.com/s/7yqvl652bua3gx5/project.properties - Cheok Yan Cheng
你说得对,我错过了它已经在你的配置中的事实。问题必须出在别处。我需要进行调查。 - Eric Lafortune

0
这对我有用。
-keepattributes Signature,RuntimeVisibleAnnotations,AnnotationDefault
-keepclassmembers class * {
  @com.google.api.client.util.Key <fields>;
}
-dontwarn com.google.api.client.extensions.android.**
-dontwarn com.google.api.client.googleapis.extensions.android.**
-dontwarn com.google.android.gms.**
-dontnote java.nio.file.Files, java.nio.file.Path
-dontnote **.ILicensingService
-dontnote sun.misc.Unsafe
-dontwarn sun.misc.Unsafe
-keep class * extends com.google.api.client.json.** { *; }
-keep class * extends com.google.api.client.util.** { *; }
-keep class com.google.api.services.drive.** { *; }

-1
你的代码是否使用了任何实现Serializable接口的东西?所有这些都需要被排除。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接