I have a package named com.self.tryffmpeg.
There's a file MainActivity.java inside that package which declares two native functions from the C inside the JNI folder
static {
System.loadLibrary("ffmpeg");
System.loadLibrary("ffmpeg-test-jni");
}
private native int createEngine();
private native String loadFile(String file, byte[] array);
}
Inside the JNI folder, there's a C file which exports the functions needed by the MainActivity.java.
Inside the C , I exports the functions
NIEXPORT jint JNICALL Java_com_self_tryffmpeg_MainActivity_createEngine(JNIEnv* env, jclass clazz)
{
}
JNIEXPORT jintArray JNICALL Java_com_self_tryffmpeg_MainActivity_loadFile(JNIEnv* env, jobject obj,jstring file,jbyteArray array) {
}
But the error is the functions of loadFile and createEngine cannot be resolved. UnsatisfiedLinkError. Did i do anything wrong with that. I thought I export the functions correctly.
Related
I only have 1 .so file from old project.
How can I use this file without creating the same package in project or module ?
Actually, you don't need to change function name in .so file. You can use dlopen to load your .so library dynamically at runtime and dlsym to get pointer for you YOUR_FUNCTION_NAME() and then call YOUR_FUNCTION_NAME() by pointer. For do that in your current project you can create "wrapper" like that:
public class OldSoHelper {
public static native void loadOldSo();
public static native <TYPE_OF_RESULT> runFunFromOldSo(<PARAMETERS>);
public static native void unloadOldSo();
}
and in corresponding .c/.cpp file of current project (e.g. native-lib.cpp by default):
void *handle;
<TYPE_OF_OLD_FUNCTION> (*old_fun_wrapper)(<PARAMETERS_OF_OLD_FUNCTION>);
extern "C"
JNIEXPORT void JNICALL
Java_<YOUR_PACKAGE_NAME>_OldSoHelper_loadOldSo(JNIEnv *env, jclass type) {
handle = dlopen("<YOUR_OLD_SO>.so", RTLD_NOW);
old_fun_wrapper = (<TYPE_OF_OLD_FUNCTION> (*)(<PARAMETERS_OF_OLD_FUNCTION>))(dlsym(handle, "<OLD_FUNCTION_NAME_e.g._Java_com_abc_dee_Native_appInit>"));
}
extern "C"
JNIEXPORT jobject JNICALL
Java_<YOUR_PACKAGE_NAME>_OldSoHelper_runFunFromOldSo(JNIEnv *env, jclass type,
<PARAMETERS_FOR_OLD_FUNCTION>)
{
jclass ResultClass = env->FindClass("YOUR/PACKAGE/NAME/RESULT_CLASS");
jobject result = ...
jfieldID fieldId = env->GetFieldID(ResultClass, "<FIELD_NAME>", "<FILED_TYPE_LETTER>");
<TYPE_OF_OLD_FUNCTION> res = old_fun_wrapper(<PARAMETERS_FOR_OLD_FUNCTION>);
env->Set<TYPE>Field(result, fieldId , res.filed);
return result;
}
extern "C"
JNIEXPORT void JNICALL
Java_<YOUR_PACKAGE_NAME>_OldSoHelper_unloadOldSo(JNIEnv *env, jclass type) {
if (handle) {
dlclose(handle);
}
}
and from java code you can call:
...
// when you need old .so e.g. in onCreate()
OldSoHelper.loadOldSo();
...
// when you no need to call function from old .so
<TYPE_OF_RESULT> result = OldSoHelper.runFunFromOldSo(<PARAMETERS>);
...
// when you no need old .so e.g. in onDestroy()
OldSoHelper.unloadOldSo();
...
I have Android app, where I need to use C++ code. But I realised, I have problem to call C++ function. I have read something about it and tried to write something, I thought it could work. Here is my code:
private native int test(int a);
private void process() {
int ret=test(5);
Toast.makeText(this, String.valueOf(ret), Toast.LENGTH_LONG).show();
}
And my C++ code is:
#include <jni.h>
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_woodem_woodem_1opencvgrains_Main_test(JNIEnv *env, jint a)
{
return a*a;
}
Of course, my real function is much more complicated and I need to pass about 6 arguments, but I hope, this can illustrate.
NOTE: Even this code doesn't work to me.
My application crash immediately after calling process(). Could you please advice me, what am I doing wrong? Compiler tells me nothing and I have no idea, where the problem is.
You are forgetting about the second argument. For example method void test() will have JNI signature JNIEXPORT void JNICALL Java..._test(JNIEnv *env, jobject thiz). In your case, edit signature to (JNIEnv *env. jobject thiz, jint a).
Edit
The previous version showed a signature for a static function. I've updated it to match instance function.
For static signature is (JNIEnv *env, jclass clazz).
You've declared your native function as private native int test(); passing an integer argument to it.
It should be private native int test(int num);
I'm currently developping an android app with android studio, for this project i need to use a custom library written in c/c++ .
So i in order to do that i needed to use NDK.
The library contain methods that i need to implements in order to have access to specifics fonctions in android
My question is : how can I call my jni method inside the method existing in c
which will call a java method to store session key in the android system
So for a pratical exemple since it's maybe not clear:
In a file called lib_exemple.c i have three methods
the first one is the initialisation call to the library (jni to library c) -->working
JNIEXPORT void JNICALL
Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1init
(JNIEnv *env, jobject instance) {
lib_Example_init('0'); // c func of the library
}
then the second one is the jni who call a java method (jni to java) -> working
JNIEXPORT jint
JNICALL Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1store_1session_1key
(JNIEnv * env, jobject jobject1, jbyte jbyte1, jchar jchar1, jbyte jbyte2){
jclass clazz = (*env)->FindClass(env, "com/example/libExemple/Libs/ExempleLib");
jmethodID mCurrentActivityId = (*env)->GetMethodID(env, clazz, "KeyStoreSessionKey", "(BCB)I");
jint result = (*env)->CallIntMethod(env, jobject1, mCurrentActivityId, jbyte1, jchar1, jbyte2);
return result;
}
and the third method is the one the library c have in it (library C to jni)
int lib_Exemple_store_session_key(uint8_t Session, P_KEY_ST_T pKey, uint8_t keyType) {
//i want to call jni func here , so the librairy can access the native android function
return 0;
}
then to configure the ndk in a file called ExempleLib.java i have defined
static {
System.loadLibrary("LibExemple");
}
then the prototype of the initialisation of the library
public native void libExemple_init();
the prototype of the first method in lib_exemple.c
public native int lib_Exemple_store_session_key(byte pKey, char key_length, byte keyIndex);
and the java fonction called by it
protected int KeyStoreSessionKey(byte pKey, char key_length, byte keyIndex) {
...
}
my mainActivity contain
ExempleLib LibFunc = new ExempleLib();
Log.d(TAG, "init lib" );
LibFunc.lib_Exemple_init();
My goal is that after initialising my library and call an init function , it can call the first method in the second method of lib_exemple.c (the one used in the library )
thanks
EDIT :
The solution of my problem is when i initialize the lib , i need to save jvm
so i can access the JNIenv in the library method directly
jobject Savedinstance = NULL;
static JavaVM *cachedJVM;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
cachedJVM = jvm;
return JNI_VERSION_1_6;
}
JNIEnv *AttachJava() {
JavaVMAttachArgs args = {JNI_VERSION_1_6, 0, 0};
JNIEnv *java;
(*cachedJVM)->AttachCurrentThread(cachedJVM, &java, &args);
return java;
}
JNIEXPORT jint JNICALL
Java_com_example_vgosselin_libExemple_Libs_ExempleLib_lib_1init(JNIEnv *env,
jobject instance) {
Savedinstance = instance;
void *element = 0;
jint result;
result = lib_Exemple_init_();
return result;
}
so i can call the java method in
int lib_Exemple_store_session_key(uint8_t Session, P_KEY_ST_T pKey, uint8_t keyType) {
JNIEnv *NewEnv = AttachJava();
jclass clazz = (*NewEnv)->FindClass(NewEnv,"com/example/vgosselin/libExemple/Libs/ExempleLib");
jmethodID mCurrentActivityId = (*NewEnv)->GetMethodID(NewEnv,clazz,"KeystoreStoreKey","([BCB)I");
jint result;
jbyteArray Data = 0;
jchar Key = 0;
jbyte Type = 0;
result = (*NewEnv)->CallIntMethod(NewEnv, Savedinstance, mCurrentActivityId, Data, Key, Type);
return result;
}
and now i don't need the method in jni syntax anymore
JNIEXPORT jint JNICALL Java_com_example_libExemple_Libs_ExempleLib_lib_1Exemple_1store_1session_1key
Java code :
package com.example.maxim.myapplication;
//...
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = new TextView(this);
tv.setText( stringFromJNI() );
setContentView(tv);
}
public native String stringFromJNI();
static {
System.loadLibrary("hello-jni");
}
}
C code :
#include <string.h>
#include <jni.h>
jstring Java_com_example_maxim_myapplication_MainActivity_stringFromJNI( JNIEnv* env,
jobject thiz )
{
return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI .");
}
LogCat output:
Process: com.example.maxim.myapplication, PID: 2306
java.lang.UnsatisfiedLinkError: No implementation found for java.lang.String com.example.maxim.myapplication.MainActivity.stringFromJNI() (tried Java_com_example_maxim_myapplication_MainActivity_stringFromJNI and Java_com_example_maxim_myapplication_MainActivity_stringFromJNI__)
at com.example.maxim.myapplication.MainActivity.stringFromJNI(Native Method)
....
The package name is the same as the name of the function, but there is an errorThe package name is the same as the name of the function, but there is an error
You should declare you native method within your shared object like this
JNIEXPORT jstring Java_com_example_maxim_myapplication_MainActivity_stringFromJNI (JNIEnv *env, jobject obj)
The key point is the JNIEXPORT which instrucs the linker/compiler exposing/exporting your native method
EDIT - You also want make sure that indeed the "correct" so (libhello-jni.so) is being loaded (in the sense that indeed this so contains your method implementation, maybe you got some stale so somewhere in your library path)
Add debug logcat to your so upon its very loading
jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
LOGI("In JNI_OnLoad()");
return JNI_VERSION_1_6; // or the version corresponded to your NDK version
}
I am trying to use openCV in my android project and trying to run this native code but I don't know how to use this parameter
JNIEXPORT jint JNICALL Java_com_example_helloopencvactivity_nativecalls_filepath
(JNIEnv * env, jobject jo, jstring str1, jstring str2) {
cv::Mat img1 = cv::imread("");
}
I tried using this
const char *nativeString = (*env)->GetStringUTFChars(env, str1, 0);
cv::Mat img1 = cv::imread(nativeString);
but i am getting this error error: no matching function for call to '_JNIEnv::GetStringUTFChars
I need to pass the file path from android file system to openCV's native code for processing, the passing element is string and should be read by imread
first in the java side, my codes looks likeļ¼
private String path="/mnt/sdcard/";
InitFeature(width,height,path);
public native void InitFeature(int width, int height,String path);
then in the jni side, it's:
//passed from the java part and release after InitFreature
const char* nPath;
//to save the globle path
char* g_path;
JNIEXPORT void JNICALL Java_org_opencv_samples_tutorial6_Tutorial2Activity_InitFeature(JNIEnv* env, jobject,jint width,jint height,jstring path);
JNIEXPORT void JNICALL Java_org_opencv_samples_tutorial6_Tutorial2Activity_InitFeature(JNIEnv* env, jobject,jint width,jint height,jstring path)
{
//jsize path_size;
nPath=env->GetStringUTFChars(path,NULL);
//path_size=env->GetArrayLength(path);
LOGD("path: %s \n",nPath);
int length=strlen(nPath);
LOGD("length: %d \n",length);
g_path=new char[length+1];
memcpy(g_path,nPath,length+1);
LOGD("path_2: %s \n",g_path);
LOGD("length: %d \n",strlen(g_path));
char l_path[128];
strcpy(l_path,g_path);
strcat(l_path,"color.jpg");
LOGD("path_3: %s \n",l_path);
LOGD("length: %d \n",sizeof(l_path));
m_width=width;
m_height=height;
center.x=float (m_width/2.0+0.5);//float (Img_tmp->width/2.0+0.5);
center.y=float (m_width/2.0+0.5);//float (Img_tmp->height/2.0+0.5);
env->ReleaseStringUTFChars(path,nPath);
}
since I have different native calls, one to initiate features(shown here) and others to process every frame, and after you env->ReleaseStringUTFChars(path,nPath); the string would be invisible to jni part. I have the copy the string to a global char* g_path;
and a little sample is here as well, the file path is "/mnt/sdcard/color.jpg" and check those logs.
then you can use imread() to get this jpg.
I use other libs like libjpg, so I am not showing the codes here.