Android JNI 开发

作者: 叉腰大魔王 | 来源:发表于2017-08-12 16:36 被阅读104次

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

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

    第一步:配置NDK。

    image.png

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

    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代码。首先是返回值

    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 的规则书写。

    相关文章

      网友评论

        本文标题:Android JNI 开发

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