Android NDK development (5) -- C code callback Java code, androidndk

Source: Internet
Author: User

Android NDK development (5) -- C code callback Java code, androidndk

Reprinted please indicate the source:Http://blog.csdn.net/allen315410/article/details/41862479

In the previous blog, I learned how the Java layer transmits data to the C-layer code, and I am familiar with most of the actual development knowledge. I can do a basic NDK development by mastering these skills, however, it is not enough to understand whether Java calls back data in the C layer. To solve this problem, we must consider the reversible nature. If Java can call back C, can C call back Java in turn? The answer is yes. This blog will introduce how a C language calls the Java-layer code. The following are some problem scenarios. We will analyze the implementation process with this problem scenario.

Scenario 1: after a series of operations are completed at the C language layer during development, you need to notify the Java-Layer Code of what operations are required at this time.

Scenario 2: As we all know, programmers are relatively lazy. Java code encapsulates a large number of methods. C programmers do not want to repeatedly write complicated logic, in this case, you want to use the method in Java Layer Code through C language callback.

Well, with the above scenario, we will build a small Demo below to try to solve the problems in these business scenarios.


Create a project and define the Java and Native methods in the project.
Package com. example. ndkcallback; public class DataProvider {/*** C call java empty Method */public void nullMethod () {System. out. println ("hello from java ");} /*** C calls the method with two int parameters in java ** @ param x * @ param y * @ return */public int Add (int x, int y) {int result = x + y; System. out. println ("result in java" + result); return result ;} /*** C calls the method with the String parameter in java ** @ param s */public void printString (String s) {System. out. println ("java" + s);} // local method public native void callMethod1 (); public native void callMethod2 (); public native void callMethod3 ();}

Compile header files

Under the doscommand line, switch to the src directory where the source code of the project directory is stored, and use the javah command to compile the function signature in C language. Note that because JDK 1.7 is used, you must switch to the project directory/src directory to run javah. If JDK 1.6 or JDK 1.5 is used, switch to the project directory/classes directory and execute the javah command.


Note: when using the javah command, you must specify the-encoding UTF-8 parameter to prevent gibberish errors during compilation. The following is the compiled header file:

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_example_ndkcallback_DataProvider */#ifndef _Included_com_example_ndkcallback_DataProvider#define _Included_com_example_ndkcallback_DataProvider#ifdef __cplusplusextern "C" {#endif/* * Class:     com_example_ndkcallback_DataProvider * Method:    callMethod1 * Signature: ()V */JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callMethod1(JNIEnv *, jobject);/* * Class:     com_example_ndkcallback_DataProvider * Method:    callMethod2 * Signature: ()V */JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callMethod2(JNIEnv *, jobject);/* * Class:     com_example_ndkcallback_DataProvider * Method:    callMethod3 * Signature: ()V */JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callMethod3(JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif
Write C code

With the header file above, the next step is the most difficult C Code. According to the routine, first cut the compiled header file to the jni directory, and create a new Hello under the directory. c code file, copy the function signature of the newly introduced header file to Hello. c, then introduce the LOG header file, define the LOG input, and then compile the C code, as shown below:

# Include <stdio. h> # include <jni. h> # include "com_example_ndkcallback_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 _) JNIEXPORT void JNICALL encode (JNIEnv * env, jobject obj) {// call Java's empty method in C Language // 1. find the bytecode file where the native method of java code is located // jclass (* FindClass) (JNIEnv *, const char *); jclass clazz = (* env)-> FindClass (env, "com/example/ndkcallback/DataProvider"); if (clazz = 0) {LOGD ("find class error"); return;} LOGD ("find class "); // 2. find the corresponding method in the class // jmethodID (* GetMethodID) (JNIEnv *, jclass, const char *, const char *); jmethodID method1 = (* env) -> GetMethodID (env, clazz, "nullMethod", "() V"); if (method1 = 0) {LOGD ("find method1 error"); return ;} LOGD ("find method1"); // 3. call method // void (* CallVoidMethod) (JNIEnv *, jobject, jmethodID ,...); (* env)-> CallVoidMethod (env, obj, method1); LOGD ("method1 called");} JNIEXPORT void JNICALL encode (JNIEnv * env, jobject obj) {// 1. find the bytecode file where the native method of java code is located // jclass (* FindClass) (JNIEnv *, const char *); jclass clazz = (* env)-> FindClass (env, "com/example/ndkcallback/DataProvider"); if (clazz = 0) {LOGD ("find class error"); return;} LOGD ("find class "); // 2. find the corresponding method in the class // jmethodID (* GetMethodID) (JNIEnv *, jclass, const char *, const char *); jmethodID method2 = (* env) -> GetMethodID (env, clazz, "Add", "(II) I"); if (method2 = 0) {LOGD ("find method2 error"); return ;} LOGD ("find method2"); // 3. call method // jint (* CallIntMethod) (JNIEnv *, jobject, jmethodID ,...); int result = (* env)-> CallIntMethod (env, obj, method2, 3, 5); LOGD ("result in C = % d", result );} JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callMethod3 (JNIEnv * env, jobject obj) {// 1. find the bytecode file where the native method of java code is located // jclass (* FindClass) (JNIEnv *, const char *); jclass clazz = (* env)-> FindClass (env, "com/example/ndkcallback/DataProvider"); if (clazz = 0) {LOGD ("find class error"); return;} LOGD ("find class "); // 2. find the corresponding method in the class // jmethodID (* GetMethodID) (JNIEnv *, jclass, const char *, const char *); jmethodID method3 = (* env) -> GetMethodID (env, clazz, "printString", "(Ljava/lang/String;) V"); if (method3 = 0) {LOGD ("find method3 error"); return;} LOGD ("find method3"); // 3. call method // void (* CallVoidMethod) (JNIEnv *, jobject, jmethodID ,...); (* env)-> CallVoidMethod (env, obj, method3, (* env)-> NewStringUTF (env, "haha in C. "); LOGD (" method3 called ");}
Note: When writing C code, the following three important steps are required:

1. Find the bytecode file where the native method of java code is located, which can be found in JNINativeInterface in jni. h.

jclass (*FindClass)(JNIEnv*, const char*);

Among them, the first parameter is the JNINativeInterface pointer env, and the second parameter is the full path name of the class where the java method is located. "/" is used to distinguish between paths, and "." cannot be used.

2. Find the corresponding method in the class, which can be found in JNINativeInterface in jni. h.

Obtain the non-static method id:

jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);

Obtain static method id:

jmethodID   (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);

Among them, 1st parameters are the JNINativeInterface pointer env, 2nd parameters are java bytecode files, 3rd parameters are the method names in java, and the fourth parameter is the signature of the corresponding method in java.

3. Call Method

void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);jobject     (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);jboolean    (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...);jbyte       (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);jchar       (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...);jshort      (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...);jlong       (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);jfloat      (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__;jdouble     (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__;
Among them, 1st parameters are the JNINativeInterface pointer env, 2nd parameters are java object obj, 3rd parameters are the corresponding methods found in java, and 4th parameters are the parameters received by the method. Common methods are listed here. JNINativeInterface in jni. h provides a large number of methods to call back methods in java. For details, refer to the jni. h file.


Use the javap command to view the method Signature

JDK provides us with such a tool, which can view the local signature of the method from the java bytecode file. This tool is javap, before use, first, switch the path to the directory where the java bytecode file in the project is located in the doscommand line of CMD.

Command Format: javap-s package name. Java class name of the Method


In this case, the method name is marked in yellow, which is the 3rd parameters in (* GetMethodID) (JNIEnv *, jclass, const char *, const char, the method signature is marked in red, which is the 4th parameter.


Android. mk configuration and Application. mk Configuration

    LOCAL_PATH := $(call my-dir)    include $(CLEAR_VARS)    LOCAL_MODULE    := Hello    LOCAL_SRC_FILES := Hello.c        LOCAL_LDLIBS += -llog    include $(BUILD_SHARED_LIBRARY)
APP_PLATFORM := android-8

Compile C code

Switch to the current project directory in cygwin and run the "ndk-build clean" and "ndk-build" commands.


Call the Nattive method in Java

Public class MainActivity extends Activity implements OnClickListener {static {// load the dynamic library. soSystem. 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: // c calls back the empty method provider in java. callMethod1 (); break; case R. id. btn2: // c callback java method provider with two int parameters. callMethod2 (); break; case R. id. btn3: // c callback java method provider with string parameters. callMethod3 (); break; default: break ;}}}
Test


Note: In the following test LOG, Green indicates the LOG generated by Java, and blue indicates the LOG generated by C.

Test 1: c callback java empty Method


Test 2: c callback java method with two int Parameters


Test 3: c callback java method with string Parameters



In addition, the native code and the called java code are not in the same class.

In the above-mentioned Android project, native code and called java code are placed in the same DataProvider class, which makes it very convenient to call Java code in C code. However, we usually do not necessarily do this during development. There are many java files in a project. If native methods are defined in other java files, and then call the java method in another Java class. What problems will happen in this case? With this question, we define a native method in the MainActivity. java file. This native method must call the nullMethod method of the DataProvider class.

In MainActivity. java, we define this method:

private native void callMethod4();
Switch to javah under the src directory to obtain the function signature, copy the obtained signature header file to the jni directory, reference this header file in the C file, and write the corresponding C code:

JNIEXPORT void JNICALL Java_com_example_ndkcallback_MainActivity_callMethod4 (JNIEnv * env, jobject obj) {// 1. find the bytecode file where the native method of java code is located // jclass (* FindClass) (JNIEnv *, const char *); jclass clazz = (* env)-> FindClass (env, "com/example/ndkcallback/DataProvider"); if (clazz = 0) {LOGD ("find class error"); return;} LOGD ("find class "); // 2. find the corresponding method in the class // jmethodID (* GetMethodID) (JNIEnv *, jclass, const char *, const char *); jmethodID method4 = (* env) -> GetMethodID (env, clazz, "nullMethod", "() V"); if (method4 = 0) {LOGD ("find method4 error"); return ;} LOGD ("find method4"); // 3. call method // void (* CallVoidMethod) (JNIEnv *, jobject, jmethodID ,...); (* env)-> CallVoidMethod (env, obj, method4); LOGD ("method4 called ");}
After compilation and running, an error is reported.


During actual operation, the program crashed. The log showed that the bytecode class was found and the method was found, but the method was not executed, obviously, the code for executing the method has a Bug. The following is the code for calling the method:

(*env)->CallVoidMethod(env, obj, method4);
Why is an error reported in this line of code? Take a closer look. The 2nd parameter obj of the CallVoidMethod method, which belongs to the jobject type, is the object of the class where the java native method is located by default, and is the object of the MainActivity class, however, the java method actually called by this native method exists in the nullMethod of the DataProvider class. To call nullMethod, it is obvious that the object of the DataProvider class must be used. One sentence: The obj object is incorrect. The object corresponding to the java method is required, that is, DataProvider.

Once you know the problem, you can solve it. In the header file of jni. h, JNINativeInterface provides such a method to help us find the corresponding object through the bytecode jclass:

jobject     (*AllocObject)(JNIEnv*, jclass);
In this method, the first parameter is JNINativeInterface, the second parameter is jclass, And the return value is jobject. Take this method to get jobject and pass it to CallVoidMethod:

JNIEXPORT void JNICALL Java_com_example_ndkcallback_MainActivity_callMethod4 (JNIEnv * env, jobject obj) {// 1. find the bytecode file where the native method of java code is located // jclass (* FindClass) (JNIEnv *, const char *); jclass clazz = (* env)-> FindClass (env, "com/example/ndkcallback/DataProvider"); if (clazz = 0) {LOGD ("find class error"); return;} LOGD ("find class "); // 2. find the corresponding method in the class // jmethodID (* GetMethodID) (JNIEnv *, jclass, const char *, const char *); jmethodID method4 = (* env) -> GetMethodID (env, clazz, "nullMethod", "() V"); if (method4 = 0) {LOGD ("find method4 error"); return ;} LOGD ("find method4"); // 3. get jobject // jobject (* AllocObject) (JNIEnv *, jclass) through jclass; jobject jobj = (* env)-> AllocObject (env, clazz ); if (jobj = 0) {LOGD ("find jobj error"); return;} LOGD ("find jobj"); // 4. call method // void (* CallVoidMethod) (JNIEnv *, jobject, jmethodID ,...); (* env)-> CallVoidMethod (env, jobj, method4); LOGD ("method4 called ");}
After writing the code, re-compile the C code file, Refresh and clean the project. After running:

It indicates that the native method callMethod4 has been run successfully.


Download the source code here


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.