gogoWebsite

Calling java in c++ in Android

Updated to 1 hour ago
Calling java in c++ in Android

Instantiation of java class
Everyone knows that java requires obj var = new obj(); in such a process, we call the member functions of the java class in C++, and of course we also need to give an example first.
The sampled function is as follows
jobject getInstance(JNIEnv* env, jclass obj_class)
{
   jmethodID construction_id = env->GetMethodID(obj_class, "<init>", "()V");
   jobject obj = env->NewObject(obj_class, construction_id);
   return obj;
}
The env in this function represents the environment parameter, and jclass represents the handle of a java class.
jmethodID construction_id = env->GetMethodID(obj_class, "<init>", "()V");
The parameters of GetMethodID are (class handle, method name, parameter name)
This is to get the handle of a method in the java class. One thing that needs special attention is that the handle of the constructor is different from the handle of other methods.
The parameter "Method Name" filled in when you get the handle of a general method is directly the name of this method, and if you want to create a constructor, you must fill in "<init>". Apart from this difference, there is no difference.
And our "parameter name" seems to be written a bit strange. But it’s no wonder if he talks about his rules in detail.
The writing rules for parameter names are (parameter type 1, parameter type 2...). Return type. The corresponding type of representation is as follows
 
Java Types
Local type
Aliases defined in JNI
int
long
jint
long
_int64
jlong
byte
signed char
jbyte
boolean
unsigned char
jboolean
char
unsigned short
jchar
short
short
jshort
float
float
jfloat
double
double
jdouble
Object
_jobject*
jobject
For example, if we want to find a function of type int func(double), we should write it like this:
jmethodID construction_id = env->GetMethodID(obj_class, " func ", "(D)I");
 
After finding the constructor method, we call the constructor directly and return the generated class.
jobject obj = env->NewObject(obj_class, construction_id);
where obj_class is the handle of the class construction_id is the handle of the constructor, and the instance of the class is returned by the function.
5.3 Calling other functions of java class
JNIEXPORT jstring JNICALL Java_com_hm_hello_CActivityMain_stringFromJNI
(JNIEnv* env, jobject)
{
  jstring str;
  jclass java_class = env->FindClass("com/hm/hello/CForCall");
   if (java_class == 0)
   {
      return env->NewStringUTF("not find class!");
   }
  jobject java_obj = getInstance(env, java_class);
  if (java_obj == 0)
  {
      return env->NewStringUTF("not find java OBJ!");
  }
  jmethodID java_method = env->GetMethodID(java_class, "GetJavaString", "()Ljava/lang/String;");
  if(java_method == 0)
  {
      return env->NewStringUTF("not find java method!");
  }
  str = (jstring)env->CallObjectMethod(java_obj, java_method);
  return str;
}
 
After reading the introduction of the constructor, this code is actually easy to understand. We found that the steps to call functions in java are just a few steps:
1 Found the class handle
2 Find the handle of the method of the class
3 Instantiated Class
4 Call the instantiated class method.
There is only one paragraph in the above code that has not been mentioned, that is
jclass java_class = env->FindClass("com/hm/hello/CForCall");
You can tell by looking at the name that you are looking for the handle of the corresponding class, where you prepare the complete package name of the calling class (convert . to /), and CforCall is the name of the class we want to call.
At this point, compile and then debug in Android, the cute robot appears again, haha, 12345 appears on the screen
success! ! ! !
 
 
 
 
Android NDK development: Jni calls Java objects

Java objects are used in local code

By using appropriate JNI functions, you can create Java objects, get, set static and instance fields, and call static and instance functions. JNI recognizes domains and methods through IDs. The ID of a domain or method is a necessary parameter to any function that processes domains and methods.
The following table lists the JNI functions used to obtain static and instance domains and methods. Each function accepts (as arguments) the class of a domain or method, their name, symbol and their corresponding returned jfieldID or jmethodID.
function
describe
GetFieldID
Get the ID of the domain of an instance
GetStaticFieldID
Get the ID of a static domain
GetMethodID
Get the ID of the method of an instance
GetStaticMethodID
Get the ID of a static method
Construct an instance of a Java object
jclass cls = (*env)->FindClass(env, "Lpackagename/classname;");  //Create a reference to class
jmethodID id = (*env)->GetMethodID(env, cls, "", "(D)V"); //Note that the name of the method here is "", which means that this is a constructor, and the constructor parameters are double type
job obj = (*env)->NewObjectA(env, cls, id, args); //Get an instance, args is a parameter of the constructor, it is a jvalue* type
First, get a class reference of a Java class (*env)->FindClass(env, "Lpackagename/classname;"); Please note the parameters: Lpackagename/classname; , L represents that this is describing an object type. Packagename/classname is the class path of the object's ear. Please note that it must end with a semicolon (;)!
Then get the id of the function, jmethodID id = env->GetMethodID(cls, "", "(D)V"); The first is the class reference just obtained, the second is the name of the method, and the last is the signature of the method
Still don't understand? I used to do this, please continue to see...
 
Still don't understand? I used to do this, please continue to see...
Difficult to understand function signatures

The definition of JNINativeMethod is as follows:
typedef struct {
  const char* name;
  const char* signature;
  void* fnPtr;
} JNINativeMethod;
The first variable name is the name of the function in Java.
The second variable signature, using a string, describes the parameter and return value of the function.
The third variable fnPtr is a function pointer, pointing to a C function.

Among them, the second parameter is difficult to understand, for example
"()V"
"(II)V"
"(Ljava/lang/String;Ljava/lang/String;)V"

In fact, these characters correspond one by one to the parameter type of the function.
The characters in "()" represent parameters, and the following ones represent the return value. For example, "()V" means void Func();
"(II)V" means void Func(int, int);

What about other situations? Please check the table below:
type
symbol
boolean
Z
byte
B
char
C
short
S
int
I
long
L
float
F
double
D
void
V
object object
LClassName;      Class L name;
Arrays
[array-type        [array type
methods methods
(argument-types)return-type     (parameter type)Return type
A little addition:
1. When the method parameter or return value is an object in java, the signature must be plus its path, but this path must be separated by "/", and custom objects also use this rule.
For example, it is "java/lang/String", it is "Lcom /nedu/jni/helloword/Student;"
2. When the method parameters or return value is array type, please add [
For example, [I represents int[],[[[D represents double[][][], that is, add several [

Calling a method of a Java object in a local method

1. Get the class of the Java object you need to access:

jclass cls = (*env)->GetObjectClass(env, obj);         // Use the GetObjectClass method to get the jclass corresponding to obj.
jclass cls = (*env)->FindClass("android/util/log") // Directly search for the class name, it needs to be a static modified class.
2. Get the MethodID:

jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "(I)V"); //GetStaticMethodID(…), get the ID of the static method using the GetMethdoID method to get the MethdoID of the method you want to use
The significance of its parameters:
env-->JNIEnv
cls-->Jclass obtained in the first step
"callback"-->The method name to be called
"(I)V"-->The Signature of the method, the signature is the same as the previous JNI rules.
3. Call method:

(*env)->CallVoidMethod(env, obj, mid, depth);// CallStaticIntMethod(….) , call static methods
Call the method using the CallVoidMethod method. The meaning of parameters:
env-->JNIEnv
obj-->Jobject passed through local method
mid-->MethodID to be called (i.e. MethodID obtained in the second step)
depth-->The parameters required by the method (the corresponding method requirements are added)

Note: The CallVoidMethod method call is used here, because there is no return value. If there is a return value, use the corresponding method, which will be mentioned later.
CallVoidMethod                   CallStaticVoidMethod
CallIntMethod                     CallStaticVoidMethod
CallBooleanMethod              CallStaticVoidMethod
CallByteMethod                   CallStaticVoidMethod
Now I understand a little bit that the article has started to construct an instance of Java object? Let's go deeper:
Jni operates Java String object

The String object passed from a java program corresponds to the jstring type in the local method. The jstring type is different from the char* in c, so if you use it directly as a char*, an error will occur. Therefore, before using it, you need to convert jstring to char* in c/c++. Here, the method provided by JNIEnv is used to convert it.
const char *str = (*env)->GetStringUTFChars(env, jstr, 0);
(*env)->ReleaseStringUTFChars(env, jstr, str);
Here, use the GetStringUTFChars method to convert the passed in propt (jstring type) into UTF-8 format, and it can be used in the local method.
Note: After using the object you converted, you need to display the call to the ReleaseStringUTFChars method to allow the JVM to free the space of the object converted to UTF-8 string. If the call is not displayed, the JVM will keep the object and will not be collected by the garbage collector, which will cause memory overflow.

Here are some ways Jni can access String objects:
• GetStringUTFChars
• GetStringChars
• ReleaseStringUTFChars    Release the pointer to char* in UTF-8 format
• ReleaseStringChars           Release the pointer to char* in Unicode format
• NewStringUTF
• NewString                                                                                                                             �
• GetStringUTFLength      GetStringUTFLength Gets the length of char* in UTF-8 format
• GetStringLength            GetStringLength of char* in Unicode format
The following provides two methods of redirecting String objects and char*:
/* c/c++ string turn to java jstring */
jstring charToJstring(JNIEnv* env, const char* pat)
{
   jclass     strClass = (*env)->FindClass(env, "java/lang/String");
   jmethodID  ctorID   = (*env)->GetMethodID(env, strClass, "", "([BLjava/lang/String;)V");
   jbyteArray bytes    = (*env)->NewByteArray(env, strlen(pat));
   (*env)->SetByteArrayRegion(env, bytes, 0, strlen(pat), (jbyte*)pat);
   jstring    encoding = (*env)->NewStringUTF(env, "UTF-8");
   return (jstring)(*env)->NewObject(env, strClass, ctorID, bytes, encoding);
}
 
/* java jstring turn to c/c++ char* */
char* jstringToChar(JNIEnv* env, jstring jstr)
{      
   char* pStr = NULL;
   jclass     jstrObj   = (*env)->FindClass(env, "java/lang/String");
   jstring    encode    = (*env)->NewStringUTF(env, "utf-8");
   jmethodID  methodId  = (*env)->GetMethodID(env, jstrObj, "getBytes", "(Ljava/lang/String;)[B");
   jbyteArray byteArray = (jbyteArray)(*env)->CallObjectMethod(env, jstr, methodId, encode);
   jsize      strLen    = (*env)->GetArrayLength(env, byteArray);
   jbyte      *jBuf     = (*env)->GetByteArrayElements(env, byteArray, JNI_FALSE);
   if (jBuf > 0)
   {
       pStr = (char*)malloc(strLen + 1);
       if (!pStr)
       {
           return NULL;
       }
       memcpy(pStr, jBuf, strLen);
       pStr[strLen] = 0;
   }
   env->ReleaseByteArrayElements(byteArray, jBuf, 0);
   return pStr;
}
 
How to call Java in C/C++

The cross-platform feature of Java makes Java more and more popular among developers, but you often hear a lot of complaints: a control panel window will pop up every time the graphical user window interface developed with Java is launched, and this console window makes the originally great interface lose its color. How can GUI programs developed through Java not pop up the Java console window? In fact, many popular development environments such as JBuilder and Eclipse are integrated environments that use pure Java development. These integrated environments do not open a command window when they are started because it uses JNI (Java Native Interface) technology. Through this technology, developers do not have to use the command line to start Java programs. They can directly start Java programs by writing a local GUI program, which can avoid opening another command window, making the developed Java programs more professional.

JNI allows Java programs running on virtual machines to make calls to programs or class libraries written in other languages ​​(such as C and C++). At the same time, JNI provides a complete set of APIs that allow Java virtual machines to be embedded directly into local applications.

This article will introduce how to call Java methods in C/C++, and introduce the entire development steps, possible difficulties and solutions based on the possible problems involved. The tools used in this article are Java Development Kit (JDK) version 1.3.1 created by Sun, and Microsoft's Visual C++ 6 development environment.


Environment construction

In order for the code in the following sections of this article to work properly, we must establish a complete development environment. First, you need to download and install JDK 1.3.1, and its download address is "". Assume that the installation path is C:\JDK. The next step is to set up an integrated development environment and open the options dialog box through the menu Tools → Options of Visual C++ 6.

Add the directories C:\JDK\include and C:\JDK\include \win32 to the Include Files directory of the development environment, and add the C:\JDK\lib directory to the Library Files directory of the development environment. These three directories are header files and library files for some constants, structures and methods defined by JNI. The integrated development environment has been set. In order to execute the program, the directory C:\JDK\jre\bin\classic is set to the system's Path environment variable in order to execute the program. What needs to be mentioned here is that in order to facilitate the convenience of some developers, they can directly copy the DLL files used in JRE to the system directory. This does not work, and will cause the initialization of the Java virtual machine environment to fail (return value -1), because the Java virtual machine uses a relative path to find the used library files and other related files. At this point, the entire JNI development environment has been set up. In order to make this JNI journey go smoothly, a Java class must be prepared first. Almost all representative properties and methods in Java will be used in this class, such as static methods and properties, arrays, exception throwing and catching, etc. The Java program we define is as follows. All the code demonstrations in this article will be based on the Java program, and the code is as follows:

package ;
/**
* This class is to demonstrate how JNI accesses various object properties, etc.
* @author liudong
*/
public class Demo {
// Used to demonstrate how to access static basic type attributes
public static int COUNT = 8;
//Demonstrate object type attribute
public String msg;
private int[] counts;
public Demo() {
this("default constructor");
}
/**
* Demonstrate how to access the constructor
*/
public Demo(String msg) {
(“<init>:” + msg);
= msg;
= null;
}
/**
* This method demonstrates how to access an access and handle Chinese characters
*/
public String getMessage() {
return msg;
}
/**
* Demonstrate access to array objects
*/
public int[] getCounts() {
return counts;
}
/**
* Demonstrate how to construct an array object
*/
public void setCounts(int[] counts) {
= counts;
}
/**
* Demonstrate exception capture
*/
public void throwExcp() throws IllegalAccessException {
throw new IllegalAccessException(“exception occur.”);
}
}




Initialize the virtual machine


Local code must first load the Java virtual machine before calling the Java method, and then all Java programs are executed in the virtual machine. In order to initialize a Java virtual machine, JNI provides a series of interface functions Invocation APIs. These APIs make it easy to load virtual machines into memory. To create a virtual machine, you can use the function jint JNI_CreateJavaVM (JavaVM **pvm, void **penv, void *args). One thing to note about this function is that in JDK 1.1, the third parameter always points to a structure JDK1_1InitArgs, which cannot be completely seamlessly ported in all versions of virtual machines. In JDK 1.2, a standard initialization structure JavaVMInitArgs has been used instead of JDK1_1InitArgs. Below we give two different versions of the sample code.

Initialize the virtual machine in JDK 1.1:

#include <>
int main() {
JNIEnv *env;
JavaVM *jvm;
JDK1_1InitArgs vm_args;
jint res;
/* IMPORTANT: Version number settings must not be missed */
vm_args.version = 0×00010001;
/*Get the default virtual machine initialization parameters*/
JNI_GetDefaultJavaVMInitArgs(&vm_args);
/* Add a custom classpath */
sprintf(classpath, “%s%c%s”,
vm_args.classpath, PATH_SEPARATOR, USER_CLASSPATH);
vm_args.classpath = classpath;
/*Set some other initialization parameters*/
/* Create a virtual machine */
res = JNI_CreateJavaVM(&jvm,&env,&vm_args);
if (res < 0) {
fprintf(stderr, “Can’t create Java VM\n”);
exit(1);
}
/*Release virtual machine resources*/
(*jvm)->DestroyJavaVM(jvm);
}



JDK 1.2 Initializes the virtual machine:

/* */
#include <>
int main() {
int res;
JavaVM *jvm;
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options[3];
vm_args.version=JNI_VERSION_1_2;//This field must be set to this value
/*Set initialization parameters*/
options[0].optionString = “-=NONE”;
options[1].optionString = “-=.”;
options[2].optionString = "-verbose:jni"; // Used to track runtime information
/* Version number setting cannot be missed*/
vm_args.version = JNI_VERSION_1_2;
vm_args.nOptions = 3;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (res < 0) {
fprintf(stderr, “Can’t create Java VM\n”);
exit(1);
}  
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, “Java VM destory.\n”);
}



In order to ensure the portability of JNI code, it is recommended to use the JDK 1.2 method to create a virtual machine. The second parameter of JNI_CreateJavaVM function, JNIEnv *env, is a parameter that runs through the entire JNI, because almost all functions require one parameter that is JNIEnv *env.


Access class method


After initializing the Java virtual machine, you can start calling Java methods. To call a method of a Java object, you have to go through several steps:

1. Get the class definition of the specified object (jclass)

There are two ways to obtain the class definition of an object: the first is to use FindClass to find the corresponding class when the class name is known. However, it is important to note that the class name is different from the Java code I usually write. For example, to get the definition of the class, you must call the following code:

jclass cls = (*env)->FindClass(env, "jni/test/Demo"); //Replace the dot with a slash



Then directly obtain the corresponding class definition through the object:

jclass cls = (*env)-> GetObjectClass(env, obj);
// where obj is the object to be referenced, and the type is jobject



2. Read the definition of the method to be called (jmethodID)

Let's first look at the functions that get the method definition in JNI:

jmethodID (JNICALL *GetMethodID)(JNIEnv *env, jclass clazz, const char *name,
const char *sig);
jmethodID (JNICALL *GetStaticMethodID)(JNIEnv *env, jclass class, const char
*name, const char *sig);

The difference between these two functions is that GetStaticMethodID is used to obtain the definition of static methods, and GetMethodID is used to obtain the definition of non-static methods. Both functions need to provide four parameters: env is the JNI environment obtained by initializing the virtual machine; the second parameter class is the object's class definition, which is the obj obtained in the first step; the third parameter is the method name; the most important is the fourth parameter, which is the method definition. Because we know that the polymorphism of methods allowed in Java cannot be located in a specific method just by the method name, so the fourth parameter is needed to specify the specific definition of the method. But how to use a string to represent the specific definition of a method? A decompilation tool Javap has been prepared in JDK. Through this tool, you can get the definition of each property and method in the class. Let’s take a look at the definition below:

Open the command line window and run javap -s -p to get the run result as follows:

Compiled from
public class extends {
public static int COUNT;
/*  I  */
public msg;
/*  Ljava/lang/String;  */
private int counts[];
/*  [I  */
public ();
/*  ()V  */
public ();
/*  (Ljava/lang/String;)V  */
public getMessage();
/*  ()Ljava/lang/String;  */
public int getCounts()[];
/*  ()[I  */
public void setCounts(int[]);
/*  ([I)V  */
public void throwExcp() throws ;
/*  ()V  */
static {};
/*  ()V  */
}



We see that each property and method in the class has a comment below. The content that does not contain spaces in the comment is what to fill in for the fourth parameter (for the specific parameters of Javap, please check the help of using JDK). The following code shows how to access the getMessage method:

/*
Suppose we already have an instance of obj
*/
jmethodID mid;
jclass cls = (*env)-> GetObjectClass (env, obj); //Get the class definition of the instance
mid=(*env)->GetMethodID(env,cls,"getMessage"," ()Ljava/lang/String; ");
/*If mid is 0, it means that the method definition has failed*/
jstring msg = (*env)-> CallObjectMethod(env, obj, mid);
/*
If the method is a static method, you only need to change the last sentence code to the following writing method:
jstring msg = (*env)-> CallStaticObjectMethod(env, cls, mid);
*/



3. Calling methods

In order to call a method of the object, you can use the function Call<TYPE>Method or CallStatic<TYPE>Method (static method of accessing the class), and <TYPE> depends on different return types. These methods are defined using variable parameters. If you need parameters to access a method, you only need to fill in all parameters into the method in order. When talking about constructor access, it will demonstrate how to access constructors with parameters.


Access class properties


The properties of the access class are generally consistent with the methods of the access class, but they just turn the method into attributes.

1. Get the class of the specified object (jclass)

This step is exactly the same as the first step of accessing the class method. For details, see the first step of accessing the class method.

2. Read the definition of class attributes (jfieldID)

In JNI, the method of obtaining class attributes is defined like this:

jfieldID (JNICALL *GetFieldID)
(JNIEnv *env, jclass clazz, const char *name, const char *sig);
jfieldID (JNICALL *GetStaticFieldID)
(JNIEnv *env, jclass clazz, const char *name, const char *sig);



The first parameter of these two functions is the JNI environment; clazz is the definition of the class; name is the attribute name; and the fourth parameter is also to express the attribute type. Before, we used the Javap tool to obtain the detailed definition of the class, there were two lines like this:

public msg;
/*  Ljava/lang/String;  */



The content of the commented second line is the information to be filled in for the fourth parameter, which is the same as when accessing the class method.

3. Read and set property values

It is easy to access the property value with the definition of the property. There are several methods to read and set the properties of a class, which are: Get<TYPE>Field, Set<TYPE>Field, GetStatic<TYPE>Field, SetStatic<TYPE>Field. For example, when reading the msg attribute of the Demo class, you can use GetObjectField, and when accessing COUNT, you can use GetStaticIntField. The relevant code is as follows:

jfieldID field = (*env)->GetFieldID(env,obj,"msg"," Ljava/lang/String;");
jstring msg = (*env)-> GetObjectField(env, cls, field); //msg is the msg corresponding to Demo
jfieldID field2 = (*env)->GetStaticFieldID(env,obj,"COUNT","I");
jint count = (*env)->GetStaticIntField(env,cls,field2);


Access the constructor

Many people often encounter problems in this section when they first come into contact with JNI. After searching through the entire function NewObject, it should be a constructor that can be used to access the class. However, this function needs to provide a method definition of the constructor, whose type is jmethodID. From the previous content, we know that to get the definition of a method, we must first know the name of the method, but how do we fill in the name of the constructor? In fact, accessing a constructor is generally the same as accessing an ordinary class method. The only difference is that the method name is different and the method is called different. When accessing the constructor of the class, the method name must be filled in "<init>". The following code demonstrates how to construct an instance of a Demo class:

jclass cls = (*env)->FindClass(env, "jni/test/Demo");
/**
First, get the definition of the class through the name of the class, which is equivalent to the method in Java
*/
if (cls == 0)
<error handler>
jmethodID mid = (*env)->GetMethodID(env,cls,"<init>","(Ljava/lang/String;)V ");
if(mid == 0)
<error handler>
jobject demo = jenv->NewObject(cls,mid,0);
/**
Accessing the constructor must use the NewObject function to call the definition of the constructor obtained earlier.
In the above code, we construct an instance of a Demo and pass an empty string null
*/




Array processing

Create a new array

To create an array, we should first know the type and length of the array element. JNI defines the type j<TYPE>Array of a batch of arrays and the function of array operations New<TYPE>Array, where <TYPE> is the type of elements in the array. For example, to create an array of integers with a size of 10 and each position value is 1-10, write the code as follows:

int i = 1;
jintArray array; //Define array object
(*env)-> NewIntArray(env, 10);
for(; i<= 10; i++)
(*env)->SetIntArrayRegion(env, array, i-1, 1, &i);



Accessing data in an array

When accessing an array, you should first know the length of the array and the type of element. Now we print out the value of each element in the created array, the code is as follows:

int i;
/* Get the number of elements of the array object */
int len = (*env)->GetArrayLength(env, array);
/* Get all elements in the array */
jint* elems = (*env)-> GetIntArrayElements(env, array, 0);
for(i=0; i< len; i++)
printf("ELEMENT %d IS %d\n", i, elems[i]);

Chinese processing

The handling of Chinese characters is often a headache, especially software developed using Java language, which is more prominent in JNI. Since all characters in Java are Unicode encoded, in local methods, such as programs written in VC, if there is no special definition, the Unicode encoding method is generally not used. In order to enable local methods to access Chinese characters defined in Java and Chinese strings generated by Java access local methods, I have defined two methods for converting each other.

Method 1: Convert Java Chinese string to local string

/**
The first parameter is the virtual machine's environment pointer
The second parameter is the Java string definition to be converted
The third parameter is the memory block of the converted string locally stored
The third parameter is the size of the memory block
*/
int JStringToChar(JNIEnv *env, jstring str, LPTSTR desc, int desc_len)
{
int len = 0;
if(desc==NULL||str==NULL)
return -1;
//In VC wchar_t is the data type used to store wide byte characters (UNICODE)
wchar_t *w_buffer = new wchar_t[1024];
ZeroMemory(w_buffer,1024*sizeof(wchar_t));
//Use GetStringChars instead of GetStringUTFChars
wcscpy(w_buffer,env->GetStringChars(str,0));
env->ReleaseStringChars(str,w_buffer);
ZeroMemory(desc,desc_len);
//Calling the character encoding conversion function (Win32 API) to convert UNICODE into ASCII encoding format string
//For the use of the function WideCharToMultiByte, please refer to MSDN
len = WideCharToMultiByte(CP_ACP,0,w_buffer,1024,desc,desc_len,NULL,NULL);
//len = wcslen(w_buffer);
if(len>0 && len<desc_len)
desc[len]=0;
delete[] w_buffer;
return strlen(desc);
}



Method 2: Convert C string into a Unicode string that Java can recognize

jstring NewJString(JNIEnv* env,LPCTSTR str)
{
if(!env || !str)
return 0;
int slen = strlen(str);
jchar* buffer = new jchar[slen];
int len = MultiByteToWideChar(CP_ACP,0,str,strlen(str),buffer,slen);
if(len>0 && len < slen)
buffer[len]=0;
jstring js = env->NewString(buffer,len);
delete [] buffer;
return js;
}




abnormal

Since Java methods are called, exception information about operations is inevitable. These exceptions cannot be caught through the exception handling mechanism of C++ itself, but JNI can obtain exception information thrown in Java through some functions. Previously, we defined a method throwExcp in the Demo class. The following will access the method and capture the exception information thrown by it. The code is as follows:

/**
Suppose we have constructed an instance of Demo obj with its class defined as cls
*/
jthrowable excp = 0; /* Exception information definition */
jmethodID mid=(*env)->GetMethodID(env,cls,”throwExcp”,”()V”);
/*If mid is 0, it means that the method definition has failed*/
jstring msg = (*env)-> CallVoidMethod(env, obj, mid);
/* After calling this method, an exception of IllegalAccessException will be thrown */
excp = (*env)->ExceptionOccurred(env);
if(excp){
(*env)->ExceptionClear(env);
//Get specific exception information by accessing excp
/*
In Java, most of the exception information is an extension class, so you can access the toString of excp
Or getMessage to get the content of exception information. Accessing these two methods is the same as the method mentioned above for how to access the class.
*/
}




Threading and synchronous access

Sometimes it is necessary to use multi-threading methods to access Java. We know that a Java virtual machine consumes the system's memory resources very much, and each virtual machine requires about 20MB of memory. In order to save resources, each thread requires that it uses the same virtual machine, so that only one virtual machine is initialized in the entire JNI program. Everyone thinks this way, but once the child thread accesses the virtual machine environment variables created by the main thread, the system will have an error dialog box and the entire program will terminate.

In fact, there are two concepts involved, namely, virtual machine (JavaVM *jvm) and virtual machine environment (JNIEnv *env). What really consumes a lot of system resources is jvm rather than env. jvm allows multiple threads to access, but env can only be accessed by the thread that creates itself, and each thread must create its own virtual machine environment env. At this time, someone will ask questions. The main thread creates the virtual machine environment env when initializing the virtual machine. To enable child threads to create their own envs, JNI provides two functions: AttachCurrentThread and DetachCurrentThread. The following code is the framework for child threads to access Java methods:

DWORD WINAPI ThreadProc(PVOID dwParam)
{
JavaVM jvm = (JavaVM*)dwParam; /* Pass the virtual machine into */
JNIEnv* env;
(*jvm)-> AttachCurrentThread(jvm, (void**)&env, NULL);
………
(*jvm)-> DetachCurrentThread(jvm);
}




time

The topic of time is a problem I encountered in actual development. When publishing a program that uses JNI, the client does not necessarily require the client to install a Java runtime environment, because the runtime environment can be packaged in the installer. In order to facilitate downloading of the packaged program, this package must be relatively small, so some unnecessary files in the JRE (Java Runtime Environment) need to be removed. However, if the calendar type in Java is used in the program, such as, etc., then there is a file that cannot be removed, and this file is [JRE]\lib\tzmappings. It is a time zone map file. Once the file is not available, you will find that the time operation often differs from the correct time by several hours. Below is a list of essential files in packaged JRE (taking the Windows environment as an example), where [JRE] is the directory of the running environment, and the relative paths between these files cannot be changed.


File name Directory
[JRE]\bin
[JRE]\bin
[JRE]\bin
[JRE]\bin
[JRE]\bin
[JRE]\bin
[JRE]\bin\classic
[JRE]\lib
tzmappings [JRE]\lib


Since there are about 10MB, a large part of the files are not needed and can be deleted according to the actual application situation. For example, if the program does not use Java Swing, it can delete all files involved in Swing and repackage them.
Android C calls JAVA method
I learned and reproduced Lao Luo’s Java to C through JNI call process, and implemented it one by one on my own platform, which is very useful. This article will verify C to JAVA calls on this basis. The call from C to JAVA has been used in the data reporting part of the GPS part of the Android system.
For testing, add the dev->init function in hardware, implement the callback resume, and create a thread in init and report it in loop. The added code is as follows:
/hardware/libhardware/include/hardware/
 
[cpp]view plaincopy
 
1 #ifndef ANDROID_HELLO_INTERFACE_H  
2 #define ANDROID_HELLO_INTERFACE_H  
3 #include <sys/>  
4 #include <hardware/>  
5 #include <>  
6 __BEGIN_DECLS  
7  
8 /*Define module ID*/
9 #define HELLO_HARDWARE_MODULE_ID "hello"  
10  
11  
12 typedef void (* hello_val_callback)(const char* val,int length);  
13 typedef pthread_t (* hello_create_thread)(const char* name, void (*start)(void *), void* arg);  
14  
15 typedef struct {  
16    /** set to sizeof(GpsCallbacks) */  
17    size_t      size;  
18    hello_val_callback hello_cb;  
19    hello_create_thread create_thread_cb;  
20 } HelloCallbacks;  
21  
22  
23  
24 /*Hardware module structure*/
25 struct hello_module_t {  
26    struct hw_module_t common;  
27 };  
28  
29 /*Hardware interface structure*/
30 struct hello_device_t {  
31    struct hw_device_t common;  
32    int fd;  
33    int (*set_val)(struct hello_device_t* dev, int val);  
34    int (*get_val)(struct hello_device_t* dev, int* val);  
35    int   (*init)( HelloCallbacks* callbacks );  
36    int (*remove)(struct hello_device_t* dev, int* val);  
37 };  
38  
39 __END_DECLS  
40  
41 #endif  

/hardware/xxxx/libhellotest/
 
[cpp]view plaincopy
 
42 #define LOG_TAG "HelloStub"  
43  
44 #include <hardware/>  
45 #include <hardware/>  
46 #include <>  
47 #include <>  
48 #include <cutils/>  
49 #include <cutils/>  
50 #include <>    
51 #include <sys/>  
52 #include <>  
53  
54  
55 #define DEVICE_NAME "/dev/hello"  
56 #define MODULE_NAME "Hello"  
57 #define MODULE_AUTHOR "zhhpudk@"  
58  
59  
60  
61 typedef struct {  
62        bool  use;  
63    HelloCallbacks            callbacks;  
64    pthread_t               thread;      
65 } HelloState;  
66  
67 static HelloState  _hello_state[1];  
68 static HelloState *hello_state = _hello_state;  
69  
70  
71 /*The device opens and closes the interface*/
72 static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);  
73 static int hello_device_close(struct hw_device_t* device);  
74  
75  
76 /* Device Access Interface*/
77 static int hello_set_val(struct hello_device_t* dev, int val);  
78 static int hello_get_val(struct hello_device_t* dev, int* val);  
79 static int hello_init(HelloCallbacks* callbacks);  
80 //static int hello_close(struct hello_device_t* dev);  
81 static int hello_remove(struct hello_device_t* dev, int* val);  
82  
83  
84 /*Module method table*/
85 static struct hw_module_methods_t hello_module_methods = {  
86    open: hello_device_open  
87 };  
88  
89 /*Module instance variable*/
90 struct hello_module_t HAL_MODULE_INFO_SYM = {  
91    common: {  
92        tag: HARDWARE_MODULE_TAG,  
93        version_major: 1,  
94        version_minor: 0,  
95        id: HELLO_HARDWARE_MODULE_ID,  
96        name: MODULE_NAME,  
97        author: MODULE_AUTHOR,  
98        methods: &hello_module_methods,  
99    }  
100 };  
101  
102 static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) {  
103    struct hello_device_t* dev;  
104    dev = (struct hello_device_t*)malloc(sizeof(struct hello_device_t));  
105      
106    if(!dev) {  
107        LOGE("Hello Stub: failed to alloc space");  
108        return -EFAULT;  
109    }  
110  
111    memset(dev, 0, sizeof(struct hello_device_t));  
112    dev-> = HARDWARE_DEVICE_TAG;  
113    dev-> = 0;  
114    dev-> = (hw_module_t*)module;  
115    dev-> = hello_device_close;  
116    dev->set_val = hello_set_val;  
117    dev->get_val = hello_get_val;  
118    dev->init=hello_init;  
119    dev->remove=hello_remove;  
120      
121  
122    if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {  
123        LOGE("Hello Stub: failed to open /dev/hello -- %s.", strerror(errno));  
124        free(dev);  
125        return -EFAULT;  
126    }  
127  
128    //*device = &(dev->common);  
129     *device = (struct hw_device_t*)dev;  
130    LOGI("Hello Stub: open /dev/hello successfully.");  
131  
132    return 0;  
133 }  
134  
135 static int hello_device_close(struct hw_device_t* device) {  
136    struct hello_device_t* hello_device = (struct hello_device_t*)device;  
137  
138    LOGI("Hello Stub: close /dev/hello successfully.");  
139  
140    if(hello_device) {  
141        close(hello_device->fd);  
142        free(hello_device);  
143    }  
144      
145    return 0;  
146 }  
147  
148 static int hello_set_val(struct hello_device_t* dev, int val) {  
149    LOGI("Hello Stub: set value %d to device.", val);  
150  
151    write(dev->fd, &val, sizeof(val));  
152  
153    return 0;  
154 }  
155  
156 static int hello_get_val(struct hello_device_t* dev, int* val) {  
157    if(!val) {  
158        LOGE("Hello Stub: error val pointer");  
159        return -EFAULT;  
160    }  
161  
162    read(dev->fd, val, sizeof(*val));  
163  
164    LOGI("Hello Stub: get value %d from device", *val);  
165  
166    return 0;  
167 }  
168  
169  
170 static void *hello_state_thread( void*  arg )  
171 {  
172    HelloState*   state = (HelloState*) arg;  
173    char val[1]={0};  
174    // now loop  
175    for (;;)  
176    {  
177    sleep(5);  
178  
179    if(!state->use)  
180        {  
181        LOGI("Hello Stub: hello thread exit");  
182        break;  
183        }  
184      
185    val[0]++;  
186    LOGI("Hello Stub: hello thread val %d .",val[0]);  
187    state->callbacks.hello_cb(val,1);  
188        }  
189     return NULL;  
190 }  
191  
192 static int hello_init(HelloCallbacks* callbacks)  
193 {  
194    HelloState*  s = _hello_state;  
195  
196    LOGI("Hello Stub: hello init.");  
197    s->callbacks = *callbacks;  
198  
199  if(callbacks!= NULL && callbacks->create_thread_cb != NULL)  
200    s->thread = s->callbacks.create_thread_cb("hello", hello_state_thread, s);  
201  
202     if (!s->thread)  
203    {  
204        LOGE("could not create hello thread: %s", strerror(errno));  
205        return -1;  
206       }  
207    s->use = true;            
208    return 0;  
209 }  
210  
211 static int hello_remove(struct hello_device_t* dev, int* val)  
212 {  
213    struct hello_device_t* hello_device = (struct hello_device_t*)dev;  
214        void *dummy;  
215    hello_state->use=false;  
216          
217    LOGI("Hello Stub: hello_remove value %d to device.", val);  
218  
219    if(hello_device) {  
220        close(hello_device->fd);  
221        free(hello_device);  
222    }  
223    return 0;  
224 }  

The GetMethodID method is implemented in JNI, and the specific code is as follows:
/frameworks/base/services/jni/com_android_server_HelloSerivce.cpp
 
[javascript]view plaincopy
 
225 #define LOG_TAG "HelloService"    
226 #include ""    
227 #include ""    
228 #include "android_runtime/"    
229 #include <utils/>    
230 #include <utils/>    
231 #include <hardware/>    
232 #include <hardware/>    
233 #include <>    
234 #include <>  
235  
236 static jobject mCallbacksObj = NULL;    
237 static jmethodID method_receive;    
238  
239 namespace android  
240 {  
241 static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {  
242    if (env->ExceptionCheck()) {  
243        LOGE("An exception was thrown by callback '%s'.", methodName);  
244        LOGE_EX(env);  
245        env->ExceptionClear();  
246    }  
247 }  
248  
249 static void hello_recval_callback(const char* buf,int len)  
250 {  
251    JNIEnv* env = AndroidRuntime::getJNIEnv();  
252    jbyteArray var = NULL;  
253    //jbyte buffer[100];  
254        jbyte *buffer;  
255        LOGI("hello recval_callback buf[0] %d  len %d",buf[0],len);  
256      
257    if(len)  
258    {  
259        var = env->NewByteArray(len);  
260        buffer = (jbyte*)calloc(len, sizeof(jbyte));    
261    }  
262  
263    if(var)  
264    {  
265        for(int i=0;i<len;i++)  
266            //buffer[i] = buf[i];  
267               *(buffer+i)=*(buf+i);  
268        env->SetByteArrayRegion(var,0,len,buffer);  
269    }  
270  
271    //Calling the java method to report data
272      env->CallVoidMethod(mCallbacksObj, method_receive,var,len);    
273  
274    if(var){  
275        env->DeleteLocalRef(var);  
276              free(buffer);    
277             buffer = NULL;    
278    }  
279    checkAndClearExceptionFromCallback(env, __FUNCTION__);  
280 }  
281  
282 static pthread_t hello_thread_callback(const char* name, void (*start)(void *), void* arg)  
283 {  
284    return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg);  
285 }  
286  
287 HelloCallbacks sHelloCallback =  
288 {  
289    sizeof(HelloCallbacks),  
290    hello_recval_callback,    
291    hello_thread_callback  
292 };  
293  
294 static void android_hello_class_init_native(JNIEnv* env, jclass clazz)    
295 {    
296    method_receive = env->GetMethodID(clazz, "Receive", "([BI)V");    
297 }    
[javascript]view plaincopy
 
298  
[javascript]view plaincopy
 
299    /*Hardware access structure defined in the hardware abstraction layer, refer to <hardware/>*/
300        struct hello_device_t* hello_device = NULL;  
301    /*Set the value of the hardware register val through the hardware access interface defined by the hardware abstraction layer*/
302        static void hello_setVal(JNIEnv* env, jobject clazz, jint value) {  
303        int val = value;  
304        LOGI("Hello JNI: set value %d to device.", val);  
305        if(!hello_device) {  
306            LOGI("Hello JNI: device is not open.");  
307            return;  
308        }  
309          
310        hello_device->set_val(hello_device, val);  
311    }  
312        /*Read the value of the hardware register val through the hardware access interface defined by the hardware abstraction layer*/
313    static jint hello_getVal(JNIEnv* env, jobject clazz) {  
314        int val = 0;  
315        if(!hello_device) {  
316            LOGI("Hello JNI: device is not open.");  
317            return val;  
318        }  
319        hello_device->get_val(hello_device, &val);  
320          
321        LOGI("Hello JNI: get value %d from device.", val);  
322      
323        return val;  
324    }  
325        /*Open the hardware device through the hardware module defined by the hardware abstraction layer*/
326    static inline int hello_device_open(const hw_module_t* module, struct hello_device_t** device) {  
327        return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);  
328    }  
329        /* Load the specified hardware abstraction layer module through the hardware module ID and open the hardware*/
330    static jboolean hello_init(JNIEnv* env, jobject clazz) {  
331        hello_module_t* module;  
332  
333  
334            // this must be set before calling into the HAL library  
335            if (!mCallbacksObj)  
336                mCallbacksObj = env->NewGlobalRef(clazz);  
337      
338          
339        LOGI("Hello JNI: initializing......");  
340        if(hw_get_module(HELLO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) {  
341            LOGI("Hello JNI: hello Stub found.");  
342            if(hello_device_open(&(module->common), &hello_device) == 0) {  
343                LOGI("Hello JNI: hello device is open.");  
344  
345                if(hello_device->init(&sHelloCallback)==0){  
346                        LOGI("Hello JNI: Init HelloCallback.");                  
347                            return true;  
348                    }  
349                LOGE("Hello JNI : Init HelloCallback Error");  
350                return false;  
351            }  
352            LOGE("Hello JNI: failed to open hello device.");  
353            return false;  
354        }  
355        LOGE("Hello JNI: failed to get hello stub module.");  
356        return false;        
357    }  
358  
359    static jboolean hello_remove(JNIEnv* env, jobject clazz) {  
360        int val = 0;  
361        if(!hello_device) {  
362            LOGI("Hello JNI: device is not open.");  
363            return false;  
364        }  
365        LOGI("hello_close 111");      
366        if(hello_device->remove(hello_device,&val)!=0)  
367            return false;  
368        else  
369            return true;  
370 //      LOGI("hello_close");          
371        //return true;  
372    }  
373        /*JNI method table*/
374    static const JNINativeMethod method_table[] = {  
375            {"class_init_native", "()V", (void *)android_hello_class_init_native},  
376        {"init_native", "()Z", (void*)hello_init},  
377            {"remove_native", "()Z", (void*)hello_remove},  
378        {"setVal_native", "(I)V", (void*)hello_setVal},  
379        {"getVal_native", "()I", (void*)hello_getVal},  
380    };  
381        /*Register JNI method*/
382    int register_android_server_HelloService(JNIEnv *env) {  
383            return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table));  
384    }  
385 };  

Implement the Receive method in Java and callback implementation:
/framework/base/services/java/com/android/server/
 
[javascript]view plaincopy
 
386 package ;  
387 import ;  
388 import ;  
389 import ;  
390 import ;  
391 import ;  
392  
393 public class HelloService extends {  
394          
395    private static final String TAG = "HelloService";  
396  
397    private static Context mContext = null;  
398  
399      
400      
401    HelloService(Context context)  
402    {  
403        mContext = context;  
404    }  
405  
406    public void setVal(int val) {  
407        setVal_native(val);  
408        ("Hello Test", "Setval");    
409    }    
410    public int getVal() {  
411        ("Hello Test", "getval");  
412        return getVal_native();      
413    }  
414  
415    public boolean hello_init() {  
416        ("Hello Test","init");  
417        return init_native();        
418    }  
419  
420    public void hello_close(){  
421        ("Hello Test","close");  
422             remove_native();  
423         return;  
424    }  
425  
426    private void Receive(byte[] buffer,int length){    
427       //  
428        ("Hello Test", "buffer "+(buffer[0]&0xFF)+" length "+length);    
429    }    
430  
431       static { class_init_native(); }  
432          
433    private static native boolean init_native();  
434    private static native boolean remove_native();  
435    private static native void class_init_native();  
436        private static native void setVal_native(int val);  
437    private static native int getVal_native();  
438 };  

Implement aidl interface, /frameworks/base/core/java/android/os/
 
[javascript]view plaincopy
 
439 package ;    
440    
441 interface IHelloService {    
442    boolean hello_init();  
443    void hello_close();  
444    void setVal(int val);    
445    int getVal();    
446 }    

Package implementation

 
[javascript]view plaincopy
 
447 package ;  
448  
449 import ;  
450 import ;  
451 import ;  
452 import ;  
453 import ;  
454 import ;  
455 import ;  
456 import ;  
457 import ;  
458 import ;  
459 import ;  
460  
461 public class Hello extends Activity implements OnClickListener {  
462    private final static String LOG_TAG = "";  
463      
464    private IHelloService helloService = null;  
465  
466    private EditText valueText = null;  
467    private Button readButton = null;  
468    private Button writeButton = null;  
469    private Button clearButton = null;  
470      
471    /** Called when the activity is first created. */  
472    @Override  
473    public void onCreate(Bundle savedInstanceState) {  
474        (savedInstanceState);  
475        setContentView();  
476  
477    helloService = (  
478        ("hello"));  
479  
480       try {  
481        helloService.hello_init();  
482    } catch (RemoteException e) {  
483        (LOG_TAG, "Remote Exception while init_native device.");  
484    }  
485          
486        valueText = (EditText)findViewById(.edit_value);  
487        readButton = (Button)findViewById(.button_read);  
488        writeButton = (Button)findViewById(.button_write);  
489        clearButton = (Button)findViewById(.button_clear);  
490  
491    (this);  
492    (this);  
493    (this);  
494          
495        (LOG_TAG, "Hello Activity Created");  
496    }  
497      
498    @Override  
499    public void onClick(View v) {  
500        if((readButton)) {  
501        try {  
502                int val = ();  
503                String text = (val);  
504                (text);  
505        } catch (RemoteException e) {  
506            (LOG_TAG, "Remote Exception while reading value from device.");  
507        }        
508        }  
509        else if((writeButton)) {  
510        try {  
511                String text = ().toString();  
512                int val = (text);  
513            (val);  
514        } catch (RemoteException e) {  
515            (LOG_TAG, "Remote Exception while writing value to device.");  
516        }  
517        }  
518        else if((clearButton)) {  
519            String text = "";  
520            (text);  
521        //       try {  
522        //          helloService.hello_close();  
523        //      } catch (RemoteException e) {  
524        //          (LOG_TAG, "Remote Exception while Destroy device.");  
525        //      }  
526        }  
527    }  
528  
529  
530    public void onDestroy() {  
531        (LOG_TAG, "Hello Activity onDestroy called!");    
532       try {  
533        helloService.hello_close();  
534    } catch (RemoteException e) {  
535        (LOG_TAG, "Remote Exception while Destroy device.");  
536    }  
537        ();  
538    }    
539 }  

Note: When compiling, you must compile it through mm under the add file, then package the system, or add the added lib and apk to PRODUCT_PACKAGES in the make file before you can package it.