crash with NoSuchMethodError after proguard with method references

后端 未结 4 1106
难免孤独
难免孤独 2021-01-18 01:21

the source code before compile&proguard :

public class IntentSession extends BaseIntentSession {
    @Override
    public void onResume() {
        super         


        
相关标签:
4条回答
  • 2021-01-18 01:41

    Most of the time, the website/github of your library provides the necessary proguard rules like retrolamda:

    -dontwarn java.lang.invoke.*
    -dontwarn **$$Lambda$*
    

    Proguarding is a trail-error story. Check your logging to see which library, class or component causes a problem and add them carefully to the rules :).

    Your error NoSuchMethod specifically:

    Your code is probably calling something like myClass.getMethod, trying to find some method dynamically. Since ProGuard can't always detect this automatically, you have to keep the missing method in using the appropriate -keep option:

    -keepclassmembers class mypackage.MyClass { void myMethod(); }

    0 讨论(0)
  • 2021-01-18 01:54

    after re-check carefully, i concluded it probably not a bug of proguard, only gradle.

    first, i let the source code using general interface coding style :

    mExecutor.exec(getIntent(), new MyInterface() {
        @Override
        public void execute() {
            finish();
        }
    });
    

    then i clean the build cache and rebuild :

    ./gradlew clean
    ./gradlew :app:assembleRelease
    

    I perform the output release app and make it reach the problematic code, it work without crash.

    this time i turn to method references :

    mExecutor.exec(getIntent(), this::finish);
    

    but i didn't clean the build cache before re-building :

    ./gradlew :app:assembleRelease
    

    now re-perform with the crash happen :

    05-22 11:35:33.870 D/AndroidRuntime(  631): Shutting down VM
    05-22 11:35:37.470 E/AndroidRuntime(  631): FATAL EXCEPTION: main
    05-22 11:35:37.470 E/AndroidRuntime(  631): Process: com.cmrobot.assistant, PID: 631
    05-22 11:35:37.470 E/AndroidRuntime(  631): java.lang.NoSuchMethodError: com.session.a.finish
    05-22 11:35:37.470 E/AndroidRuntime(  631):     at com.newsessions.b.executeDone(Unknown Source)
    05-22 11:35:37.470 E/AndroidRuntime(  631):     at com.newsessions.base.a.a(BaseIntentExecutor.java:99)
    05-22 11:35:37.470 E/AndroidRuntime(  631):     at com.newsessions.base.a.e(BaseIntentExecutor.java:76)
    05-22 11:35:37.470 E/AndroidRuntime(  631):     at com.newsessions.base.a.a(BaseIntentExecutor.java:67)
    05-22 11:35:37.470 E/AndroidRuntime(  631):     at com.newsessions.cmd.general.volume.VolumeChangeExecutor.b(VolumeChangeExecutor.java:28)
    05-22 11:35:37.470 E/AndroidRuntime(  631):     at com.newsessions.cmd.general.volume.a.a(LowerVolumeExecutor.java:63)
    05-22 11:35:37.470 E/AndroidRuntime(  631):     at com.newsessions.base.a.d(BaseIntentExecutor.java:44)
    05-22 11:35:37.470 E/AndroidRuntime(  631):     at com.newsessions.base.b.run(Unknown Source)
    05-22 11:35:37.470 E/AndroidRuntime(  631):     at android.os.Handler.handleCallback(Handler.java:733)
    05-22 11:35:37.470 E/AndroidRuntime(  631):     at android.os.Handler.dispatchMessage(Handler.java:95)
    05-22 11:35:37.470 E/AndroidRuntime(  631):     at android.os.Looper.loop(Looper.java:136)
    05-22 11:35:37.470 E/AndroidRuntime(  631):     at android.app.ActivityThread.main(ActivityThread.java:5001)
    05-22 11:35:37.470 E/AndroidRuntime(  631):     at java.lang.reflect.Method.invokeNative(Native Method)
    05-22 11:35:37.470 E/AndroidRuntime(  631):     at java.lang.reflect.Method.invoke(Method.java:515)
    05-22 11:35:37.470 E/AndroidRuntime(  631):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:829)
    05-22 11:35:37.470 E/AndroidRuntime(  631):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:645)
    05-22 11:35:37.470 E/AndroidRuntime(  631):     at dalvik.system.NativeStart.main(Native Method)
    

    in order to confirm it is the build cache reason, i clean then re-build on the changed code basically :

    ./gradlew clean
    ./gradlew :app:assembleRelease
    

    that crash gone in the afterword app.

    i attempt to create a demonstrating project to prove this problem, but that project doesn't popup the crash, only in my productive project.

    0 讨论(0)
  • 2021-01-18 01:55

    Working through these bugs that -keep doesn't fix is a REAL pain and the only way I've ever made headway on them is by following this strategy:

    1. Figure out at what phase of the proguard cycle the bug is being introduced (shrinking, optimizing or obfuscating)
    2. Add/remove exceptions for that step, working your way from the broadest scope of exclusion to the narrowest until the problem resurfaces

    E.G.

    Verify that this is an optimization issue or not

    1. Add -dontoptimize instead of the -optimizations string rebuild and test
    2. If the crash is mitigated, work backwards through the classes of optimization exclusions at the highest level !method/*, !code/*, !class/*, !field/* until you determine which exclusion makes the issue go away
    3. Determine what the minimum exclusion is in that class of exclusion (assuming it was !method/*, go from it to !method/marking/* and then if that works too try !method/marking/final. If that works, then you've found the minimum exclusion)

    This could very well be a bug in the Proguard rules of one of the libs you're using or in the version of Proguard you're using itself (I've seen both) so try to update both as well.

    0 讨论(0)
  • 2021-01-18 01:55

    This happens when a class is looking for, or directly invoking a method of a given argument via reflection in runtime. Proguard can't warn you about this, because there is no link between the obfuscated class and the consumer class in compile time. You can have something like

    public class AbstractbaseSomething{
    
        public abstract void doStuff();
    
    }
    
    public class iDoStuff{
    
        public void letsGo(Object o){
            Method method = o.getClass().getDeclaredMethod("doStuff");
            // do stuff with the method
        }
    
    }
    

    Since the method is referenced used a string with the name, proguard does not detect it, and in runtime, you get a crash. the only solution is, assuming you can't modify the code, is to avoid obfuscating the method and class.

    (You can check a more realistic example in Ormlite-Android)

    0 讨论(0)
提交回复
热议问题