Android NDK Development (iv) -- transfer data to C and androidndk in Java
Reprinted please indicate the source:Http://blog.csdn.net/allen315410/article/details/41845701
The previous articles introduced the simple concepts, common errors, and handling of Android NDK development and a simple JNI development example starting from the first Hello World, we have a conceptual understanding of NDK development, so we need to go deeper into the development of NDK, we know that NDK development is to use the "protocol" of JNI to build a "bridge" between Java and C, and to combine Java and Native C, let Java and C's direct data exchange. Speaking of data calls between Java and C, how does Java transmit data to C? How does C return the processed data to Java after obtaining the data processing? Let's take a look at how Java transfers data to C.
1. Create an Android project, create a DataProvider class under the project, and define three native methods in this class, as shown below:
Package com. example. ndktransferdata; public class DataProvider {/*** transmits the int values in two java classes to the C language. After the C language completes processing, return the added result to java ** @ param x * @ param y * @ return */public native int add (int x, int y ); /*** pass the String in java to the C language. After obtaining the C language, add a hello string to the String, return to java ** @ param s * @ return */public native String sayHelloInC (String s);/*** to pass an int array in java to C language, the C language receives this array and adds 10 to each element in the int array, then return to Java ** @ param iNum * @ return */public native int [] intMethod (int [] iNum );}2. Use Javah to compile the header file
To achieve this step found a problem, from the description should be the Encoding Error, here the error uses GBK to compile the java file, changed to the UTF-8 is no problem, you only need to add-encoding UTF-8 after the javah command to provide the encoding environment for the compiler. The following is the correct result:
It indicates that the function signature of the native code has been generated. We cut the header file of the generated function signature to the jni directory and reference this header file in the c code.
/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_example_ndktransferdata_DataProvider */#ifndef _Included_com_example_ndktransferdata_DataProvider#define _Included_com_example_ndktransferdata_DataProvider#ifdef __cplusplusextern "C" {#endif/* * Class: com_example_ndktransferdata_DataProvider * Method: add * Signature: (II)I */JNIEXPORT jint JNICALL Java_com_example_ndktransferdata_DataProvider_add (JNIEnv *, jobject, jint, jint);/* * Class: com_example_ndktransferdata_DataProvider * Method: sayHelloInC * Signature: (Ljava/lang/String;)Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_com_example_ndktransferdata_DataProvider_sayHelloInC (JNIEnv *, jobject, jstring);/* * Class: com_example_ndktransferdata_DataProvider * Method: intMethod * Signature: ([I)[I */JNIEXPORT jintArray JNICALL Java_com_example_ndktransferdata_DataProvider_intMethod (JNIEnv *, jobject, jintArray);#ifdef __cplusplus}#endif#endif
3. Write C code
Previously, we compiled the header file of the function signature in step 2, so we need to use the method signature in the header file here. There are three such native functions in total, the implementation in the function is as follows:
# Include <stdio. h> # include <jni. h> # include <malloc. h> # include <string. h> # include "com_example_ndktransferdata_DataProvider.h" # include <android/log. h> # define LOG_TAG "System. out. c "# define LOGD (...) _ android_log_print (ANDROID_LOG_DEBUG, LOG_TAG, _ VA_ARGS _) # define LOGI (...) _ android_log_print (ANDROID_LOG_INFO, LOG_TAG, _ VA_ARGS __) /*** return value char ** indicates the first address of the char array * Jstring2CStr converts the jstring type in java into a char string */char * Jstring2CStr (JNIEnv * env, jstring jstr) {char * rtn = NULL; jclass clsstring = (* env)-> FindClass (env, "java/lang/String "); // Stringjstring strencode = (* env)-> NewStringUTF (env, "GB2312"); // obtain a java string "GB2312" jmethodID mid = (* env) -> GetMethodID (env, clsstring, "getBytes", "(Ljava/lang/String;) [B"); // [String. getBytes ("gb2312"); jbyteArray barr = (jbyteArray) (* env)-> CallObjectMethod (env, jstr, mid, strencode); // String. getByte ("GB2312"); jsize alen = (* env)-> GetArrayLength (env, barr); // The length of the byte array jbyte * ba = (* env) -> GetByteArrayElements (env, barr, JNI_FALSE); if (alen> 0) {rtn = (char *) malloc (alen + 1 ); // "\ 0" memcpy (rtn, ba, alen); rtn [alen] = 0;} (* env)-> ReleaseByteArrayElements (env, barr, ba, 0 ); // return rtn;} JNIEXPORT jint JNICALL encode (JNIEnv * env, jobject obj, jint x, jint y) {LOGD ("x = % d", x ); LOGD ("y = % d", y); return x + y;} JNIEXPORT jstring JNICALL encode (JNIEnv * env, jobject obj, jstring jstr) {char * cstr = Jstring2CStr (env, jstr); LOGD ("cstr = % s", cstr); char arr [7] = {'', 'h ', 'E', 'l', 'l', 'O', '\ 0'}; strcat (cstr, arr); LOGD ("new cstr = % s ", cstr); return (* env)-> NewStringUTF (env, cstr);} JNIEXPORT jintArray JNICALL struct (JNIEnv * env, jobject obj, jintArray jarr) {// get the length of the passed array int len = (* env)-> GetArrayLength (env, jarr); // obtain the element of the passed array, that is, the first address of the array, jint * intArr = (* env)-> GetIntArrayElements (env, jarr, 0); int I = 0; for (; I <len; I ++) {// print the array element LOGD before processing ("intArr [% d] = % d", I, intArr [I]); // traverse the array element + 10 * (intArr + I) + = 10;} return jarr ;}
4. Configure the Android. mk File
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := Hello LOCAL_SRC_FILES := Hello.c LOCAL_LDLIBS += -llog include $(BUILD_SHARED_LIBRARY)
You can simply configure the Android. mk file, but you still need to solve the version compatibility problem by creating the Application. mk file in the jni directory, and adding
APP_PLATFORM := android-8
5. compile C code
6. Process returned data in Java code
Data is transmitted to the C code in Java, and the C code is returned to Java after processing. At this time, after obtaining the data, Java can perform some of its own business operations, first, after compiling the Native code, Refresh the project, clean the project, and write the following test cases:
Public class MainActivity extends Activity implements OnClickListener {// load the local library file static {System. loadLibrary ("Hello");} private Button btn1, btn2, btn3; private DataProvider provider; @ Overrideprotected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); btn1 = (Button) findViewById (R. id. btn1); btn2 = (Button) findViewById (R. id. btn2); btn3 = (Button) findViewById (R. id. btn3); listener (this); btn2.setOnClickListener (this); btn3.setOnClickListener (this); provider = new DataProvider () ;}@ Overridepublic void onClick (View v) {switch (v. getId () {case R. id. btn1: // pass two intns to the c code int result = provider. add (3, 5); Toast. makeText (this, "added result:" + result, 0 ). show (); break; case R. id. btn2: // pass the string to the C code String str = provider. sayHelloInC ("zhang san"); Toast. makeText (this, str, 0 ). show (); break; case R. id. btn3: // pass the int array to the C code int [] arr = {1, 2, 3, 4, 5}; provider. intMethod (arr); for (int I = 0; I <arr. length; I ++) {System. out. println ("arr [" + I + "] =" + arr [I]);} break; default: break ;}}}Run the project. Note: You can only enable the arm Simulator here. If the x86 simulator is used, an error is reported when the apk is installed, because the native code only writes the versions supported by arm and does not support x86. If some errors occur during the test, please refer to the prompts in the previous article to solve them slowly. Android NDK development-Common Errors and LOG usage
Test 1: Java passes two intors to C
Logcat output:
Test 2: pass a string to C in Java
Logcat output:
Test 3: Pass the int array to C in Java
Logcat output:
Summary:
The preceding simple example shows how Java passes data to C code During ndk development. The program simply introduces three data types, int, string and int [] are far from enough because Java supports many data types. What should I do at this time? Well, with the above example, we can draw the opposite line. In my previous blog, I also emphasized the jni under the ndk decompression package. h: The importance of this file. This file not only defines the representation of Java Data Types in C language, but also looks at the source code to find a one-to-one ing relationship:
......typedef uint8_t jboolean; /* unsigned 8 bits */typedef int8_t jbyte; /* signed 8 bits */typedef uint16_t jchar; /* unsigned 16 bits */typedef int16_t jshort; /* signed 16 bits */typedef int32_t jint; /* signed 32 bits */typedef int64_t jlong; /* signed 64 bits */typedef float jfloat; /* 32-bit IEEE 754 */typedef double jdouble; /* 64-bit IEEE 754 */#elsetypedef unsigned char jboolean; /* unsigned 8 bits */typedef signed char jbyte; /* signed 8 bits */typedef unsigned short jchar; /* unsigned 16 bits */typedef short jshort; /* signed 16 bits */typedef int jint; /* signed 32 bits */typedef long long jlong; /* signed 64 bits */typedef float jfloat; /* 32-bit IEEE 754 */typedef double jdouble; /* 64-bit IEEE 754 */......
There is also a very important structure JNINativeInterface, which defines many C functions. As long as you understand the general meaning, you can try to call these functions. These functions are particularly important in native development:
...... jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize); jbyteArray (*NewByteArray)(JNIEnv*, jsize); jcharArray (*NewCharArray)(JNIEnv*, jsize); jshortArray (*NewShortArray)(JNIEnv*, jsize); jintArray (*NewIntArray)(JNIEnv*, jsize); jlongArray (*NewLongArray)(JNIEnv*, jsize); jfloatArray (*NewFloatArray)(JNIEnv*, jsize); jdoubleArray (*NewDoubleArray)(JNIEnv*, jsize); jboolean* (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*); jbyte* (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*); jchar* (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*); jshort* (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*); jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*); jlong* (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*); jfloat* (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*); jdouble* (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*);......
The source code is relatively long. If you are interested, please refer to it. Here we only paste some.
Download the source code here