I'm trying to return a string from native. For that I'm using the following code.
Native:
#include <string.h>
#include <jni.h>
jstring Java_com_test_Demo_getString(JNIEnv *env, jobject javaThis) {
return (*env)->NewStringUTF(env, "hello");
}
Java:
private native String getString(); //in com.test.Demo
After this I'm generating .so files using ndk-build and including them in jniLibs.
However, the string returned is null. Any ideas on what is wrong ?
Related
For the protection issue of the shared library I will try to get package name using JNI but it will give errors. So, is it possible to get package name or applicationId using JNI? If anyone have example or references for this problem then suggests. Because not any good tutorial or solution available. Else any other way suggest of the protection of the shared library.
Yes, it's possible. Android is based on Linux, we can obtain a lot of information in user space provided by kernel.
In your example, the information stored here /proc/${process_id}/cmdline
We can read this file, and get the application id.
See a simple example
#include <jni.h>
#include <unistd.h>
#include <android/log.h>
#include <stdio.h>
#define TAG "YOURAPPTAG"
extern "C"
JNIEXPORT void JNICALL
Java_com_x_y_MyNative_showApplicationId(JNIEnv *env, jclass type) {
pid_t pid = getpid();
__android_log_print(ANDROID_LOG_DEBUG, TAG, "process id %d\n", pid);
char path[64] = { 0 };
sprintf(path, "/proc/%d/cmdline", pid);
FILE *cmdline = fopen(path, "r");
if (cmdline) {
char application_id[64] = { 0 };
fread(application_id, sizeof(application_id), 1, cmdline);
__android_log_print(ANDROID_LOG_DEBUG, TAG, "application id %s\n", application_id);
fclose(cmdline);
}
}
This works for me:
static jstring get_package_name(
JNIEnv *env,
jobject jActivity
) {
jclass jActivity_class = env->GetObjectClass(jActivity);
jmethodID jMethod_id_pn = env->GetMethodID(
jActivity_class,
"getPackageName",
"()Ljava/lang/String;");
jstring package_name = (jstring) env->CallObjectMethod(
jActivity,
jMethod_id_pn);
return package_name;
}
I create my first NDK project, that show some text on screen.
I use native method in class NativeLib in java and implement in a class in C.
But I received an error base operand of '->' has non-pointer type 'JNIEnv {aka _JNIEnv}
#include <jni.h>
#include <string.h>
#include <stdio.h>
#include <android/log.h>
#define DEBUG_TAG "MY_NDK_DEMO"
JNIEXPORT jstring JNICALL Java_com_example_helloworld_NativeLib_helloWorld
(JNIEnv * env, jobject obj) {
return (*env)->NewStringUTF("Hello World JNI!");
}
try return env->NewStringUTF("Hello World JNI!");
c++ is different from c
so to make it work
use
env->NewStringUTF("Hy");
I just started to use NDK and i ran Hello_Jni and i know how that works but i wanted to try do something like this and cant get it to work (Im doing this manually)
simple.c
#include "simple.h"
#include <jni.h>
JNIEXPORT jdouble JNICALL
Java_com_example_Test_round_decimals (JNIEnv * env, jobject obj, jdouble value, jint decimals) {
double m = pow (10, decimals);
return (double) round (value * m) / m;
}
JNIEXPORT jstring JNICALL Java_com_example_Test_hello(JNIEnv* env, jobject javaThis) {
return (*env)->NewStringUTF(env, "Hello from ME!");
}
simple.h
#include <stdio.h>
#include <stdlib.h>
#include "math.h"
#include <string.h>
#include <jni.h>
JNIEXPORT jdouble JNICALL Java_com_example_Test_round_decimals (JNIEnv * env, jobject obj,jdouble value, jint decimals);
JNIEXPORT jstring JNICALL Java_com_example_Test_hello(JNIEnv* env, jobject javaThis);
Activity
public class Test{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
double test = round_decimals(10.1234,2);
double test2 = hello();
Log.i("Round","Number" + test); // i would like to get this
Log.i("String","hello? " + test2); // this works it shows "Hello From ME!"
}
public native double round_decimals(double value, int decimals);
public native String hello();
static {
System.loadLibrary("simple");
}
}
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := simple
LOCAL_SRC_FILES := simple.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)
include $(BUILD_SHARED_LIBRARY)
android.mk, simple.c and simple.h are in JNI folder of my Activity.
And also running Build-ndk in folder of my Activity shows no errors
But when i try to build it on my tablet i get this error.
java.lang.UnsatisfiedLinkError:round_decimals
You generated the .h file from a version of the .java that had a package statement in it, then you modified the .java file to remove the package statement, then you compiled and ran, and nothing matched up.
There's something else wrong here. The method signature should be (JNIEnv*, jobject, jdouble, jint) if you've generated the files correctly.
Regenerate the .h file and adjust the .c file accordingly.
i Found the problem it was with Java_com_example_Test_round_decimals had to rename it to
Java_com_example_Test_roundDecimals and with that i had to rename public native double round_decimals(double value, int decimals); to public native double roundDecimals(double value, int decimals);
You had an underscore (_) inside your method name and underscores are name delimiters in JNI convention.
Changing the java method name to camel case is a solution but you can also escape underscores using _1 in your native method name, like so: Java_com_example_Test_round_1decimals.
I have this JNI code that uses C++.
#include <eigenfaces_jni.h>
#include <opencv2/core/core.hpp>
#include <opencv2/contrib/contrib.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
using namespace cv;
JNIEXPORT jstring JNICALL Java_com_example_facedetector_FaceTracker_nativeCreateObject
(JNIEnv *env, jclass, jstring JFileDir){
// These vectors hold the images and corresponding labels.
vector<Mat> images;
const char *str = env->GetStringUTFChars(jFileDir, NULL);
const char *buf;
/*Releasing the Java String once you have got the C string
is very important!!!*/
env->ReleaseStringUTFChars(jFileDir, str);
Mat im = imread(jFileDir);
if (im.empty())
{
cout << "Cannot load image!" << endl;
buf = "Can load image";
}
else{
buf = "Cannot load image";
}
return env->NewStringUTF("Hello");
}
However, there is always an error at
const char *str = env->GetStringUTFChars(jFileDir, NULL);
which states that
- Symbol 'jFileDir' could not be resolved
- 'jFileDir' was not declared in this scope
- Invalid arguments ' Candidates are: const char * GetStringUTFChars(_jstring *, unsigned
char *) '
Tried the solution in this link Type 'std::string' could not be resolved.
Still not working. Any help to solve this problem would be appreciated. Thank you.
You've declared a function parameter called JFileDir, but nothing called jFileDir. C++ is case-sensitive.
Well, since I don't have enough reputation points to commend, I have to write this as an answer. Check your function parameter list (JNIEnv *env, jclass, jstring JFileDir), what is jclass about, I feel something is missing here.
I have a simple piece of code that i want to use in my java (android) application:
#include <string.h>
#include <jni.h>
jstring
Java_com_example_ndk_MainActivity_stringFromJNI( JNIEnv* env,
jobject thiz)
{
return (*env)->NewStringUTF(env, "Hello from JNI !");
}
If i use C and call this file *.c - everything is ok, but i want this code on C++, i rename this file to *.cpp (and change Android.mk). Everything is compiled but when i try to use this function in the way i've used it in C, i have a error.
*Also i modify body of the func:
return env->NewStringUTF("Hello from JNI !");
Try to use this:
public native String stringFromJNI();
static {
System.loadLibrary("hello-jni");
}
And got such an error:
09-10 17:55:46.410: W/dalvikvm(6339): No implementation found for native Lcom/example/ndk/MainActivity;.stringFromJNI ()Ljava/lang/String;
09-10 17:55:46.410: E/AndroidRuntime(6339): java.lang.UnsatisfiedLinkError: stringFromJNI
09-10 17:55:46.410: E/AndroidRuntime(6339): at com.example.ndk.MainActivity.stringFromJNI(Native Method)
09-10 17:55:46.410: E/AndroidRuntime(6339): at com.example.ndk.MainActivity.onCreate(MainActivity.java:22)
I can't understand why the same code run in C and fail (runtime) in C++.
To allow for overloading of functions, C++ uses something called name mangling. This means that function names are not the same in C++ as in plain C.
To inhibit this name mangling, you have to declare functions as extern "C":
extern "C" jstring
Java_com_example_ndk_MainActivity_stringFromJNI( JNIEnv* env,
jobject thiz)
{
return (*env)->NewStringUTF(env, "Hello from JNI !");
}
To use C calling conventions in C++ code, surround your functions with:
extern "C"
{
/* your C API */
}