2

I am trying to call Java method from C class file, built using NDK.

It constantly throws usual "no non-static method found" error and crashes whole Android App.

Code pieces below (some stuff might be not needed, but I left them as-is because focus/question is on refreshJNIIntegration() method, not the well-formatting of code in general).

Java class:

@Keep
@RequiresApi(api = Build.VERSION_CODES.DONUT)
public class MainActivity extends AppCompatActivity {

    public native String getHealthStatus(String stringValue, String packageName);

    @SuppressLint("SourceLockedOrientationActivity")
    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(BuildConfig.FLAVOR.endsWith("<obfusciated>")) {
            if (Build.SUPPORTED_ABIS[0].equals("armeabi-v7a") || (Build.SUPPORTED_ABIS[0].equals("arm64-v8a"))) {
                System.loadLibrary("integration");
            }
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        try {
            super.onActivityResult(requestCode, resultCode, data);
            if (requestCode == RC_SIGN_IN) {
                Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
                GoogleSignInAccount account = task.getResult(ApiException.class);
                if (account!=null) {
                    if (BuildConfig.FLAVOR.endsWith("<obfusciated>")) {
                        try {
                            if (integrationAvailable) {
                                if (mGSIA==null) mGSIA = account;
                                final String ProStatusHealth = getHealthStatus(account.getEmail(), getApplicationContext().getPackageName());
                            }
                        }
                    }
                }
            }
        }
    }

    public String refreshJNIIntegration(String version) {
        return String.valueOf(getFilesDir());
    }

}

C class (which is initially called from Java and then calls method below) in integraton.c file:

#ifdef __cplusplus
extern "C" {
#endif
    JNIEXPORT jstring JNICALL
    Java_com_linardsl_libhacontimig_MainActivity_getHealthStatus(JNIEnv *env, jobject instance, jstring stringValue_, jstring packageName_) {
        jstring apppath = Java_com_linardsl_libhacontimig_MainActivity_setJNIInterface(env, instance, "", "");
}

#ifdef __cplusplus
}
#endif

C class (where error and crash happens) in integraton.c file:

#ifdef __cplusplus
extern "C" {
#endif


JNIEXPORT jstring JNICALL
Java_com_linardsl_libhacontimig_MainActivity_setJNIInterface(JNIEnv *env, jobject instance,
                                                             jstring stringValue_, jstring packageName_) {

    jstring jstr = (*env)->NewStringUTF(env, LIB_VERS);

    // Does not work: jclass clazz = (*env)->FindClass(env,"com/linardsl/libhacontimig/MainActivity");
    jclass clazz = (*env)->GetObjectClass(env, instance);

    jmethodID messageMe = (*env)->GetMethodID(env, clazz, "refreshJNIIntegration",
                                              "(Ljava/lang/String;)Ljava/lang/String;");
    // Does not work: jmethodID messageMe = (*env)->GetMethodID(env, clazz, "refreshJNIIntegration", "(Ljava/lang/String;)V");

    jobject result = (*env)->CallObjectMethod(env, instance, messageMe, jstr);

    const char* str = (*env)->GetStringUTFChars(env, (jstring) result, NULL);

    (*env)->ReleaseStringUTFChars(env, jstr, str);

    return (*env)->NewStringUTF(env, str);
}

#ifdef __cplusplus
}
#endif

build.gradle file:

/*
 * Copyright (c) Linards Liepiņš, Riga, Latvia, 19-2022. All rights reserved.
 */

import org.apache.tools.ant.taskdefs.condition.Os

// android
apply plugin: 'com.android.application'

// junit
apply plugin: 'de.mannodermaus.android-junit5'

// google
apply plugin: 'com.google.gms.google-services'

// Apply the Crashlytics Gradle plugin
apply plugin: 'com.google.firebase.crashlytics'

// Apply the Performance Monitoring plugin
apply plugin: 'com.google.firebase.firebase-perf'

// huawei
apply plugin: 'com.huawei.agconnect'

android {
    signingConfigs {
        release {
            v2SigningEnabled true
            if (Os.isFamily(Os.FAMILY_WINDOWS)) {
                storeFile file(System.getProperty("user.home")+"\\keystore.jks")
                storePassword '<obfusciated>'
                keyPassword '<obfusciated>'
                keyAlias = '<obfusciated>'
            } else if (Os.isFamily(Os.FAMILY_UNIX)) {
                printf("Signing not supported in *Nix (Linux) OS environment. Yet. ")
            } else if (Os.isFamily(Os.FAMILY_MAC)) {
                printf("Signing not supported in Mac OS environment. Yet. ")
            }
        }
    }
    compileSdkVersion 33
    buildToolsVersion "33.0.0"
    ndkVersion '25.1.8937393'
    defaultConfig {
        applicationId "com.linardsl.libhacontimig"
        minSdkVersion 26
        targetSdkVersion 33
        versionCode 40
        versionName "0.3.1"
        multiDexEnabled true
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        if (Os.isFamily(Os.FAMILY_WINDOWS)) {
            signingConfig signingConfigs.release
        }
        ndk {
            // Source(s): https://stackoverflow.com/questions/63373245/how-to-add-debug-symbols-to-build-gradle
            debugSymbolLevel 'FULL'
            abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
        }
        resConfigs 'en', 'fr', 'ru', 'lv', 'in', 'es'
    }

    flavorDimensions "version"
    productFlavors {
        alpha {
            dimension "version"
            applicationIdSuffix ".alpha"
            versionNameSuffix "-alpha"
            buildConfigField "boolean", "IS_BLEEDING", "true"
            resValue 'string', 'app_name', "<obfusciated>"
        }
        alphaPro {
            dimension "version"
            applicationIdSuffix ".alphaPro"
            versionNameSuffix "-alphaPro"
            buildConfigField "boolean", "IS_BLEEDING", "true"
            resValue 'string', 'app_name', "<obfusciated>"
        }
        beta {
            dimension "version"
            applicationIdSuffix ".beta"
            versionNameSuffix "-beta"
            buildConfigField "boolean", "IS_BLEEDING", "true"
            resValue 'string', 'app_name', "<obfusciated>"
        }
        betaPro {
            dimension "version"
            applicationIdSuffix ".betaPro"
            versionNameSuffix "-betaPro"
            buildConfigField "boolean", "IS_BLEEDING", "true"
            resValue 'string', 'app_name', "<obfusciated>"
        }
        demo {
            dimension "version"
            applicationIdSuffix ".demo"
            versionNameSuffix "-demo"
            buildConfigField "boolean", "IS_BLEEDING", "false"
            resValue 'string', 'app_name', "<obfusciated>"
        }
        production {
            dimension "version"
            applicationIdSuffix ".final"
            versionNameSuffix "-final"
            buildConfigField "boolean", "IS_BLEEDING", "false"
            resValue 'string', 'app_name', "<obfusciated>"
            if (Os.isFamily(Os.FAMILY_WINDOWS)) {
                signingConfig signingConfigs.release
            }
        }
    }

    externalNativeBuild {
        ndkBuild {
            path "src/main/jni/Android.mk"
        }
    }

    buildTypes {
        debug {
            minifyEnabled true
            shrinkResources true
            testCoverageEnabled = true
            jniDebuggable true

        }
        release {
            minifyEnabled true
            testCoverageEnabled = false
            debuggable false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            if (Os.isFamily(Os.FAMILY_WINDOWS)) {
                signingConfig signingConfigs.release
            }
            zipAlignEnabled true

            firebaseCrashlytics {
                nativeSymbolUploadEnabled true
            }
        }
    }

    // Fix for > com.android.builder.errors.EvalIssueException: The option 'android.enablePrefab' is deprecated. It was removed in version 4.1 of the Android Gradle plugin.
    // Fix for > https://stackoverflow.com/questions/45279479/error-could-not-parse-the-android-application-modules-gradle-config
    buildFeatures {
        prefab true
        viewBinding true
    }

    // Fix for https://stackoverflow.com/questions/67672583/jni-detected-error-in-application-java-lang-unsatisfiedlinkerror-couldnt-find
    packagingOptions {
        jniLibs {
            useLegacyPackaging = true
        }
    }

    // Fix for https://stackoverflow.com/questions/49891730/invoke-customs-are-only-supported-starting-with-android-0-min-api-26
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    testOptions {
        reportDir = "$project.buildDir/reports"
        resultsDir = "$project.buildDir/test-results/junit"
    }

    dependenciesInfo {
        includeInApk true
        includeInBundle true
    }

    lint {
        abortOnError false
        checkDependencies true
        checkGeneratedSources true
        htmlOutput = file("$project.buildDir/reports/lint/LINT-results.html")
        xmlOutput = file("$project.buildDir/test-results/lint/LINT-results.xml")
    }
    namespace 'com.linardsl.libhacontimig'
}

ext {
    // System variables
    supportLibraryVersion = '28.0.0'
    clouddashProductVersion = '<obfusciated>'
    clouddashLibraryVersion = '<obfusciated>'
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "com.android.support:appcompat-v7:${supportLibraryVersion}"
    implementation "com.android.support:design:${supportLibraryVersion}"
    implementation 'com.android.support:multidex:1.0.3'
    implementation "androidx.core:core:1.7.0" // core:1.7.0+ => 'compileSdkVersion' => SDK v31+
    implementation 'androidx.appcompat:appcompat:1.3.1' // appcompat:appcompat:1.3.1+ => 'compileSdkVersion' => SDK v31+
    // core:1.4.0+ => 'compileSdkVersion' => SDK v31+
    implementation 'com.google.android.material:material:1.6.1'
    // material:1.5.0+ => 'compileSdkVersion' => SDK v31+
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    implementation 'androidx.preference:preference:1.2.0' // preference:1.2.0+ => 'compileSdkVersion' => SDK v31+
    implementation 'com.google.android.play:core:1.10.3'
    implementation 'com.google.android.gms:play-services-auth:20.3.0' //19.2.0
    implementation 'com.google.android.gms:play-services-fitness:21.1.0' //20.0.0
    implementation 'com.google.android.gms:play-services-ads:21.2.0' //21.0.0 20.3.0'
    implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1' //17.1.0 17.0.1'
    implementation 'com.google.android.gms:play-services-basement:18.1.0' //18.0.2 17.6.0
    implementation 'com.google.ads.mediation:facebook:6.11.0.1'
    // Declare the dependencies for the Crashlytics and Analytics libraries
    // When using the BoM, you don't specify versions in Firebase library dependencies
    implementation platform('com.google.firebase:firebase-bom:30.5.0') //:30.1.0 :29.2.1 firebase-bom:29.0.2+ => 'compileSdkVersion' => SDK v31+
    implementation 'com.google.firebase:firebase-analytics'//:18.0.2'
    implementation 'com.google.firebase:firebase-crashlytics'
    implementation 'com.google.firebase:firebase-crashlytics-ndk'
    implementation 'com.google.firebase:firebase-auth'//:21.0.1'//:17.4.0'
    implementation 'com.google.firebase:firebase-messaging'//:23.0.0'
    implementation 'com.google.firebase:firebase-perf'
    implementation 'androidx.viewpager2:viewpager2:1.0.0'
    testImplementation 'junit:junit:4.13.2'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.1' // 5.8.2
    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.1' // 5.8.2
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.1' // 5.8.2
    // Optional -- Robolectric environment
    testImplementation 'androidx.test:core:1.4.0'
    // Optional -- Mockito framework
    testImplementation 'org.mockito:mockito-core:4.8.0'
    androidTestImplementation 'androidx.test:rules:1.4.0'
    androidTestImplementation 'androidx.test:runner:1.4.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    androidTestImplementation 'org.testng:testng:7.6.1'
    // HTTP classes
    // -- optional to use httpurlconnection-executor, any other HttpRequestExecutor implementation will do
    implementation 'com.jakewharton.threetenabp:threetenabp:1.4.1'
    // OAuth2-essentials
    implementation 'org.dmfs:oauth2-essentials:0.18'
    implementation 'org.dmfs:httpurlconnection-executor:1.16.2'
    implementation 'androidx.browser:browser:1.4.0'
    // browser:1.4.0+ => 'compileSdkVersion' => SDK v31+
    // ndk for curl
    implementation 'com.android.ndk.thirdparty:openssl:1.1.1l-beta-1'
    implementation 'com.android.ndk.thirdparty:curl:7.79.1-beta-1'
    // huawei
    //implementation 'com.huawei.hms:hihealth-base:4.0.4.300'
    implementation 'com.huawei.hihealth:hihealthkit:5.1.0.300' // (https://github.com/HMS-Core/hms-health-extention-demo/blob/master/HiHealthKitDemo/app/build.gradle)
    implementation 'com.google.code.gson:gson:2.9.1'
    implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.10'
    implementation 'com.huawei.hms:base:6.7.0.300'
    implementation 'com.huawei.hms:hwid:6.7.0.300'
    implementation 'com.huawei.hms:game:6.7.0.300' //6.5.0.300 //5.0.4.303
    implementation 'com.huawei.hms:health:6.7.0.300' //6.4.0.301 //6.3.0.301 //6.0.0.300
    //implementation files('libs/hihealthkit-2.0.1.300.jar')
    // version-compare
    implementation("io.github.g00fy2:versioncompare:1.5.0")
    // whats new
    implementation 'io.github.tonnyl:whatsnew:0.1.7'
    // badger
    implementation 'me.leolin:ShortcutBadger:1.1.22'
    // ironsource offerwall
    implementation 'com.ironsource.sdk:mediationsdk:7.2.4.1'
    // aws lex
    implementation 'com.amazonaws:aws-android-sdk-lex:2.53.0'
    // google health connect
    implementation 'androidx.health.connect:connect-client:1.0.0-alpha05'
}

ProGuard .pro file:

# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

-keep public class com.linardsl.libhacontimig.MainActivity{
    *;
}

# Uncomment this to preserve the line number information for
# debugging stack traces.
-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

-printmapping mapping.txt

Android makefile:

# Build static module. See https://developer.android.com/ndk/guides/cmake
# Android Studio 4.0+ tutorial: https://stackoverflow.com/questions/44297396/compile-native-library-that-relies-on-other-native-libraries-to-run-using-androi
# https://android-developers.googleblog.com/2020/02/native-dependencies-in-android-studio-40.html

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_LDLIBS := -llog -landroid -lz
LOCAL_MODULE    := integration
LOCAL_SRC_FILES := integration.c
LOCAL_CPP_FEATURES := rtti exceptions
LOCAL_MODULE_TAGS := optional
LOCAL_SHARED_LIBRARIES := curl
include $(BUILD_SHARED_LIBRARY)

$(call import-module, prefab/curl)

Application makefile:

# Build all API/ABI compatible binaries

APP_ABI := all
APP_SHORT_COMMANDS := true

# Include STL port lib

APP_STL := c++_shared
APP_CPPFLAGS := -std=c++17
#APP_CFLAGS := -Wall -Werror

# Build error workarounds
APP_ALLOW_MISSING_DEPS := true

How to fix this crash and get Java method callable and working? What am I doing wrong?


UPDATE: Adding initial java-to-native method, build.gradle (module level) and android.mk file snipps as requested.


UPDATE: Bugs created in GitHub and google issue tracker:

Fraser
  • 15,275
  • 8
  • 53
  • 104
HX_unbanned
  • 583
  • 1
  • 15
  • 51
  • 2
    It looks and sounds like the Java program may not have actually loaded the native library. This is conventionally performed via a static initializer in the host class. Refer to [the JNI docs](https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#compiling_loading_and_linking_native_methods) for more detail. – John Bollinger Oct 05 '22 at 14:33
  • Thanks @JohnBollinger for hint, but I think this is not the case; I am using loader at onCreate(). I have forgotten about this code piece thus I just updated MainActivity snipp to reflect this. I have selected x64 from Build Variants, if that changes something. – HX_unbanned Oct 05 '22 at 14:44
  • 1
    Observations: (1) your `onCreate()` only *conditionally* loads the native library. Are you sure the conditions for it to be loaded are satisfied during your test runs? (2) You say you want to focus on the `refreshJNIIntegration()` method, but that is not a native method, and it is not clear how it is related to the native method you present. (3) the Java-side declaration of your native method has not been presented, and a mismatch there could conceivably be related to the issue. – John Bollinger Oct 05 '22 at 14:54
  • Yes, I am sure, because I am getting crashes the second C methods are triggered via JNI. The idea is simple - android app calls C method within "integration" library, within loaded "integration" library C methods calls presented C method, which calls back Java method. I assume the declaration of C method being called initially from Java-side is not relevant here as crash happens inside ```Java_com_linardsl_libhacontimig_MainActivity_setJNIInterface``` . Or is it? – HX_unbanned Oct 05 '22 at 15:01
  • I'm sorry, but this is far, far too complicated, and not nearly enough of what is going has been presented in the question for us to sort it out. This is where I remind you that at SO, our expectation is that debugging questions will provide a [mre] that demonstrates the problem. – John Bollinger Oct 05 '22 at 15:07
  • I see. Would the declaration of initial C method suffice the requirement(s)? – HX_unbanned Oct 05 '22 at 15:14
  • 1
    I can say, however, that the runtime error you describe must be originating from a dynamic loading problem. Not having loaded the (correct) shared library is a particularly likely source for such a problem, but if the needed library has in fact been loaded then the problem is presumably that its contents are in some way not as expected. And it probably arises in conjunction with the invocation of a native method from (or at least *via*) Java, not a native-to-native function call. – John Bollinger Oct 05 '22 at 15:15
  • 2
    No, the declaration of the initial native method would not suffice. If you are unclear about the meaning of "minimal reproducible example" then the link I provided describes the concept in detail. – John Bollinger Oct 05 '22 at 15:16
  • 1
    Can you provide your `build.gradle` and `CMakeLists.txt` ? – 김선달 Oct 06 '22 at 04:47
  • I added all needful, @김선달 ;) – HX_unbanned Oct 06 '22 at 07:46
  • https://stackoverflowteams.com/c/healthdash/questions/15 – HX_unbanned Apr 13 '23 at 08:26

0 Answers0