As the title suggested, How can I call Java function from C++ if the function is from a different java activity class?
All of the sample and tutorials calls C++ function and java back and forth but the caller is the class and the JNIEnv and jobject are passed from java thru JNI. But what if the function that needed to be called is from a different java activity class? How to do this? passing the "this" of the activity did not work
Here is sample layout of classes
Activity class
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
JNIAdapter.launch();
}
private void DisplayLoginDialog()
{
//...
}
}
JNIAdapter.class
public class JNIAdapter {
static {
System.loadLibrary("jnisample-lib");
}
public static native void launch();
}
jnisample.cpp
extern "C"
JNIEXPORT void JNICALL
Java_com_JNIAdapter_launch(JNIEnv *env,jobject object)
{
jclass dataClass = env->FindClass("com/game/ramo/MainActivity");
jmethodID javaMethodRef = env->GetMethodID(dataClass, "DisplayLoginDialog", "()V");
env->CallVoidMethod(object, javaMethodRef);
}
In the above code, using the jobject, refers to the JNIAdapter class and not the Activity hence the DisplayLoginDialog() is not called.
How to do this?
Your small example (I understand that you reduced all details not relevant to the specific problem, that's very nice!) could run without native method. JNIAdaptor.launch() could be pure Java. So, to begin with, rewrite it in Java and make sure it works.
The issues could be that MainActivity.DisplayLoginDialog() may expect its parent activity to be in the foreground, or in some specific state. This is easier to fix in pure Java.
After that, the JNI code you wrote should run without problems.
Related
I am diving into the ndk stuff and I've managed to make a simple library and call it from my activity.
In order to test it, in my Activity I have used:
static {
System.loadLibrary("my-lib");
}
public native static String callNativeFunction(Context context);
This means that calling MyActivity.callNativeFunction(context) does return the String value from my Cpp function.
I have 2 questions:
The loadLibrary is made in my main activity, however I want to be able for instance, to call the callNativeFunction function from an IntentService when the activity may be closed or from other places of my app. How can I properly load the library and have it available from all places of the app?
Since this is a simple function that I'll use in my project, is there anything else specific to do on release? From https://developer.android.com/studio/projects/add-native-code.html it seems that Gradle builds for all supported abis and adds them into the apk.
You need to load the library once. This can also be done in your Application class. Once the library is loaded, you can call the defined native methods from the appropriate classes defined in your cpp class
Application class :
public class SRApplication extends Application {
static {
System.loadLibrary("my-lib");
}
#Override
public void onCreate() {
super.onCreate();
}
}
CPP :
extern "C"
jstring
Java_com_example_service_ExampleService_getNativeString (
JNIEnv* env,
jobject /* this */) {
return env->NewStringUTF("yo");
}
Your Service :
package com.example.service.ExampleService;
public class ExampleService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
String nativeString = getNativeString();
return null;
}
public native String getNativeString();
}
If you don't have specific code for different cpu variants, you don't need to explicitly handle anything, the default
externalNativeBuild {
cmake {
cppFlags ""
}
}
is sufficient.
I have an Android app with an activity derived from NativeActivity like this:
public class MyNativeActivity extends android.app.NativeActivity
{
public native void TellNativeSide(int info);
static {
System.loadLibrary("MyNatAct"); // <--- is this necessary?
}
public int OtherMethods(...) ...
}
On the C/C++ side, I have
extern "C" void
Java_mycom_nativity_MyNativeActivity_TellNativeSide(JNIEnv *env,
jobjectactivityobj, jint info)
{
... do something
} // java native TellNativeSide() method //
extern "C" jint JNI_OnLoad(JavaVM *vm, void *)
{
LOGI("***JNI_OnLoad called...");
}
The libMyNatAct.so library is loaded automatically by the NativeActivity class and indeed android_main() and everything runs correctly with or without the system.loadLibrary() line. However, JNI_OnLoad() would never be called and the TellNativeSide() method is also not available on the Java side unless the
system.loadLibrary("MyNatAct");
call is there in the static class init block.
So it seems that the native .so has to be loaded twice. Once in the init block to make available all the native methods and get JNI_OnLoad() called, and another time by the NativeActivity class but not through system.loadLibrary()?
Is this the correct behaviour?
That's right. You must explicitly call system.loadLibrary() to have the native Java methods bound to exported functions of the .so file
I have one activity that has one void method say receiveSMS(), this is a void method, because of some reason i can not make it static function.
my question is how to call a void method of activity from a c file.
as calling a non- static method we need the instance of the class. how can i get the instance of a Activity in my c file.
Thanks In advance.
you need the Following Signature for Void Type
JNIEXPORT void JNICALL
after that add the method name with its package and its attribute
as an example following can be use as reference
Java_com_putitout_buck_talkinganimals_VideoPlayback_setActivityPortraitMode(JNIEnv *, jobject, jboolean isPortrait)
{
isActivityInPortraitMode = isPortrait;
}
in Java Method Signature will be like this
private native void setActivityPortraitMode(boolean isPortrait);
for further underStanding take look at JNI
as far as I understood the Android-NDK-thingy it works as follows: I have to use a NativeActivity that itself calls into the attached native code handing over some OpenGL graphics context. This context can be used by the native part to draw some things with.
What I could not fiddle out until now: how about some GUI elements? Is there a possibility to call back from native code to Java just to create some UI-elements and perhaps to use layouts? Means is it possible to use the standard Android GUI elements also with such native code?
If yes: how can this be done? If not: what alternatives exist (except drawing everything manually)?
Thanks!
If you want to use GUI stuff on the openGL view, I think it would be better to use a normal Activity instead of a NativeActivity.
You can use the Android sample called GL2JNIActivity. Check that to import it from Eclipse.
You can declare some native functions in the JNI Library part (class JNILib in the sample). which you will call when you your GUI listeners get called. Basically it will look like that:
public class GL2JNILib {
static {
// Maybe you need to load other relevant libraries here
System.loadLibrary("gl2jni");
}
public static native void init(int width, int height);
public static native void step();
/*Add here your native functions to send to the native code*/
public static native void buttonClicked();
//... add other listeners here (i.e: screen touched, etc.)
}
Then in the Activity itself (in the sample that corresponds to the class JNIActivity), you can just load an XML GUI file as usual. When you get some input through the GUI, you can just call the native functions added in the GL2JNI class. More or less it will look like that:
public class GL2JNIActivity extends Activity {
GL2JNIView mView;
Button mButton;
#Override protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mView = new GL2JNIView(getApplication());
setContentView(mView);
mButton = (Button)findViewById(R.id.your_button);
mButton.setOnClickListener( new Button.OnClickListener(){
#Override
public void onClick(View v) {
mView.buttonClicked();
}
});
}
/*...*/
}
Finally, you have to implement the native function in the native side:
JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_buttonClicked(
JNIEnv * env, jobject obj)
{
// will be called when the button is clicked
}
I am using jni and I can call java functions in regular activity from c++ class but when I try to call java functions in non activity class, my code does not work.
I mean
jclass activityclass = env->FindClass("com/example/test/MyActivity);
jmethodID methodID = env->GetMethodID(activityclass,"FunctionName","()V");
env ->CallVoidMethod(obj,methodID);
This works.
When I try to call same function(with same name) from non activity regular java class, it doesnt work.
jclass regularclass = env->FindClass("com/example/test/MyRegularClass);
jmethodID methodID = env->GetMethodID(regularclass ,"FunctionName","()V");
env ->CallVoidMethod(obj,methodID);
Why I cannot call function in non activity class? What is my mistake?
My MyRegularClass
public class MyRegularClass{
public static void FunctionName(){
Log.i("Java Worked","Java Worked");
}
}
My Activity classs
public class MyActivity{
system.load.library("mylib");
#Override
public void onCreate(Bundle savedInstanceState){
....
....
testJNI();
}
public static void FunctionName(){
Log.i("Activity Worked","Activity Worked");
}
public native void testJNI();
}
You need to use GetStaticMethodID() and CallStaticVoidMethod() for static methods. This has nothing to do with the methods being in activities or not.