Home > database >  Why jni calls fail on modification of composable remembered values?
Why jni calls fail on modification of composable remembered values?

Time:10-04

This is my PseudoStream class which interfaces with C

class PseudoStream{
    companion object{
        // This native function receives a String as a Input and returns a Modified output
        external fun output(input: String): String
    }
}

This is the C Inteface code to for the output function

// Creates a jstring from a std::string passed as mystr

jstring Create_Java_String(JNIEnv *environment, const std::string& mystr){
    return environment->NewStringUTF(mystr.data());
}

// Converts a jstring to std::string and returns it

std::string jstringtostring(JNIEnv *env, jstring jStr) {
    if(!jStr)
        return "";

       const jclass stringClass = env->GetObjectClass(jStr);
       const jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B");
       const jbyteArray stringJbytes = (jbyteArray) env->CallObjectMethod(jStr, getBytes, env->NewStringUTF("UTF-8"));

       size_t length = (size_t) env->GetArrayLength(stringJbytes);
       jbyte* pBytes = env->GetByteArrayElements(stringJbytes, NULL);

       std::string ret = std::string((char *)pBytes, length);
       env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT);

       env->DeleteLocalRef(stringJbytes);
       env->DeleteLocalRef(stringClass);
       return ret;
}

// This is the actual function called by jni to modify the String

extern "C"
JNIEXPORT jstring JNICALL
Java_com_phantom_automath_PseudoStream_00024Companion_output(JNIEnv *env, jobject thiz, jstring input) {
    std::string expression = jstringtostring(env, input);
    return Create_Java_String(env, expression   " (Modified by C  )");
}

Now, this code is the main UI code


var text_input = remember {mutableStateOf("Initial value ")} // Used by the TextField for Input
var text_output = remember {mutableStateOf("")} // Used by Text() for modified output

TextField(
    value = text_input.value,
    onValueChange = { text_input.value = it },
)

Button(onClick = {
        // The jni method is called here
        text_output.value = PseudoStream.output(text_input.value)
        }
    ){
    Text("Modify")
}

Text(text_output.value) // Displays the output by the jni call

So here is my problem, Whenever I click the Modify Button without changing the initial value of the TextField. The code works as expected. But, once I modify the TextField and then click Modify Button again this runtime error occurs in kotlin code.

java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter interactionSource
        at androidx.compose.foundation.ClickableKt.clickable(Unknown Source:7)
        at androidx.compose.foundation.ClickableKt.clickable$default(Clickable.kt:112)
        at androidx.compose.material.ButtonKt.Button(Button.kt:117)

The code works fine as long as I don't modifiy the value inside the TextField

CodePudding user response:

This is not the error caused by JNI , this issue is with how you are passing your TextField value to the jniMethod . The error in the stack trace says : java.lang.NullPointerException: Parameter specified as non-null is null. This means that you are passing null value to the function .

The code works fine as long as I don't modifiy the value inside the TextField -> This is because you have set the initial value of text_input , so a non-null value is passed ,thus causing no-error .

You need to do the following to correct your error : Just change this line :

text_output.value = PseudoStream.output(text_input.value)

To :

text_output.value = PseudoStream.output(text_input.value.text)

Here you pass the text that you given as an input to the TextField instead of the TextField data class which will do the trick . To know more about the above said , debug text_input.value and know your mistake .

CodePudding user response:

I have finally figured out why my app is not working. It has something to do with nullables inside the code logic of recompostion. Since, I have updated my android project to use the stable release of compose. The code is actually working as expected.

  • Related