在ndk中,使用JNI_OnLoad方法进行java本地方法与C语言组件方法进行一一映射,然后使用C组件方法调用java的静态方法与非静态方法,静态属性与非静态属性。
1.在eclipse新建androidNdkC的android工程,修改MainActivity.java代码如下
package com.undergrowth.androidndkc; import com.undergrowth.android.R; import android.os.Bundle; import android.app.Activity; import android.util.Log; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Manager manager=new Manager(MainActivity.this, 10, 20); manager.managerDisplay(); Log.d("msg", MainActivity.class.getName()); } }
含有本地方法的类 Manager.java
package com.undergrowth.androidndkc; import android.content.Context; import android.util.Log; /* * 用于提供静态方法和非静态方法 * 用于提供静态成员与非静态成员 * 提供本地方法 */ public class Manager { private Context context;//用于接受创建对象的上下文 public int numNot; public static int num; public Manager(Context context,int num,int numNot) { this.context=context; this.num=num; this.numNot=numNot; Log.d("msg", Manager.class.getName()+" is created!"); managerInit(); } //提供静态方法与非静态方法 public static void managerStaticMethod(int value) { Log.d("msg", "managerStaticMethod is called,value from c,value is "+value); } public void managerNotStaticMethod(int value) { Log.d("msg", "managerNotStaticMethod is called,value from c,value is "+value); } public native void managerInit(); //本地方法用于初始化 public native void managerDisplay(); //本地方法 用于显示 即在c语言中调用java代码 //加载共享库 static{ try { System.loadLibrary("manager"); } catch (Exception e) { e.printStackTrace(); } } }
2.然后在项目androidNdkC中新建jni文件夹 新建android.mk文件
LOCAL_PATH :=$(call my-dir) include $(CLEAR_VARS) LOCAL_LDLIBS :=-llog LOCAL_MODULE :=manager LOCAL_SRC_FILES :=manager.c include $(BUILD_SHARED_LIBRARY)
这里说一下LOCAL_LDLIBS :=-llog 表示需要在链接时用到调试的liblog.so文件
在jni中新建manager.c文件 内容如下
#include <jni.h> #include <android/log.h> #include <stdio.h> jclass cla; jmethodID aMethodId1,aMethodId2; jfieldID aFieldId1,aFieldId2; static void init(JNIEnv *env,jobject thiz) { jclass clazz=(*env)->GetObjectClass(env,thiz); //get class byte code: object.getClass() cla=(jclass)(*env)->NewGlobalRef(env,clazz); aMethodId1=(*env)->GetStaticMethodID(env,clazz,"managerStaticMethod","(I)V");//get static method aMethodId2=(*env)->GetMethodID(env,clazz,"managerNotStaticMethod","(I)V");//get non-static method aFieldId1=(*env)->GetStaticFieldID(env,clazz,"num","I"); //get static field aFieldId2=(*env)->GetFieldID(env,clazz,"numNot","I"); //get non-static field } static void display(JNIEnv *env,jobject thiz) { int num=(int)(*env)->GetStaticObjectField(env,cla,aFieldId1); int numNot=(int)(*env)->GetObjectField(env,thiz,aFieldId2); (*env)->CallStaticVoidMethod(env,cla,aMethodId1,(num+numNot)); (*env)->CallVoidMethod(env,thiz,aMethodId2,(num-numNot)); } static JNINativeMethod methods[]= { {"managerInit","()V",(void *)init }, {"managerDisplay","()V",(void *)display}, }; jint register_native_method(JNIEnv *env) { static char *className="com/undergrowth/androidndkc/Manager"; jclass clazz; clazz=(*env)->FindClass(env,className);//similar to java reflect: Class.forName() if(clazz==NULL) { __android_log_print(ANDROID_LOG_DEBUG,"msg","can't find %s class.",className); return -1; } if((*env)->RegisterNatives(env,clazz,methods,sizeof(methods)/sizeof(methods[0]))!=JNI_OK) { __android_log_print(ANDROID_LOG_DEBUG,"msg","can not register native."); return -1; } return 0; } //in order to use JNI_VERSION_1_4 ,you must JNIEXPORT JNI_OnLoad and return JNI_VERSION_1_4 jint JNI_OnLoad(JavaVM *vm,void *reserved) //when the native library load,the function willbe called by java vm { int result=-1; //-1 presentative unknown error JNIEnv *env=NULL; //current thread jni function interface if((*vm)->GetEnv(vm,(void **)&env,JNI_VERSION_1_4)!=JNI_OK) { __android_log_print(ANDROID_LOG_DEBUG,"msg","JNI_OnLoad init error!!"); return result; } if(register_native_method(env)!=0) { __android_log_print(ANDROID_LOG_DEBUG,"msg","register native failed!!"); return result; } result=JNI_VERSION_1_4; return result; }
然后在androidNdkC的目录中使用ndk-build工具 编译androidNdkC 显示信息如下:
u1@u1:~/java/workspace/androidNdkC$ ndk-build Compile thumb : manager <= manager.c SharedLibrary : libmanager.so Install : libmanager.so => libs/armeabi/libmanager.so
3.运行 在LogCat中显示如下信息
4.上面即是成功的效果 嗯 在整个过程中出了一些错误 如下
08-28 15:12:37.916: W/dalvikvm(6885): JNI WARNING: jclass arg has wrong type (expected Ljava/lang/Class;, got Lcom/undergrowth/androidndkc/Manager;) 08-28 15:12:37.944: W/dalvikvm(6885): in Lcom/undergrowth/androidndkc/Manager;.managerDisplay:()V (GetStaticObjectField)
嗯 出现如上错误 是因为 (*env)->GetStaticObjectField(env,cla,aFieldId1); 写成了 (*env)->GetStaticObjectField(env,thiz,aFieldId1);
在获取静态成员或者静态方法的时候需要使用类的字节码才能获取
08-28 15:21:18.015: W/dalvikvm(7235): JNI WARNING: GetStaticObjectField for field 'num' of expected type L, got I 08-28 15:21:18.025: W/dalvikvm(7235): in Lcom/undergrowth/androidndkc/Manager;.managerDisplay:()V (GetStaticObjectField)
出现这个错误 是因为版本问题 我试过 相同的代码 用2.2 2.3.3 4.0.3只有在2.2上是没有问题的 同时我也把数据类型换为其他的,也是相同的问题,所以想要上面的代码成功执行,必须要使用android2.2版本
JNI的函数查询: http://game.ceeger.com/Script/AndroidJNI/AndroidJNI.GetStaticFieldID.html