JNI开发

前期准备- NDK环境搭建

1.下载NDK的包(Mac OS X版本)
目前最新版本android-ndk-r13b-darwin-x86_64.zip,下载链接点击此处,

2.配置环境变量

1. 启动终端Terminal2. 进入当前用户的home目录    输入cd ~    3. 创建.bash_profile    输入touch .bash_profile    4. 编辑.bash_profile文件    输入open -e .bash_profile.bash_profile 文件内容:    export PAHT=$PATH:/Users/work/dev_app/adt-bundle-mac/sdk/tools    export PATH=$PATH:/Users/work/dev_app/android-ndk-r8c/    ANDROID_NDK_ROOT=/Users/work/dev_app/android-ndk-r8c/    export ANDROID_NDK_ROOT     ANDROID_SDK_ROOT=/Users/work/dev_app/adt-bundle-mac/sdk/    exprot ANDROID_SDK_ROOT5. 保存文件,关闭.bash_profile6. 更新刚配置的环境变量    输入source .bash_profile    7.输入ndk-build试试    出现这个结果就是正确    Android NDK: Could not find application project directory !    Android NDK: Please define the NDK_PROJECT_PATH variable to point to it.    /Users/bujue/mogujie/soft/ndk/android-ndk-r10e/build/core/build-    local.mk:143: *** Android NDK: Aborting    .  Stop.

开始编写JNI

在工程根目录下新建jni目录,一定要创建jni目录,否者ndk-build不知道执行文件,在这个文件夹下面创建Android.mk和Application.mk两个文件,附上我的jni目录文件结构图和make文件

文件结构图

.├── Android.mk├── Application.mk├── network_jni.cpp└── network_jni.h

Android.mk

Android.mk文件:是Android提供的一种makefile文件,用来指定诸如编译生成so库名,编译的.cc/.cpp文件和.a静态文件。

以下是我这边用到的Android.mk。有使用到.a静态依赖,循环文件夹便利.cc/.cpp文件

关于Android.mk文件的详解,可以点击这里

LOCAL_PATH := $(call my-dir)#依赖sodium.a 静态库include $(CLEAR_VARS)LOCAL_MODULE := sodiumLOCAL_SRC_FILES := ../ThirdParty/sodium/libsodium.ainclude $(PREBUILT_STATIC_LIBRARY)include $(CLEAR_VARS)#编译模块时要使用的附加的连接器选项,关联logLOCAL_LDLIBS := -llogLOCAL_CFLAGS := -DDEBUGNDK_DEBUG=1#生成的so库名称LOCAL_MODULE := network_hermes #依赖静态包LOCAL_C_INCLUDES :=  $(LOCAL_PATH)/../ThirdParty/sodiumLOCAL_STATIC_LIBRARIES += sodium#下方循环遍历.cc/.cpp文件进行编译MY_FILES_PATH  :=  $(LOCAL_PATH)/../Core $(LOCAL_PATH)MY_FILES_PATH  +=  $(LOCAL_PATH)/../ThirdParty/jsoncppMY_FILES_PATH  +=  $(LOCAL_PATH)/../ThirdParty/sodiumMY_FILES_PATH  +=  $(LOCAL_PATH)/../ThirdParty/crypt/srcMY_FILES_SUFFIX := %.cppMY_FILES_SUFFIX += %.ccrwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))MY_ALL_FILES := $(foreach src_path,$(MY_FILES_PATH), $(call rwildcard,$(src_path),*.*) )MY_ALL_FILES := $(MY_ALL_FILES:$(MY_CPP_PATH)/./%=$(MY_CPP_PATH)%)MY_SRC_LIST  := $(filter $(MY_FILES_SUFFIX),$(MY_ALL_FILES))MY_SRC_LIST  := $(MY_SRC_LIST:$(LOCAL_PATH)/%=%)define uniq =  $(eval seen :=)  $(foreach _,$1,$(if $(filter $_,${seen}),,$(eval seen += $_)))  ${seen}endefMY_ALL_DIRS := $(dir $(foreach src_path,$(MY_FILES_PATH), $(call rwildcard,$(src_path),*/) ) )MY_ALL_DIRS := $(call uniq,$(MY_ALL_DIRS))#表示需要编译的文件LOCAL_SRC_FILES  := $(MY_SRC_LIST)#表示头文件的搜索路径LOCAL_C_INCLUDES += $(MY_ALL_DIRS)include $(BUILD_SHARED_LIBRARY)

Application.mk

Application.mk目的是描述在你的应用程序中所需要的模块(即静态库或动态库)。

Application.mk更多信息可以看这里

附上我的Application.mk

APP_PLATFORM := android-9  APP_STL := stlport_static #NDK构建系统提供由Android系统给出的最小C++运行时库的C++头文件。APP_CPPFLAGS := -frtti -fexceptions  #允许异常功能,及运行时类型识别 APP_CPPFLAGS += -std=c++11 #允许使用c++11的函数等功能 APP_CPPFLAGS += -fpermissive #此项有效时表示宽松的编译形式,比如没有用到的代码中有错误也可以通过#APP_ABI := allAPP_ABI := armeabi-v7a //生成ABI对应的so

OK,开始讲解代码了

JNI的头文件

1.首先在AS中创建一个类,不以自己的项目为例了,写了个HelloJNI来看:

public class JniTest {static{    System.loadLibrary("hello-jni");}public static final native String helloJni();}

然后通过命令javah -jni XX.XX.XX.JNITest就可以在java文件夹下生成头文件。格式如下:

/* DO NOT EDIT THIS FILE - it is machine generated */#include /* Header for class mogujie_com_jnitestapp_JniTest */#ifndef _Included_mogujie_com_jnitestapp_JniTest#define _Included_mogujie_com_jnitestapp_JniTest#ifdef __cplusplusextern "C" {#endif/* * Class:     mogujie_com_jnitestapp_JniTest * Method:    helloJni * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_mogujie_com_jnitestapp_JniTest_helloJni  (JNIEnv *, jclass);#ifdef __cplusplus}#endif#endif

JNI的C文件

复制刚才生成的头文件,然后将后缀名改为.cpp.并实现hellojni的方法,格式如下:

/* DO NOT EDIT THIS FILE - it is machine generated */#include #include "hellojni.h"#include /* Header for class mogujie_com_jnitestapp_MainActivity */#ifdef __cplusplusextern "C" {#endif/* * Class:     mogujie_com_jnitestapp_MainActivity * Method:    helloJni * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_mogujie_com_jnitestapp_JniTest_helloJni(JNIEnv *env, jclass jobj){    // return (*env)->NewStringUTF(env,"czz重温android studio生产so文件");    jstring str = env->NewStringUTF("123123123123");    return str;}#ifdef __cplusplus}#endif

最后

  1. cd到jni目录,输入ndk-build进行编译,最后会在jni同级目录生成libs文件,.so文件就在里面

  2. 回到AS编辑器中,将.so放置在jniLibs文件夹下(如没有,则创建一个,与java文件夹同级)

  3. 在build.gradle添加JNI配置

     buildTypes {     sourceSets.main {         jni.srcDirs = []//disable automatic ndk-build call     } } 

4.运行工程,检查是否成功

JNI 相应的格式

JNI参数类型 JNI方法参数

I

使用JNI的函数以及复杂对象传递

可以查看JNI学习积累之三 ---- 操作JNI函数以及复杂对象传递

碰到的问题

编译问题

  1. Android NDK 'std::string' has not been declared

     需要让Android NDK支持STL 将Application.mk放在jni目录下(内容如下) APP_STL := stlport_static  头文件中#include 就OK了,  注意使用std::string或加上using namespace std; #include  using namespace std; class X {     public:     void a(string);     void b(std::string); };
  2. Android2.2:'pthread_rwlock_t' does not name a type: android 2.3版本以下不支持读写锁的解决办法

     在Android API < 9时, 采用android NDK编译代码是不支持pthread_rwlock_t结构体的。 当时给的解决办法是改写application.mk文件, 把版本改成9,APP_PLATFORM := android-9//对应2.3.1
  3. 不支持C++11函数

     在Application中添加APP_CPPFLAGS +=-std=c++11 未生效 最后修改使用到C++11的代码改为低版本的书写方式

运行问题

  1. JNI层没有及时DetachCurrentThread(g_jvm->DetachCurrentThread())导致内存泄漏Crash

     class JNIEnvGuard { public:     JNIEnvGuard() {         int status;         needDetachEnv = false;         status = g_jvm->GetEnv((void**)&env, JNI_VERSION_1_6);         if(status < 0){             if (g_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {                 JNI_LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);                 return;             }             needDetachEnv = true;         }     }  virtual ~JNIEnvGuard() {     if (needDetachEnv) {         if (g_jvm->DetachCurrentThread() != JNI_OK) {             JNI_LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);         }     } } JNIEnv *getEnv() {     return env; }  private:     JNIEnv *env;     bool needDetachEnv; }; 通过这个类自动析构线程
  2. (g_jvm)->AttachCurrentThread(&env, NULL) 后使用 (g_jvm)->DetachCurrentThread();程序报错

     调用DetachCurrentThread函数的地方在java线程中, 即在java调用C++代码时在C++代码中调用了AttachCurrentThread方法来获取JNIEnv, 此时JNIEnv已经通过参数传递进来 你不需要再次AttachCurrentThread来获取。在释放时就会报错。     int status; needDetachEnv = false; status = g_jvm->GetEnv((void**)&env, JNI_VERSION_1_6); if(status < 0){     if (g_jvm->AttachCurrentThread(&env, NULL) != JNI_OK) {         JNI_LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);         return;     }     needDetachEnv = true; }

相关链接

Android官网NDK入门指南(中文版)

JNI/NDK开发指南

AndroidJNI 通过C++调用JAVA

JNI学习积累之三 ---- 操作JNI函数以及复杂对象传递

Android NDK之----- C调用Java [GetMethodID方法的使用]

更多相关文章

  1. Android(安卓)OOM ,回收布局文件中ImageView占用的内存.Bitmap O
  2. android分包原理--MultiDex
  3. Android(安卓)5.0 Binder编译问题及解决方案
  4. Android(安卓)6.0 读写SD卡权限问题(续)
  5. Android(安卓)Studio重写方法时参数显示异常的解决方法
  6. [Android(安卓)SQLite]数据存储与访问 - 内部存储
  7. Android(安卓)GUI更新过程
  8. 常用到的Android命令(持续更新)
  9. java.io.IOException: open failed: EINVAL (Invalid argument)

随机推荐

  1. Android自定义view实现圆形waveview
  2. Android中项目中各个文件夹的含义和用途
  3. 创建第一个Andorid程序
  4. Android Service 笔记
  5. Android性能优化系列---管理你的app内存(
  6. Android的一段常用动画效果代码(如何让点
  7. Android监听键盘显示和隐藏
  8. Android 文件读写操作方法总结
  9. Android Notes 之 Tween动画 (1)四种基本动
  10. Appium - Android 对比 iOS