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.
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.