本文属于自我复习,内容重复请勿见怪
如何在android应用的自定义底层C中调用java的代码?这是本次自我复习的计划。
首先,我得创建一个demo,结构如下:
1.创建一个接口类NativeDataProvider,他的作用是调用c方法,然后回调java方法
public class NativeDataProvider { static final String TAG = "NativeDataProvider"; private Context context; public NativeDataProvider(Context context){ this.context = context; } //调用C中的代码 public native void callCCode(); public native int callCAdd(int i,int j); public native void callCPrintString(); //在C中调用java中的org.jan.ndk.example.NativeDataProvider无参方法helloFrmJava public void helloFromJava(){ Log.i(TAG, "hello from java!"); Toast.makeText(context, "hello from java", Toast.LENGTH_SHORT).show(); } //在C中调用参数为string的方法 public void printString(String str){ Log.i(TAG, "[from java]:"+str); Toast.makeText(context, "printString->str:"+str, Toast.LENGTH_SHORT).show(); } //在c中调用int参数的方法 public int add(int i,int j){ int result = i+j; Log.i(TAG, "result is"+result); Toast.makeText(context, "add->result="+result, Toast.LENGTH_SHORT).show(); return result; } }
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := jni_demo LOCAL_SRC_FILES := jni_demo.c #增加log库 LOCAL_LDLIBS += -llog include $(BUILD_SHARED_LIBRARY)3.创建jni,也就是c代码,之前先创建NativeDataProvider的头文件,到项目目录/bin/classes下,调用 javah org.jan.ndk.example.NativeDataProvider
就会生成org_jan_ndk_example_NativeDataProvider.h
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class org_jan_ndk_example_NativeDataProvider */ #ifndef _Included_org_jan_ndk_example_NativeDataProvider #define _Included_org_jan_ndk_example_NativeDataProvider #ifdef __cplusplus extern "C" { #endif /* * Class: org_jan_ndk_example_NativeDataProvider * Method: callCCode * Signature: ()V */ JNIEXPORT void JNICALL Java_org_jan_ndk_example_NativeDataProvider_callCCode (JNIEnv *, jobject); /* * Class: org_jan_ndk_example_NativeDataProvider * Method: callCAdd * Signature: (II)I */ JNIEXPORT jint JNICALL Java_org_jan_ndk_example_NativeDataProvider_callCAdd (JNIEnv *, jobject, jint, jint); /* * Class: org_jan_ndk_example_NativeDataProvider * Method: callCPrintString * Signature: ()V */ JNIEXPORT void JNICALL Java_org_jan_ndk_example_NativeDataProvider_callCPrintString (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
其中看到有Signature:()V 这样类似的 就是 方法签名。
例如:
har* classname = "org/jan/ndk/example/NativeDataProvider";
jclass dpclazz = (*env)->FindClass(env, classname);
这是通过jnienv指针获取class对象。
jmethodID methodID = (*env)->GetMethodID(env, dpclazz, "helloFromJava", "()V");
GetMethodID指 从java中获取定义好的method方法,第二个参数是class对象,第三个参数是方法名,第四个是方法签名(可以从头文件中看Signature)
看方法签名的方法: 进入项目\bin\classes 下,命令:javap -s org.jan.ndk.example.NativeDataProvider
"()V"的大概意思就是 括号内没有参数,就是没有入参,V就是void无返回值。
下面是C主要代码(具体请看注释):
#include "org_jan_ndk_example_NativeDataProvider.h" #include <string.h> #include <android/log.h> #define LOG_TAG "System.out" #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_org_jan_ndk_example_NativeDataProvider_callCCode (JNIEnv * env, jobject obj) { //调用NativeDataProvider对象中的helloFromJava()方法 //获取到某个对象, 获取对象中的方法, 调用获取到的方法 LOGI("in code"); //NativeDataProvider完整类名 org.jan.ndk.example.NativeDataProvider char* classname = "org/jan/ndk/example/NativeDataProvider"; jclass dpclazz = (*env)->FindClass(env, classname); if(dpclazz == 0) LOGI("org.jan.ndk.example.NativeDataProvider.class not find !!!"); else LOGI("org.jan.ndk.example.NativeDataProvider.class find !!!"); //使用jni.h中提供的GetMethodID方法, 获取jmethodID, 传入参数 ①JNIEnv指针 ②Class对象 ③ 方法名 ④方法签名, 在这里方法名和方法签名确定一个方法, 方法签名就是方法的返回值 与 参数的唯一标示; //参数介绍 : 第二个参数是Class对象, 第三个参数是方法名,第四个参数是方法的签名, 获取到调用的method jmethodID methodID = (*env)->GetMethodID(env, dpclazz, "helloFromJava", "()V"); if(methodID == 0) LOGI("method helloFromJava not find !!!"); else LOGI("method helloFromJava find !!!"); /* * 调用方法 void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); * 参数介绍 : 后面的 ... 是可变参数, 如果该返回值void的方法有参数, 就将参数按照次序排列 */ LOGI("before call method"); (*env)->CallVoidMethod(env, obj, methodID); LOGI("after call method"); } JNIEXPORT void JNICALL Java_org_jan_ndk_example_NativeDataProvider_callCPrintString (JNIEnv *env, jobject obj) { //调用DataProvider对象中的helloFromJava()方法 //获取到某个对象, 获取对象中的方法, 调用获取到的方法 LOGI("in code"); //NativeDataProvider完整类名 org.jan.ndk.example.NativeDataProvider char* classname = "org/jan/ndk/example/NativeDataProvider"; jclass dpclazz = (*env)->FindClass(env, classname); if(dpclazz == 0) LOGI("class not find !!!"); else LOGI("class find !!!"); //参数介绍 : 第二个参数是Class对象, 第三个参数是方法名,第四个参数是方法的签名, 获取到调用的method jmethodID methodID = (*env)->GetMethodID(env, dpclazz, "printString", "(Ljava/lang/String;)V"); if(methodID == 0) LOGI("method not find !!!"); else LOGI("method find !!!"); /* * 调用方法 void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); * 参数介绍 : 后面的 ... 是可变参数, 如果该返回值void的方法有参数, 就将参数按照次序排列 */ LOGI("before call method"); (*env)->CallVoidMethod(env, obj, methodID, (*env)->NewStringUTF(env, "printString method callback success!!")); LOGI("after call method"); } /* * 实际开发的情况 * C代码工程师给我们 first.h first.c , 我们只需要将first.h引入, 然后就可以使用其中的方法了 */ JNIEXPORT jint JNICALL Java_org_jan_ndk_example_NativeDataProvider_callCAdd (JNIEnv *env, jobject obj, jint a, jint b) { //调用DataProvider对象中的helloFromJava()方法 //获取到某个对象, 获取对象中的方法, 调用获取到的方法 LOGI("in code test_add=>"); //NativeDataProvider完整类名 org.jan.ndk.example.NativeDataProvider char* classname = "org/jan/ndk/example/NativeDataProvider"; jclass dpclazz = (*env)->FindClass(env, classname); if(dpclazz == 0) LOGI("class not find !!!"); else LOGI("class find !!!"); //参数介绍 : 第二个参数是Class对象, 第三个参数是方法名,第四个参数是方法的签名, 获取到调用的method jmethodID methodID = (*env)->GetMethodID(env, dpclazz, "add", "(II)I"); if(methodID == 0) LOGI("method not find !!!"); else LOGI("method find !!!"); /* * 调用方法 void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); * 参数介绍 : 后面的 ... 是可变参数, 如果该返回值void的方法有参数, 就将参数按照次序排列 */ return (*env)->CallIntMethod(env, obj, methodID, a,b); }
4.编译jni,在demo目录下手动执行ndk-build
5.android的代码:
public class MainActivity extends ActionBarActivity { private static final String TAG = "MainActivity"; private static NativeDataProvider ndp; static{ System.loadLibrary("jni_demo"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ndp = new NativeDataProvider(this); if (savedInstanceState == null) { getSupportFragmentManager().beginTransaction() .add(R.id.container, new PlaceholderFragment()).commit(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } /** * A placeholder fragment containing a simple view. */ public static class PlaceholderFragment extends Fragment implements OnClickListener { private Button callVoidMethod, callStringMethod, callIntMethod; public PlaceholderFragment() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_main, container, false); callVoidMethod = (Button) rootView .findViewById(R.id.call_void_method_btn); callStringMethod = (Button) rootView .findViewById(R.id.call_string_parameter_method); callIntMethod = (Button) rootView .findViewById(R.id.call_int_parameter_method); callVoidMethod.setOnClickListener(this); callStringMethod.setOnClickListener(this); callIntMethod.setOnClickListener(this); return rootView; } @Override public void onClick(View v) { switch (v.getId()) { case R.id.call_void_method_btn: ndp.callCCode(); break; case R.id.call_string_parameter_method: ndp.callCPrintString(); break; case R.id.call_int_parameter_method: ndp.callCAdd(2,3); break; } } } }
代码示例下载:请打开如意门下载