在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