Problem with ProGuard and RoboGuice with @Inject annotations
Asked Answered
N

2

5

I'm having some problems with proguard when optimizing my android app. It seems that there is something done to an annotation class (@com.google.inject.Inject) which Dalvik/Harmony is not happy with at runtime.

java.lang.annotation.IncompleteAnnotationException: The element optional is not complete for the annotation com.google.inject.Inject

com.google.inject.Inject looks like this (Part of Guice):

@Target(value={ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD})
@Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)
@Documented
public abstract @interface com.google.inject.Inject extends Annotation {
  public abstract boolean optional() default false;
}

Here's the failure on launch:

04-07 09:48:00.864: ERROR/AndroidRuntime(9384): FATAL EXCEPTION: main
04-07 09:48:00.864: ERROR/AndroidRuntime(9384): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.bitgrind.wtb/com.bitgrind.wtb.activity.Main}: com.google.inject.b.q: com.google.inject.b.q: java.lang.annotation.IncompleteAnnotationException: The element optional is not complete for the annotation com.google.inject.Inject
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1622)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1638)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at android.app.ActivityThread.access$1500(ActivityThread.java:117)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:928)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at android.os.Handler.dispatchMessage(Handler.java:99)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at android.os.Looper.loop(Looper.java:123)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at android.app.ActivityThread.main(ActivityThread.java:3647)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at java.lang.reflect.Method.invokeNative(Native Method)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at java.lang.reflect.Method.invoke(Method.java:507)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at dalvik.system.NativeStart.main(Native Method)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384): Caused by: com.google.inject.b.q: com.google.inject.b.q: java.lang.annotation.IncompleteAnnotationException: The element optional is not complete for the annotation com.google.inject.Inject
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.b.do.a(MapMaker.java:553)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.b.do.a(MapMaker.java:419)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.b.w.get(CustomConcurrentHashMap.java:2041)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.b.av.b(FailableCache.java:46)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.q.a(ConstructorInjectorStore.java:52)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.n.a(ConstructorBindingImpl.java:57)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.aq.a(InjectorImpl.java:375)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.g.run(BindingProcessor.java:169)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.e.a(BindingProcessor.java:224)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.an.b(InjectorBuilder.java:120)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.an.a(InjectorBuilder.java:105)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.ab.a(Guice.java:92)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.ab.a(Guice.java:69)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.bitgrind.wtb.WTBApp.a(WTBApp.java:59)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at roboguice.application.RoboApplication.a_(RoboApplication.java:84)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at roboguice.activity.RoboActivity.a_(RoboActivity.java:192)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at roboguice.activity.RoboActivity.onCreate(RoboActivity.java:70)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.bitgrind.wtb.activity.Main.onCreate(Main.java:41)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1586)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     ... 11 more
04-07 09:48:00.864: ERROR/AndroidRuntime(9384): Caused by: com.google.inject.b.q: java.lang.annotation.IncompleteAnnotationException: The element optional is not complete for the annotation com.google.inject.Inject
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.b.do.a(MapMaker.java:553)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.b.do.a(MapMaker.java:419)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.b.w.get(CustomConcurrentHashMap.java:2041)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.b.av.b(FailableCache.java:46)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.br.a(MembersInjectorStore.java:66)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.q.b(ConstructorInjectorStore.java:69)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.q.a(ConstructorInjectorStore.java:31)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.r.a(ConstructorInjectorStore.java:35)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.b.aw.a(FailableCache.java:35)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.b.do.a(MapMaker.java:549)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     ... 30 more
04-07 09:48:00.864: ERROR/AndroidRuntime(9384): Caused by: java.lang.annotation.IncompleteAnnotationException: The element optional is not complete for the annotation com.google.inject.Inject
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at org.apache.harmony.lang.annotation.AnnotationFactory.invoke(AnnotationFactory.java:312)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at $Proxy1.optional(Native Method)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.e.n.<init>(InjectionPoint.java:85)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.e.p.a(InjectionPoint.java:384)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.e.n.a(InjectionPoint.java:353)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.e.n.b(InjectionPoint.java:295)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.br.b(MembersInjectorStore.java:78)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.br.a(MembersInjectorStore.java:35)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.bs.a(MembersInjectorStore.java:40)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.b.aw.a(FailableCache.java:35)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     at com.google.inject.b.do.a(MapMaker.java:549)
04-07 09:48:00.864: ERROR/AndroidRuntime(9384):     ... 39 more

And this is my (current) proguard.cfg: (I've tried many things)

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

-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

# Needed for RoboGuice, etc
-keepattributes SourceFile,LineNumberTable,RuntimeVisibleAnnotations,RuntimeVisibleParameterAnnotations,RuntimeVisibleFieldAnnotations
-keep public class com.google.inject.Inject
-keep,allowobfuscation public class com.google.inject.name.Named
-keep,allowobfuscation public class * implements com.google.inject.Provider
-keep,allowobfuscation @com.google.inject.Provides class *
-keep,allowobfuscation @com.google.inject.Provides class *
-keep,allowobfuscation @com.google.inject.ProvidedBy class * 
-keep,allowobfuscation @com.google.inject.Singleton class *
-keep,allowobfuscation @com.google.inject.BindingAnnotation class *
-keep,allowobfuscation @com.google.inject.ScopeAnnotation class *

-keep class com.google.inject.Binder

#-keep public class roboguice.**

-keepclassmembers class * {
    @com.google.inject.Inject <init>(...);
}

-keepclassmembers class com.google.inject.Inject {
    public boolean optional();
}

# Annotations
#-keepclasseswithmembernames class * {
#    public ** value();
#}

-keepclassmembers class * implements java.lang.annotation.Annotation { 
    ** *(); 
}

-keepclasseswithmembernames class * {
    native <methods>;
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
    public void set*(...);
}

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

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

# Guava primitives lexicographicalComparator() references sun.misc.Unsafe dynamically 
# which is obviously not provided in the Android Runtime
-dontwarn sun.misc.Unsafe

# Slf4j api is in libs for the server side stuff, not used in the app
-dontwarn org.slf4j.*

# Other dynamically referenced methods in Guava
-keepclassmembers class com.google.guava.* {
    void finalizeReferent();
    void startFinalizer(java.lang.Class,java.lang.Object);
}

# newBuilder() is referenced dynamically in generated ProtoBuf code
-keepclassmembers class * {
    ** newBuilder();
}
Newkirk answered 7/4, 2011 at 14:3 Comment(0)
O
8

Doesn't look like you are keeping annotations. They will be totally stripped out since they have no effect on the execution of code, And that's real bad since the only way to retrieve then is with reflection.

Try adding

-keepattributes *Annotation*
Overrefinement answered 7/4, 2011 at 14:9 Comment(2)
Mark is explicitly keeping the various runtime annotations, but not the AnnotationDefault attribute. This might be causing the exception. Adding it or using the wildcards may indeed solve the problem.Predestine
I found that it is even safer to use -keepattributes *Priscian
P
3

To get RoboGuice 2 working for me, I had to add the following to what I already had:

-keep class com.google.inject.** { *; } 
-keep class javax.inject.** { *; } 
-keep class javax.annotation.** { *; } 
-keep class roboguice.** { *; } 

And run it with the non-optimization version from the SDK. My project.properties follows. Note that it is proguard-android.txt not proguard-android-optimization.txt, followed by a colon character and my custom proguard.cfg which is in my project root directory.

/project.properties:

# ProGuard configuration
proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard.cfg

My final, which is probably safer than I need:

/proguard.cfg:

-keepattributes Signature
-keepattributes *Annotation*
-keep class roboguice.**

# if not using Google Maps library, safe to ignore these
-dontwarn roboguice.activity.RoboMapActivity
# safe to ignore testing classes, when proguard not being run against an instrumentation testing app
-dontwarn roboguice.test.**

-keep class com.google.inject.** { *; } 
-keepclassmembers class * {
    @com.google.inject.Inject <fields>;
    @com.google.inject.Inject <init>(...);
}
-keep class javax.inject.** { *; } 
-keep class javax.annotation.** { *; } 

# My application classes used by injection framework
-keep class com.myapp.RoboGuiceModule { *; }
-keep class com.myapp.AppProvider { *; }
-keep class com.myapp.MyInjectableSingletonExample { *; }
# ... other classes that are referenced in my custom RoboGuiceModule.configure() bindings
Philippe answered 29/3, 2013 at 20:41 Comment(3)
This almost make my code work!! After add ' -dontwarn javax.inject.Singleton ' it is realy work for me! Thx to @Jon AdamsWhitaker
@acntwww: I never got @Singleton to work reliably for me, so I didn't need that option in my ProGuard. You wouldn't happen to want to write up in the docs or on a blog how you got @Singleton working?Philippe
I'm really sorry for not making my words clear. I meant there is a warn related to javax.inject.Singleton, so I JUST dontwarn this. Another thing is that: I'm new to roboguice.Whitaker

© 2022 - 2024 — McMap. All rights reserved.