美文网首页
Android NDK-Build方式开发JNI

Android NDK-Build方式开发JNI

作者: duyi324 | 来源:发表于2018-12-22 13:11 被阅读0次

【参考链接】
https://blog.csdn.net/qq_35071078/article/details/70502481


使用ndk-build构建Jni

ndk-build是什么

ndk-build 文件是 Android NDK r4 中引入的一个 shell 脚本。其用途是调用正确的 NDK 构建脚本。可以用这个命令来生成.so文件。

ndk-build如何使用

当你想使用该命令将.cpp/.c文件生成.so文件,必须有具备以下几个条件

需有有Android.mk文件,并且与对应的.cpp/.c文件在同一个目录下

需要有Application.mk文件,并且与对应的.cpp/.c文件在用一个目录下

什么是Android.mk

Android.mk 文件位于项目 jni/ 目录的子目录中,用于向构建系统描述源文件和共享库。 它实际上是构建系统解析一次或多次的微小 GNU makefile 片段。 Android.mk 文件用于定义 Application.mk、构建系统和环境变量所未定义的项目范围设置。 它还可替换特定模块的项目范围设置。
Android.mk 的语法用于将源文件分组为模块。 模块是静态库、共享库或独立可执行文件。 可在每个 Android.mk 文件中定义一个或多个模块,也可在多个模块中使用同一个源文件。 构建系统只会将共享库放入应用软件包。 此外,静态库可生成共享库。
除了封装库之外,构建系统还可为您处理各种其他详细信息。例如,您无需在 Android.mk 文件中列出标头文件或生成的文件之间的显式依赖关系。 NDK 构建系统会自动为您计算这些关系。 因此,您应该能够享受到未来 NDK 版本中新工具链/平台支持的优点,而无需接触 Android.mk 文件。
此文件的语法与随整个 Android 开放源代码项目分发的 Android.mk 文件中使用的语法非常接近。 虽然使用它们的构建系统实现不同,但类似之处在于,其设计决定旨在使应用开发者更容易重复使用外部库的源代码。
简单来说,这个文件可以定义生成的.so文件的名字,可以像.cpp/.c文件引入一些动态库。
具体可以去看谷歌官方的参考(https://developer.android.com/ndk/guides/android_mk.html?hl=zh-cn

什么是Application.mk

Application.mk 文件实际上是定义要编译的多个变量的微小 GNU Makefile 片段。 它通常位于 PROJECT/jni/下,其中PROJECT/jni/下,其中PROJECT 指向应用的项目目录。 另一种方式是将其放在顶级 $NDK/apps/ 目录的子目录下。
例如,Application.mk可以用来指定需要生成哪些cpu类型的.so文件,或者定义引用.so的最低android api。
详细请看谷歌官方参考(https://developer.android.com/ndk/guides/application_mk.html?hl=zh-cn

示例

首先创建一个项目:


创建一个项目

创建一个类Jni,用来声明native方法:


创建一个类并声明native方法
用javah命令生成.h文件:首先 cd app/build/intermediates/classes/debug 进入到debug目录,然后 javah com.chenxin.testndk.MyJni 生成.h文件,在app/src/main目录下创建一个jni文件夹,将刚才生成的.h文件剪切到jni文件夹里面,在jni文件夹里面创建一个.c/.cpp的文件
image.png

如果创建的是.c:

#include <com_chenxin_testndk_MyJni.h>
#include <android/log.h>

#define LOG_TAG "infoo"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

JNIEXPORT jstring JNICALL Java_com_chenxin_testndk_MyJni_get
  (JNIEnv *env, jclass jz){

        LOGI("hello,这里是native层");
        return (*env)->NewStringUTF(env,"hello jni , you are so easy!");

  }

如果是.cpp:

#include <com_chenxin_testndk_MyJni.h>
#include <android/log.h>

#define LOG_TAG "infoo"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

JNIEXPORT jstring JNICALL Java_com_chenxin_testndk_MyJni_get
  (JNIEnv *env, jclass jz){

        LOGI("hello,这里是native层");
        return env->NewStringUTF("hello jni , you are so easy!");

  }

区别在于方法中的env变量的调用方式。

在jni目录下创建Android.mk文件:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE := MyJni 
LOCAL_SRC_FILES := test.c
LOCAL_LDLIBS +=-L$(SYSROOT)/usr/lib -lm -llog
include $(BUILD_SHARED_LIBRARY)
LOCAL_MODULE:是将要生成的.so的名字
LOCAL_SRC_FILES:是需要将哪个.c/.cpp文件生成.so文件,例如我这里是test.c
LOCAL_LDLIBS:这里是给.c/.cpp文件添加了一个log库,可以打印log

在jni目录下创建Application.mk文件

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := all
APP_PLATFORM := android-8
APP_ABI:是将要生成哪些cpu类型的so,all代表全部
APP_PLATFORM:生成的so的最低android版本,对应Android API Level

.c/.cpp,android.mk,application.mk创建完成之后:


image.png

打开android studio的终端,进入到jni文件夹,cd app/src/main/jni ,然后 输入 ndk-build 回车ok,so就生成了:


image.png
此时就可以看到libs文件夹里面的so文件了:

接下来就是使用so了,首先先与.so关联起来,有两种方法
(1)第一种方法,app/src/main目录下面创建一个jniLibs文件夹,将刚才生成的libs下的所有文件夹拷贝或者剪切到这里面,然后在 build.gradle中加入:

android {
   ......
    sourceSets {
        main() {
            jni.srcDirs = [] //屏蔽掉默认的jni编译生成过程
        }
    }
}

(2)第二种方法,直接在gradle中加入

android {
   ......
    sourceSets {
        main() {
            jniLibs.srcDirs = ['src/main/libs']
            jni.srcDirs = [] //屏蔽掉默认的jni编译生成过程
        }
    }
}

然后就是在代码中调用了:
MyJni.java:

public class MyJni {

    static {
        System.loadLibrary("MyJni");
    }

    public  native  static String get();

}

MainActivity.java:
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Log.e("info","调用jni层的结果 : "+MyJni.get());
}

}
编译运行:


image.png

ok,成功!

【可能遇到的错误】

1.生成so库时报错如下:

Android NDK: WARNING: APP_PLATFORM android-14 is higher than android:minSdkVersion 1 in ... ...

出现如上warning时,可以在AndroidManifest.xml中添加一行:

<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="23" />

并将Application.mk中的APP_PLAFORM这一项设置为与minSdkVersion相同的版本。
注意:app->src中的build.gradle、AndroidManifest.xml中的minSdkVersion、targetSdkVersion、与Application中的APP_PLATFORM版本一致!!!

2.NDK报错:

Application targets deprecated ABI(s): armeabi mips mips64
说明现在ndk已经不支持armeabimipsmips64这些架构了。

解决方案:
修改Application.mk文件

APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := arm64-v8a x86 x86_64
APP_PLATFORM := android-14

其中第三行,把all修改为arm64-v8a x86 x86_64
如果用CMake编译,则修改app/build.gradle,在cmake中添加abiFilter

externalNativeBuild {  
  cmake {  
    arguments '-DANDROID_TOOLCHAIN=clang',  
                    '-DANDROID_STL=c++_static'  
    //abiFilters "arm64-v8a"  
    abiFilters 'arm64-v8a','armeabi-v7a','x86','x86_64'  
  }  
}

相关文章

网友评论

      本文标题:Android NDK-Build方式开发JNI

      本文链接:https://www.haomeiwen.com/subject/lvtikqtx.html