刚来公司,接了一个项目(具体项目不便透露),其中涉及到一个socket数据传输的问题,这里当然就不可避免的用到了.so动态库进行实现。接触过jni的应该都知道,Android调用.so库其实很简单,不就是直接static { System.loadLibrary(“aaron”); }吗?其实我救算这么想的,但是直到我拿到.so和.h文件之后才发现我错了。好了,废话不说了,进入正题:
如果.h文件中的方法名类似 Java_com_aaron_demo_JniUtil_getString ,那么恭喜你,你可以直接到最后一步(五、使用符合Java规范的.so库);
如果.h文件中的方法名类似 getString ,你还是老老实实的看第二步吧
新建一个Android Project ,命名随意,这里的包名还是需要注意一下,最好是用你实际项目中的包名,比如我公司的项目包名是 “com.xyw.aaron” ,那么你new Android Project的时候也改成这个,当然,不该也不会有什么影响(为什么?后面再说 1)
我们这里以 JniUtil 为例(类的名字没有规定,个人喜好),这个类的位置也是有讲究的(后面再说 2),声明几个方法,以public static native 开头(static非必要),具体方法看第三方的.h文件(test.h),我这里给一个简单的demo代码:
#pragma once
extern int add(int a, int b);
extern int sub(int a, int b);
我建的JniUtil类代码(这个方法其实是我们要用的,也没有必要完全和test.h一样,我们只需要在自己的C里面调test.h的方法即可):
package com.xyw.aaron;
public class JniUtil {
static {
System.loadLibrary("aaron");
}
public static native int jiafa(int a, int b);
public static native int jianfa(int a, int b,String className);
}
如图所示,点击按钮即可,完成之后在build文件夹里面找到生成的JniUtil.class文件(低版本的Android studio在 build->intermediates->classes里面)
右键复制其路径(Copy Path):
D:\as_space…\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes\com\xyw\aaron\JniUtil.class
在编辑器里面改成
D:\as_space…\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes com.xyw.aaron.JniUti
打开window或mac终端,cd到项目的main目录下面,执行下面的命令
javah -d jni -classpath D:\as_space…\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes com.xyw.aaron.JniUti
在src/main/jni下面生成了 com_xyw_aaron_JniUtil.h文件
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_xyw_aaron_JniUtil */
#ifndef _Included_com_xyw_aaron_JniUtil
#define _Included_com_xyw_aaron_JniUtil
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_xyw_aaron_JniUtil
* Method: jiafa
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_xyw_aaron_JniUtil_jiafa
(JNIEnv *, jclass, jint, jint);
/*
* Class: com_xyw_aaron_JniUtil
* Method: chengfa
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_xyw_aaron_JniUtil_chengfa
(JNIEnv *, jclass, jint, jint,jstring);
#ifdef __cplusplus
}
#endif
#endif
#include "jni.h"
#include "com_example_createsodemo_SoUtil.h"
#include "test.h"
JNIEXPORT jint JNICALL Java_com_example_createsodemo_SoUtil_jiafa(JNIEnv *env, jclass jz, jint a, jint b){
return add(a,b);
}
JNIEXPORT jint JNICALL Java_com_example_createsodemo_SoUtil_chengfa(JNIEnv *env, jclass jz, jint a, jint b,jstring classname){
return mul(a,b);
}
这个文件其实就是实现com_example_createsodemo_SoUtil.h里面的两个方法jiafa()和jianfa(),注意第三行代码,这里引入了第三方的test.h文件,因为我们的aaron.c会用到tesh.h里面暴露的方法add(a,b)和mul(a,b)
记得把第三方的.so文件(libtest.so)复制到jni文件夹里面。
注意:这里的so文件必须是和你开发设备abi一致的,我设备是x86的
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := test // 根据下面的.so文件来就行
LOCAL_SRC_FILES := libtest.so // jni下的第三方.so文件
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := aaron // 你要生成的so文件的名称
LOCAL_SRC_FILES := aaron.c // 你的.c文件
LOCAL_LDLIBS += -L$(SYSROOT)/lib -llog
LOCAL_LDLIBS := -llog -ljnigraphics
LOCAL_CFLAGS := -g
LOCAL_SHARED_LIBRARIES := test // 第一个test
include $(BUILD_SHARED_LIBRARY)
Application.mk
APP_ABI := x86
APP_PLATFORM := android-16
在终端,cd到Android.mk所在的目录,执行ndk-build命令,如果配置没有问题,那么,你将会在main目录下看到libs和obj两个文件夹,我们要的东西就在libs里面,见图:
打开你将要开发的项目,在main下面建一个文件夹,文件夹的名称为“com.xyw.aaron”,这个目录必须和我们新生成的.so文件中的Java_com_xyw_aaron_JniUtil 匹配,这里我在前面(1. 新建Android项目,用来生成我们要的.so库)就已经提到了,这就是为什么我们在二次封装的时候,文件夹名称一定要和实际项目的相同的原因。如果相同,那么你就不要再建这个文件夹了,好了不废话了。
将(2.新建一个类JniUtil)提到的JniUtil类复制到该文件夹里面(如果你要自己写也未尝不可,哈哈)。
ok,到这里你就可以随意的在项目中使用.so库里面的方法了(直接调JniUtil里面的方法即可!)
有什么问题大家一起探讨!
留个邮箱:[email protected]
【转】JNI开发学习之C反射调用java方法