本文不涉及android-ndk开发环境搭。
步骤一:新建一个APP,名称为HelloJNI,然后定义一个类(将会在native代码中调用和访问该类):
package com.example.hellojni;
public class JNITestBean {
private int initField;
public String firstStr = null;
public static String secondStr = null;
public JNITestBean(){
}
public String getFirstString(String str){
firstStr = str;
return firstStr;
}
public static String getSecondString(String str){
secondStr = str;
return secondStr;
}
public int getInitField(){
return initField;
}
}
步骤二:在MainActivity中定义native方法:
MainActivity的全路径名是:com.example.hellojni.MainActivity;
native方法是:
private native JNITestBean createJNITestObjFromNative();
//Java加载.so动态库的方式如下:
static{
System.loadLibrary(“HelloJNI”);
}
步骤三:使用javah命令生成native函数的.h头文件
首先在HelloJNI目录下,新建目录jni(不能随意命名,只能是jni)
javah -classpath bin\classes -d jni com.example.hellojni.MainActivity
注:
1)如何提示:cannot access android.app.Activity(无法找到android.app.Activity类)时,需要给classpath增加android相应平台的android.jar
javah -classpath D:\software\adt-bundle-windows-x86-20140702\sdk\platforms\android-18\android.jar;bin\classes -d jni com.example.hellojni.MainActivity
记得linux平台时需要把上面的;bin\classes改成:bin/classes
2)bin\classes目录是因为你的java代码编译后在bin下classes下面,如果你的编译后的代码直接在bin下面,就不需要添加classes目录了。
步骤四:编写C/C++代码
将生成的com_example_hellojni_MainActivity.h里面的声明函数,拷贝到自定义的C++函数中,
函数中有具体的注释,需要说明几点:
第一点:C++和C函数使用JNI函数略有不同,需要注意你用的是C++,还是C
第二点:本函数中使用了一个android native层的log函数,所以在模块编译时,需要增加依赖库
第三点:在native方法中所有对类的方法、属性的操作,都是通过反射进行的,不论是静态时还是非静态是,私有属性,公有属性都可以通过反射进行操作,但是操作的时候,需要继续名称(name)和签名(signature)。可以通过下面命令查看签名信息:
javap -s -p 类名
/*
* mynative.cpp
*
*/
#include "com_example_hellojni_MainActivity.h"
#include "stdio.h"
#include "string.h"
#include "android/log.h"
#define LOG_TAG "========MYNATIVE=========="
//#define LOGI(...)_android_log_print(ANDROID_LOG_INFO, LOG_TAG,...)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO , LOG_TAG, __VA_ARGS__)
//#define LOGE(...)_android_log_print(ANDROID_LOG_ERROR, LOG_TAG,...)
//extern int addSum(int , int);
#ifdef __cplusplus
extern "C"
{
#endif
//如果自定义的函数在“main”函数后面,则需要提前声明该函数,否则会 提示error: was not declared in this scope
int addSum(int , int );
/*
*env函数指针,指向的是jni开发环境(jni开发所有的所有函数调用都靠他)
* jobject是MainActivity调用这个方法是,传入的。本方法在Java中声明的是一个static native 这个变量就是jclass对象。一般是调用此方法的当前对象或者当前类
*/
JNIEXPORT jobject JNICALL Java_com_example_hellojni_MainActivity_createJNITestObjFromNative
(JNIEnv* env , jobject thizz)
{
jclass targetClass;
jmethodID mid;
jobject newObj;
jfieldID fid;
jstring str, str1;
jint main_acti_value;
//此处两行代码的作用是得到MainActivity的域
//targetClass = env->FindClass("MainActivity");
targetClass = env->GetObjectClass(thizz);
fid = env->GetStaticFieldID(targetClass, "filed", "I");
//通过object和方法实体,得到该方法的值
main_acti_value = env->GetStaticIntField(targetClass, fid);
LOGI("在C++方法中访问java域,得到的值是: %d \n", main_acti_value);
//下面的代码将生成JNITest对象,并返回
targetClass = env->FindClass("com/example/hellojni/JNITestBean");
if(targetClass == NULL){
return NULL;
}
//得到JNITest类的构造方法
mid = env->GetMethodID(targetClass, "<init>", "()V");
LOGI("[CPP] JNITest对象 生成 \n");
newObj = env->NewObject(targetClass, mid);
//调用对象的方法
mid = env->GetStaticMethodID(targetClass, "getSecondString", "(Ljava/lang/String;)Ljava/lang/String;");
LOGI("查找JNITest对象的getsecondstring这个方法");
int result = addSum(10 , 20);
if(mid != NULL){
LOGI("找到JNITest对象的getsecondstring这个方法");
char* tempchar = "method invoke from native\n";
str1 = env->NewStringUTF(tempchar);
//const char* strch = env->GetStringUTFChars(str, NULL);
//env->call
//将jobject对象,转化成jstring对象
str = (jstring) env->CallStaticObjectMethod(targetClass, mid, str1);
//将java中的string转化成char字符数组,以便打印输出
const char* strch = env->GetStringUTFChars(str, NULL);
LOGI("在native调用java的static方法,方法返回值是: %s \n", strch);
//释放资源
//env->ReleaseStringUTFChars(str1, tempchar);
//env->ReleaseStringUTFChars(str, strch);
}else{
LOGI("没有找到JNITest对象的getsecondstring这个方法");
}
LOGI("查找JNITest对象的initField这个属性");
//设置JNITest中的私有变量的值
fid = env->GetFieldID(targetClass, "initField", "I");
if(fid != NULL){
LOGI("设置jnitest的变量的值initfield的值为200");
main_acti_value = 200;
env->SetIntField(newObj, fid, main_acti_value);
}else{
LOGI("没有找到initfield这个属性");
}
return newObj;
}
int addSum(int a, int b)
{
return a + b;
}
#ifdef __cplusplus
}
#endif
步骤五:编写Android.mk的makefile文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := HelloJNI
LOCAL_SRC_FILES := mynative.cpp
LOCAL_LDLIBS := -lm -llog -ljnigraphics
include $(BUILD_SHARED_LIBRARY)
LOCAL_MODULE := HelloJNI 此配置表示编译生成libHelloJNI.so的动态链接库
LOCAL_SRC_FILES := mynative.cpp 此配置表示需要编译的C/C++函数
LOCAL_LDLIBS := -lm -llog -ljnigraphics 此配置表示android log函数需要依赖的库
步骤五:在项目根目录下(HelloJNI),通过ndk-build命令编译生成共享库
步骤六:在MainActivity中调用操作native层生成的JNITestBean对象.