Android NDK Development (v)--c code callback Java code

Source: Internet
Author: User

Reprint Please specify source:http://blog.csdn.net/allen315410/article/details/41862479

In the previous blog on the Java layer is how to pass data to C layer code, and familiar with most of the actual development knowledge, basically master these can do a basic NDK development, but just understand Java callback C layer of data is not enough Ah, consider the problem to consider reversibility, Java can callback C , can c in turn callback Java? The answer is yes, and this blog describes how a C language can invoke Java layer code. Here are some problem scenarios where we analyze the implementation process with this problem scenario.

Scenario 1: After the C language layer in development has completed a series of operations, it is necessary to notify the Java layer code what it needs to do at this point.

Scenario 2: Everyone knows that programmers are lazy, the Java code encapsulates a lot of methods, C programmers do not want to repeat the complex logic, then want to use the C language callback method in Java layer code.

Well, with the above scenario, we'll build a small demo below to try to solve these business scenario problems.


Create a project, define Java methods and native methods within the project
Package Com.example.ndkcallback;public class Dataprovider {/** * C calls Java empty method */public void Nullmethod () { System.out.println ("Hello from Java");} /** * C calls a 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 a method in Java with a parameter of String *  * @param s */public void printstring (String s) {System.out.println ("Java" + s);} Local method public native void CallMethod1 ();p ublic native void CallMethod2 ();p ublic native void CallMethod3 ();}

Compiling the header file

Under DOS command line, switch to the SRC directory where the project directory resides, compile the C language function signature using the Javah command. It is also important to note that since I am using the JDK version 1.7, you have to switch to the project directory/SRC directory to execute Javah, if you are using JDK 1.6 or JDK 1.5, then switch to the project directory/classes directory, execute Javah command.


Note: When using the Javah command, you need to specify the-encoding utf-8 parameter to prevent compilation of garbled errors, the following is the compiled header file:

/* do not EDIT This file-it are machine generated */#include <jni.h>/* Header for class Com_example_ndkcallback_dataprovider */#if Ndef _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_exa Mple_ndkcallback_dataprovider * METHOD:CALLMETHOD2 * Signature: () V */jniexport void Jnicall java_com_example_ndkcallb ACK_DATAPROVIDER_CALLMETHOD2 (jnienv *, jobject);/* * Class:com_example_ndkcallback_dataprovider * method:callmeth OD3 * Signature: () V */jniexport void Jnicall java_com_example_ndkcallback_dataprovider_callmethod3 (jnienv *, jobject); #ifdef __cplusplus} #endif #endif 
Writing C code

With the above header file, the next is the most difficult to engage in C code, according to the routine, first of all the above compiled header file cut into the JNI directory, Create a new HELLO.C C code file in this directory, copy the function signature of the header file you just introduced to the HELLO.C, then the log header file is introduced first, the log log input is defined, and then the C code is compiled, as follows:

#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 Java_ COM_EXAMPLE_NDKCALLBACK_DATAPROVIDER_CALLMETHOD1 (JNIENV * env, Jobject obj) {//Call Java's Empty method//1 in the C language. Find the Java code native method where the bytecode file//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 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 java_com_example_ndkcallback_dataprovider_callmethod2 (jnienv * env, Jobject obj) {//1. Find the Java code native method where the bytecode file//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 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 Java code native method where the bytecode file//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 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: The following 3 important steps are required when writing C code:

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

Jclass (*findclass) (jnienv*, const char*);

The 1th parameter is the Jninativeinterface pointer env, the 2nd parameter is the class full pathname of the Java method, and the path is distinguished by "/", and cannot be used.

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

Gets the non-static method ID:

Jmethodid (*getmethodid) (jnienv*, Jclass, const char*, const char*);

Get static method ID:

Jmethodid   (*getstaticmethodid) (jnienv*, Jclass, const char*, const char*);

The 1th parameter is Jninativeinterface's pointer env, the 2nd parameter is a Java bytecode file, the 3rd parameter is the method name in Java, and the fourth parameter is the signature of the corresponding method in Java.

3. Calling methods

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__;
The 1th parameter is the Jninativeinterface pointer env, the 2nd parameter is the Java object obj, and the 3rd parameter is the one found in the corresponding Java method, and the 4th parameter is the parameter received by the method. Listed here is a common method, jni.h in the Jninativeinterface provides a number of methods to callback Java methods, for more information please refer to jni.h this file.


To view method signatures using the JAVAP command

The JDK provides us with a tool that can view the local signature of a method from a Java bytecode file, which is the JAVAP, which, before use, switches the path to the directory where the Java bytecode file in the project resides in the cmd DOS command line.

Command format: JAVAP-S package name. The Java class name where the method resides


, the yellow label is the method name, which is the 3rd parameter in (*getmethodid) (jnienv*, Jclass, const char*, const char*), and red is the method signature, 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

Compiling C code

First switch to the current project directory in Cygwin, execute "ndk-build clean" and "ndk-build" command


Calling the Nattive method in Java

public class Mainactivity extends Activity implements Onclicklistener {static {///load 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); Btn1.setonclicklistener (This), Btn2.setonclicklistener (This), Btn3.setonclicklistener (this);p Rovider = new Dataprovider ();} @Overridepublic void OnClick (View v) {switch (V.getid ()) {case R.ID.BTN1://C callback null method in Java provider.callmethod1 (); break; Case R.ID.BTN2://C callback Java method with 2 int parameters provider.callmethod2 (); Break;case r.id.btn3:// C Callback Java method with string parameter provider.callmethod3 (); break;default:break;}}
Test


Note: In log of the following test, Green is the log generated by Java, and Blue is the log generated by C.

Test 1:C callback Null method in Java


Test 2:c callback Java method with 2 int parameters


Test 3:c callback Java method with string parameter



In addition: the native code is not in the same class as the calling Java code

In the Android project built above, the native code and the calling Java code are placed in the same Dataprovider class, so it is very convenient to call Java code in C code. However, usually in development we do not necessarily do so, a project in the Java file a lot, if you define the native method in the other Java files, and then to the other Java class Java method, what will happen in this case? With this in doubt, we define a native method in the Mainactivity.java file that calls the Nullmethod method of the Dataprovider class as a native method.

In Mainactivity.java, we define such a method:

Private native void CallMethod4 ();
Switch to this SRC directory Javah get the function signature, copy the resulting signature header file to the JNI directory, reference the 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 Java code native method where the bytecode file//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 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 the compilation run, the error


The actual run time, the program crashes directly, view the log found, the bytecode class found, method methods found, but that is not the method, it is clear that the method is the way to execute the code out of a bug, the following is called Method execution code:

(*env)->callvoidmethod (env, obj, METHOD4);
So why is this line of code an error? Take a closer look at the 2nd parameter of the Callvoidmethod method, obj, which is the Jobject type, and the default is the object of the class where the Java native method is located, which is the object of the Mainactivity class. But the Java method actually called by this native method exists in the Nullmethod of the Dataprovider class, and invoking Nullmethod obviously requires the use of the Dataprovider class object. Anyway, just a word: obj object is not correct, need Java method corresponding object, namely Dataprovider.

Know the problem, you can start to solve the problem. In Jni.h's header file, Jninativeinterface provides a way to help us find the corresponding object through bytecode Jclass:

Jobject     (*allocobject) (jnienv*, Jclass);
The 1th parameter of this method is Jninativeinterface, the 2nd parameter is Jclass, and the return value is Jobject. We'll take this method to get Jobject, to Callvoidmethod:

JNIEXPORT void Jnicall JAVA_COM_EXAMPLE_NDKCALLBACK_MAINACTIVITY_CALLMETHOD4 (jnienv * env, Jobject obj) {//1. Find the Java code native method where the bytecode file//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 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) via 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, recompile the C code file, refresh and clean the project, after running:

Indicates that the native method CallMethod4 has been successfully run.


Please download the source code here


Android NDK Development (v)--c code callback Java code

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.