Android JNI 开发

Android studio 开发JNI 现在主要有两个方式:
1、自己写 Android.mk / Application.mk。
2、通过Cmake。支持Android studio 2.2 以上版本

本文主要使用第一种方法,通过自己写Android.mk / Application.mk 的方式来实现开发JNI

第一步:配置NDK。

《Android JNI 开发》 image.png

第二步:编写 java 代码,声明 native 函数

《Android JNI 开发》 image.png

第三步:编写Native 代码。在main 目录下创建 jni 文件夹,然后创建一个c或者cpp文件。

#include <jni.h>
#include <stdio.h>
#include <stdlib.h>

jstring
Java_com_longkaiwen_wffmpeg_MainActivity_getFFmpegInfo(JNIEnv     *env, jobject thiz){
    return (*env)->NewStringUTF(env, "get FFmpegInfo");
}

不用生成什么头文件了,直接写c代码。首先是返回值

《Android JNI 开发》 image.png

这是 Java 类型与 native 类型的对应关系。

然后是方法名,方法名有自己固定的格式,Java_+包名+类名+方法名。
最后是参数,JNIEnv 与 jobject 是必要的两个参数,然后自己的参数跟随在后。
规定是每个函数默认都会两个参数,一个是 JNIEnv 指针类型的结构体,一个是调用者对象,比如我们这里就是MainActivity对象,其实玩过 c++ 的都知道里面每个函数其实默认也会传入个 this 指针的。
如果创建的是 cpp文件 得加上 extern “C” ,原因很简单,在 C++ 中函数在编译的时候会拼接上参数,这也是 c++ 中函数重载的处理机制,比如一个 set(int a) 和一个 set(int a,int b) ,在编译的时候就变成了 set_int 与 set_int_int ,我们加上extern ”C“ 就表示想按照C来编译,所以函数名字后面就不会拼接上参数类型了。

第四步:Android.mk / Application.mk

Android.mk

  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)
  LOCAL_MODULE := test
  LOCAL_SRC_FILES :=test.c
  include $(BUILD_SHARED_LIBRARY)

Android.mk 文件必须首先定义 LOCAL_PATH 变量:
LOCAL_PATH := $(call my-dir)
此变量表示源文件在开发树中的位置。在这里,构建系统提供的宏函数 my-dir 将返回当前目录(包含 Android.mk 文件本身的目录)的路径。
下一行声明 CLEAR_VARS 变量,其值由构建系统提供。
include $(CLEAR_VARS)
CLEAR_VARS 变量指向特殊 GNU Makefile,可为您清除许多 LOCAL_XXX 变量,例如 LOCAL_MODULE、LOCAL_SRC_FILES 和 LOCAL_STATIC_LIBRARIES。 请注意,它不会清除 LOCAL_PATH。此变量必须保留其值,因为系统在单一 GNU Make 执行环境(其中所有变量都是全局的)中解析所有构建控制文件。 在描述每个模块之前,必须声明(重新声明)此变量。
接下来,LOCAL_MODULE 变量将存储您要构建的模块的名称。请在应用中每个模块使用一个此变量。
LOCAL_MODULE := hello-jni
每个模块名称必须唯一,且不含任何空格。构建系统在生成最终共享库文件时,会将正确的前缀和后缀自动添加到您分配给 LOCAL_MODULE 的名称。 例如,上述示例会导致生成一个名为 libhello-jni.so 的库。
下一行枚举源文件,以空格分隔多个文件:
LOCAL_SRC_FILES := hello-jni.c
LOCAL_SRC_FILES 变量必须包含要构建到模块中的 C 和/或 C++ 源文件列表。
最后一行帮助系统将所有内容连接到一起:
include $(BUILD_SHARED_LIBRARY)
BUILD_SHARED_LIBRARY 变量指向 GNU Makefile 脚本,用于收集您自最近 include 后在 LOCAL_XXX 变量中定义的所有信息。 此脚本确定要构建的内容及其操作方法。

Application.mk

 指定生成哪些cpu架构的库
APP_ABI := armeabi-v7a
 此变量包含目标 Android 平台的名称
APP_PLATFORM := android-22

第五步:执行 ndk-build。在Android studio 中的 terminal 中,进入的 src/main 目录中,执行 ndk-build 命令,这个时候会在main 目录下生成 libs 与 obj目录。

第六步:在 app的 build.gradle 文件中的 android{}块中添加如下代码

sourceSets{
    main {
        jni.srcDirs = []
        jniLibs.srcDirs = ['src/main/libs']
    }
}

最后开始运行,就能够成功调用 native 代码。

重要!!!!!!!!!!!!!!!!!!!!!!!!!!分割线

可能遇到的问题:
1、ndk-build 命令执行失败:(1)错误信息:command not find。是否配置了NDK的环境变量。(2) 错误信息:No rule to make target , needed by `obj/local/armeabi-v7a/objs/test/test.o’. Stop. 请检查Android.mk中的源文件名和jni下的文件名是否一样,路径是否一样。
2、编译失败:(1)Error:Execution failed for task ‘:app:compileDebugNdk’.
Error: Your project contains C++ files but it is not using a supported native build system. 检查在第六步中配置的suorceSets信息。jni的目录是否和配置中写的一样。Android studio 默认为jni,但是也要写成[].
3、运行时崩溃:(1)错误信息:UnsatisfiedLinkError …….. .so not find。so库没有找到,需要注意System.loadLibrary时的名字,与生成的so库的名字是否一样,注意,java代码中不需要前缀lib。同时需要检查第六步中设置的sourceSets中的jnilibs的目录是否和生成的so库的目录一样。(2)错误信息:UnsatisfiedLinkError: No implementation found 检查方法名,是否按照了 native 的规则书写。

    原文作者:叉腰大魔王
    原文地址: https://www.jianshu.com/p/d5f286c6c231
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞