关于JNI
来源:互联网 发布:增值税开票软件密码 编辑:程序博客网 时间:2024/06/04 19:07
JNI
NDK开发环境的搭建
- 将NDK的路径拷贝到环境变量path中 cmd中运行ndk-build可验证是否添加成功
JNI_HelloWorld
步骤
- 1.创建Android工程
- 2.java代码中声明native方法
- 3.在工程根目录下创建jni文件夹,编写c代码,名字要对应
- 4.编写Android.mk文件
- 5.NDK编译生成动态链接库
- 6.java代码load动态库,调用native代码
实现
- 通过一个点击事件来调用C代码
- 在Activity中添加方法
public native String hello();
- 在该项目的根目录下添加文件夹 名为jni
- 在jni文件夹下添加文件 hello.c(命名与Activity中的native方法名一样)
- hello.c文件中的代码:
- 在项目的根目录下启动cmd ndk-build,此时会出现错误Android NDK: Your APP_BUILD_SCRIPT points to an unknown file: ./jni/Android.mk。缺少Android.mk文件
- 在jni目录下创建Android.mk文件并添加
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= hello-jni//要生成的动态库
LOCAL_SRC_FILES := hello-jni.c//源文件include $(BUILD_SHARED_LIBRARY)
修改LOCAL_MODULE和LOCAL_SRC_FILES然后在 ndk-build 编译
[armeabi] Compile thumb : helloJNI <= hello.c
[armeabi] SharedLibrary : libhelloJNI.so
[armeabi] Install: libhelloJNI.so => libs/armeabi/libhelloJNI.so
生成动态库
- java代码点击事件中 添加 System.loadLibrary(“helloJNI”);
- 库的名称是 libhelloJNI.so 将lib和.so去掉 就可以
Javah生成头文件
- 如果将native方法名改为public native String hello_from_jni();那么在c代码中的方法名是这样的:
jstring Java_com_example_jnihello_MainActivity_hello_from_jni(JNIEnv* env,jobject jobj)
ndk-build编译也不会报错,但是运行的时候会崩溃
No implementation found for native Lcom/example/jnihello/MainActivity;.hello_from_jni:()Ljava/lang/String;
在c代码中将Java的全类名中的.变为_ ,原来有下划线的地方需要在后面加个1,改成这样的:
jstring Java_com_example_jnihello_MainActivity_hello_1from_1jni(JNIEnv* env,jobject jobj)
但是如果native方法中有很多下划线和数字的话,这样修改起来就会显得很麻烦,需要一个便捷的方法来实现–javah
- JDK1.6在工程的bin/classes目录下使用
- JDK1.7在工程的src目录下使用
使用方法:javah+空格+java全类名
- 例如:native方法名为
public native String hello_1_1_23_from_jni();
在src目录下启动cmd,输入Javah com.example.jnihello.MainActivity
就行了。将生成的.h文件移动到jni目录下。 - 在c代码中原来的
#include<jni.h>
修改为#include "com_example_jnihello_MainActivity.h"
并把c代码中的方法名修改为com_example_jnihello_MainActivity.h
中的方法名
- 例如:native方法名为
常见错误
- 缺少Android.mk文件报错
- c文件没有include导入jni.h的头文件
- 方法的形参没有指定名称
- 没有动态加载.so文件
- 加载.so时名字写错
- 不同cpu需要编译不用的.so文件
- 针对不同cup编译需要Application.mk文件(jni目录下)
For example, to support hardware FPU instructions on ARMv7 based devices, use:
APP_ABI := armeabi-v7a
Or to support ARMv8 AArch64 instruction set, use:
APP_ABI := arm64-v8a
Or to support the IA-32 instruction set, use:
APP_ABI := x86
Or to support the Intel64 instruction set (r1), use:
APP_ABI := x86_64
Or to support the MIPS32 instruction set, use:
APP_ABI := mips
Or to support the MIPS64 instruction set (r6), use:
APP_ABI := mips64
Or to support all at the same time, use:
APP_ABI := armeabi armeabi-v7a x86 mips arm64-v8a x86_64 mips64
Or even better, since NDK r7, you can also use the special value ‘all’ which means “all ABIs supported by this NDK release”:
APP_ABI := all
Android.mk文件
#$:调用系统的工具链函数,当前的作用:调用当前目录LOCAL_PATH := $(call my-dir)#清除LOCAL环境变量并且初始化工具链工具,但是不会清除LOCAL_PATHinclude $(CLEAR_VARS)#要生成的.so文件名称,前面省略lib可以不写,但是后缀不需要写LOCAL_MODULE := hello-jni#指向源文件,多个源文件用空格连接LOCAL_SRC_FILES := hello-jni.c#编译成动态链接库BUILD_SHARED_LIBRARY,后缀.so ,文件比较小#BUILD_STATIC_LIBRARY编译成静态链接库,后缀.a,文件比较大include $(BUILD_SHARED_LIBRARY)
NDK简便开发流程
- Windows ->Preferences->NDK->添加NDK路径
一开始我的Eclipse没有NDK这个选项,百度之后解决。
新建Android Progect
- 右键Android Tools ->Add Native Support 填上库的名字,这时候会生成jni文件夹,其中有Android.mk文件和一个cpp文件,关联源码
- 右键->Properties->C/C++ General->Paths and Sysbols->includes->add->File system->NDK安装路径->platforms->选择版本->arm->include ->确定
- 在项目工程的src文件夹下 javah 生成头文件,移动到jni目录下
- 在c文件中导入这个头文件并实现native方法
Java与C之间的数据传递
- 在C代码中打印LOG信息,需要在C代码中添加头文件
#include<android/log.h>
并使用宏定义#define LOGD(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
还需要在Android.mk文件中增加LOCAL_LDLIBS += -llog
在C代码中直接使用定义好的LOGD,并将要打印的信息传入即可。
将Java中的String类型转换成C中的char*类型
char* jstringTostring(JNIEnv* env, jstring jstr) {char* rtn = NULL;jclass clsstring = env->FindClass("java/lang/String");jstring strencode = env->NewStringUTF("utf-8");jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);jsize alen = env->GetArrayLength(barr);jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);if (alen > 0) { rtn = (char*) malloc(alen + 1); memcpy(rtn, ba, alen); rtn[alen] = 0;}env->ReleaseByteArrayElements(barr, ba, 0);return rtn;
JNI中对数组的操作
- 获取数组的长度
int size = (*env)->GetArrayLength(env, jarray);
- 获取数组中每个元素
jint* arrayElement = (*env)->GetIntArrayElements(env, jarray, JNI_FALSE);
for (i = 0; i < size;i++){
jint s = *(arrayElement + i);
}- 获取数组的长度
C调用Java
反射(Reflection)
- 动态获取类的信息以及动态调用对象的方法
Java反射机制主要提供的功能:
- 1.在运行时判断任意一个对象所属的类
- 2.在运行时构造任意一个类的对象
- 3.在运行是判断任意一个类所具有的成员变量和方法
- 4.在运行是调用任意一个对象的方法
Java Reflection API简介
- Class类:代表一个类,位于java.lang包下。
- Field类:代表类的成员变量(成员变量也称为类的属性)。
- Method类:代表类的方法。
- Constructor类:代表类的构造方法。
- Array类:提供了动态创建数组,以及访问数组的元素的静态方法。
反射步骤:
- 1.得到字节码文件
- 2.得到字节码对应的方法
- 3.得到这个类的实例
- 4.执行方法
MainActivity.java
package com.example.ccalljavademo;import android.app.Activity;import android.os.Bundle;import android.view.View;public class MainActivity extends Activity { private JNI jni; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); jni = new JNI(); } public void helloFromJava(View v) { jni.cMethods_call_helloFromJava(); } public void add(View v) { jni.cMethods_call_add(); } public void printString(View v) { jni.cMethods_call_printString("sad"); } public void callStatic(View cv) { jni.cMethods_call_sayHello(); }}
- JNI.java
package com.example.ccalljavademo;public class JNI { { System.loadLibrary("CCallJavaDemo"); } /** * C调用Java空方法 */ public void helloFromJJava() { System.out.println("Hello Form Java"); } /** * C调用Java两个int参数方法 * @param x * @param y * @return */ public int add(int x, int y) { int result = x + y; System.out.println("被c调用" + result); return result; } /** * C调用Java参数为String方法 * @param s */ public void printString(String s) { System.out.println(s); } /** * 静态方法 */ public static void sayHello (){ System.out.println("Hello"); } /** * 让C语言调用JNI.java中的 helloFromJava */ public native void cMethods_call_helloFromJava(); /** * 调用JNI.java中的add方法 */ public native void cMethods_call_add(); /** * 调用JNI.java中的printString方法 * @param s */ public native void cMethods_call_printString(String s); /** * 调用静态方法 */ public native void cMethods_call_sayHello();}
- CCallJavaDemo.c
#include "com_example_ccalljavademo_JNI.h"#include <stdio.h>#include<Stdlib.h>/** * 让c语言调用Java中的HelloFromJava */JNIEXPORT void JNICALL Java_com_example_ccalljavademo_JNI_cMethods_1call_1helloFromJava( JNIEnv *env, jobject jobj) {// 1.得到字节码文件// jclass (*FindClass)(JNIEnv*, const char*);// 第二个参数 是全类名 com.example.ccalljavademo.JNI 将.换成/ jclass jclazz = (*env)->FindClass(env, "com/example/ccalljavademo/JNI");// 2.得到字节码对应的方法// jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);// 第三个参数:方法名// 第四个参数:方法签名 jmethodID jmethodid = (*env)->GetMethodID(env, jclazz, "helloFromJJava", "()V");// 3.得到类的实例// jobject (*AllocObject)(JNIEnv*, jclass); jobject obj = (*env)->AllocObject(env, jclazz);// 4.执行方法// void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); (*env)->CallVoidMethod(env, obj, jmethodid); //成功调用JNI.java中的helloFromJava方法}/*** *C调用Java中带有两个int参数的方法 * */JNIEXPORT void JNICALL Java_com_example_ccalljavademo_JNI_cMethods_1call_1add( JNIEnv *env, jobject jobj) { // 1.得到字节码文件 // jclass (*FindClass)(JNIEnv*, const char*); // 第二个参数 是全类名 com.example.ccalljavademo.JNI 将.换成/ jclass jclazz = (*env)->FindClass(env, "com/example/ccalljavademo/JNI"); // 2.得到字节码对应的方法 // jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); // 第三个参数:方法名 // 第四个参数:方法签名 jmethodID jmethodid = (*env)->GetMethodID(env, jclazz, "add", "(II)I"); // 3.得到类的实例 // jobject (*AllocObject)(JNIEnv*, jclass); jobject obj = (*env)->AllocObject(env, jclazz); // 4.执行方法 // jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...); (*env)->CallIntMethod(env, obj, jmethodid, 99, 12); //成功调用JNI.java中的add方法}JNIEXPORT void JNICALL Java_com_example_ccalljavademo_JNI_cMethods_1call_1printString( JNIEnv *env, jobject jobj, jstring jstr) { // 1.得到字节码文件 // jclass (*FindClass)(JNIEnv*, const char*); // 第二个参数 是全类名 com.example.ccalljavademo.JNI 将.换成/ jclass jclazz = (*env)->FindClass(env, "com/example/ccalljavademo/JNI"); // 2.得到字节码对应的方法 // jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); // 第三个参数:方法名 // 第四个参数:方法签名 jmethodID jmethodid = (*env)->GetMethodID(env, jclazz, "printString", "(Ljava/lang/String;)V"); // 3.得到类的实例 // jobject (*AllocObject)(JNIEnv*, jclass); jobject obj = (*env)->AllocObject(env, jclazz); // 4.执行方法 // void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); (*env)->CallVoidMethod(env, obj, jmethodid, jstr); //成功调用JNI.java中的add方法}JNIEXPORT void JNICALL Java_com_example_ccalljavademo_JNI_cMethods_1call_1sayHello( JNIEnv *env, jobject jobj) { // 1.得到字节码文件 // jclass (*FindClass)(JNIEnv*, const char*); // 第二个参数 是全类名 com.example.ccalljavademo.JNI 将.换成/ jclass jclazz = (*env)->FindClass(env, "com/example/ccalljavademo/JNI"); // 2.得到字节码对应的方法 // jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); // 第三个参数:方法名 // 第四个参数:方法签名 jmethodID jmethodid = (*env)->GetStaticMethodID(env, jclazz, "sayHello", "()V"); // 3.得到类的实例 // jobject (*AllocObject)(JNIEnv*, jclass);// jobject obj = (*env)->AllocObject(env, jclazz); // 4.执行方法// void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...); (*env)->CallStaticVoidMethod(env, jclazz, jmethodid);}
- 关于JNI
- 关于JNI
- 关于JNI
- 关于JNI
- 关于JNI
- 关于JNI (转载)
- 关于java的JNI
- 关于C++开发JNI
- 转关于JNI使用
- 关于JNI技术
- 关于Java的JNI
- 关于JNI杂记
- 关于JNI的解析
- 关于JNI 资源释放
- 关于JNI内存泄露问题
- 关于JNI的本地引用
- 关于jni 找不到so文件
- 关于JNI的一点整理
- Android——Context的理解
- hdoj 1789 Doing Homework again
- FZU Problem 2227 邮票 (离散化+map+dfs)
- bzoj4542: [Hnoi2016]大数
- SwipeListView 详解 实现微信,QQ等滑动删除效果
- 关于JNI
- SQL SERVER常用函数
- 静态代码块 构造函数 静态代码块块执行顺序
- ZOJ 3323(B)模拟
- 正則表達式常用一
- php字符串截取函数
- 漫游Kafka实战篇之客户端编程实例
- 金格签章使用时碰到的奇葩问题解决
- 新编日语第四册(修订版)