Skip to content

Static fields or methods does not exist in release build #1626

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
josxha opened this issue Oct 2, 2024 · 6 comments
Closed

Static fields or methods does not exist in release build #1626

josxha opened this issue Oct 2, 2024 · 6 comments

Comments

@josxha
Copy link

josxha commented Oct 2, 2024

Description

I'd like to use jnigen for a flutter plugin in combination with a platform view. To pass the kotlin instance to dart I created a kotlin object class MapLibreMapRegistry. After I run jnigen I can access the singelton instance via MapLibreMapRegistry.INSTANCE. This works find when I run the app in debug mode.
However, when I build and run the app in release mode with flutter build apk jni can't find the static INSTANCE field.

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Exception in Java code called through JNI: java.lang.NoSuchFieldError: no "Lcom/github/josxha/maplibre/MapLibreMapRegistry;" field "INSTANCE" in class "Lcom/github/josxha/maplibre/MapLibreMapRegistry;" or its superclasses
java.lang.NoSuchFieldError: no "Lcom/github/josxha/maplibre/MapLibreMapRegistry;" field "INSTANCE" in class "Lcom/github/josxha/maplibre/MapLibreMapRegistry;" or its superclasses

I added some (currently a lot) proguard keep rules and added @Keep annotations to make sure that proguard is no problem.

rules in proguard-rules.pro (click to expand)
-keep class com.github.josxha.maplibre.MapLibreMapRegistry { *; }
-keepclassmembers class com.github.josxha.maplibre.MapLibreMapRegistry { *; }
-keepnames class com.github.josxha.maplibre.MapLibreMapRegistry { *; }
-keepclassmembernames class com.github.josxha.maplibre.MapLibreMapRegistry { *; }

-keep class com.github.josxha.maplibre.MapLibreMapRegistry { *; }
-keep class com.github.josxha.maplibre.MapLibreMapRegistry { public *; }
-keep class com.github.josxha.maplibre.MapLibreMapRegistry {
    @androidx.annotation.Keep *;
}
-keep class com.github.josxha.maplibre.MapLibreMapRegistry {
    public static ** get(int);
}
-keep class com.github.josxha.maplibre.MapLibreMapRegistry {
    public ** get(int);
}

When I analyze the APK, all fields and methods exist.
image

MapLibreMapRegistry byte code (click to expand)
.class public final Lcom/github/josxha/maplibre/MapLibreMapRegistry;
.super Ljava/lang/Object;
.source "SourceFile"


# static fields
.field public static final a:Lcom/github/josxha/maplibre/MapLibreMapRegistry;

.field private static final b:Ljava/util/HashMap;


# direct methods
.method static constructor <clinit>()V
    .registers 1

    .line 1
    new-instance v0, Lcom/github/josxha/maplibre/MapLibreMapRegistry;

    .line 2
    .line 3
    invoke-direct {v0}, Lcom/github/josxha/maplibre/MapLibreMapRegistry;-><init>()V

    .line 4
    .line 5
    .line 6
    sput-object v0, Lcom/github/josxha/maplibre/MapLibreMapRegistry;->a:Lcom/github/josxha/maplibre/MapLibreMapRegistry;

    .line 7
    .line 8
    new-instance v0, Ljava/util/HashMap;

    .line 9
    .line 10
    invoke-direct {v0}, Ljava/util/HashMap;-><init>()V

    .line 11
    .line 12
    .line 13
    sput-object v0, Lcom/github/josxha/maplibre/MapLibreMapRegistry;->b:Ljava/util/HashMap;

    .line 14
    .line 15
    return-void
.end method

.method private constructor <init>()V
    .registers 1

    .line 1
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    .line 2
    .line 3
    .line 4
    return-void
.end method


# virtual methods
.method public final a(ILorg/maplibre/android/maps/n;)V
    .registers 4

    .line 1
    const-string v0, "map"

    .line 2
    .line 3
    invoke-static {p2, v0}, Lh0/l;->f(Ljava/lang/Object;Ljava/lang/String;)V

    .line 4
    .line 5
    .line 6
    invoke-static {p1}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;

    .line 7
    .line 8
    .line 9
    move-result-object p1

    .line 10
    sget-object v0, Lcom/github/josxha/maplibre/MapLibreMapRegistry;->b:Ljava/util/HashMap;

    .line 11
    .line 12
    invoke-interface {v0, p1, p2}, Ljava/util/Map;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

    .line 13
    .line 14
    .line 15
    return-void
.end method

.method public final get(I)Lorg/maplibre/android/maps/n;
    .registers 3
    .annotation build Lb/a;
    .end annotation

    .line 1
    sget-object v0, Lcom/github/josxha/maplibre/MapLibreMapRegistry;->b:Ljava/util/HashMap;

    .line 2
    .line 3
    invoke-static {p1}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;

    .line 4
    .line 5
    .line 6
    move-result-object p1

    .line 7
    invoke-virtual {v0, p1}, Ljava/util/HashMap;->get(Ljava/lang/Object;)Ljava/lang/Object;

    .line 8
    .line 9
    .line 10
    move-result-object p1

    .line 11
    check-cast p1, Lorg/maplibre/android/maps/n;

    .line 12
    .line 13
    return-object p1
.end method

I added another java class MapLibreMapRegistryJava to see if the problem is related to kotlin but the static methods can't be found either.

MapLibreMapRegistryJava content (click to expand)
package com.github.josxha.maplibre;

import androidx.annotation.Keep;

import org.maplibre.android.maps.MapLibreMap;

import java.util.HashMap;

@Keep
public class MapLibreMapRegistryJava {
    private static final HashMap<Integer, MapLibreMap> registry = new HashMap<>();

    @Keep
    public static MapLibreMap get(int viewId) {
        return registry.get(viewId);
    }

    public static void add(int viewId,  MapLibreMap map) {
        registry.put(viewId, map);
    }
}

I'm currently a bit lost how to debug this problem further, so I'd be glad for any help or hint.

The project is published on GitHub:

@HosseinYousefi
Copy link
Member

Thanks for making the code available, I will try to reproduce it tomorrow.

@josxha
Copy link
Author

josxha commented Oct 3, 2024

Thanks a lot! If it helps I can create a minimal reproducible example project, too. Just let me know. (:

@HosseinYousefi
Copy link
Member

Thanks a lot! If it helps I can create a minimal reproducible example project, too. Just let me know. (:

I would appreciate a more minimal example of course if you can!

@josxha
Copy link
Author

josxha commented Oct 4, 2024

I was finally able to reproduce the bug. It turns out, it was a misconfigured proguard-rules file all along. 🥲 The only place the proguard-rules.pro file gets used is for a flutter plugin /example/android/app/proguard-rules.pro.
A wildcard rule like -keep class com.github.josxha.maplibre.** { *; } is completely sufficient.

The error message in this case was misleading. The function itself existed but the returned type is renamed or doesn't exist? I'm not really sure. For example the method signature in the screenshot of my initial post: org.maplibre.android.maps.n get(int) instead of org.maplibre.android.maps.MapLibreMap get(int). I assume this change of the method signature made it so that it couldn't get found any more.

Side note: After adding the proguard-rules.pro file in the correct location flutter build apk fails and instructed me to copy over some more rules to the proguard-rules.pro file.

Unfortunally, I couldn't find a way to inject proguard rules from the flutter plugin, only from the flutter app. See flutter/flutter#136909.

Thanks a lot for your help @HosseinYousefi. Closing this issue now. (:

@josxha josxha closed this as completed Oct 4, 2024
@github-project-automation github-project-automation bot moved this from Todo to Done in JNIgen tracker Oct 4, 2024
@HosseinYousefi
Copy link
Member

I'm glad you were able to solve it.

Unfortunally, I couldn't find a way to inject proguard rules from the flutter plugin, only from the flutter app.

You can use consumer proguard rules in this case, I use them for package:jni:

@josxha
Copy link
Author

josxha commented Oct 4, 2024

That's exactly what I was looking for! Thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

No branches or pull requests

2 participants