美文网首页
Android之从零开始JNI研发

Android之从零开始JNI研发

作者: 王三的猫阿德 | 来源:发表于2017-08-03 17:07 被阅读178次

    转载注明出处:http://www.jianshu.com/p/c394fa547998

    本文是基于Mac端Android Studio的JNI开发介绍。

    Andorid官方JNI文档

    Android官方JNI实例文档

    JNI维基百科

    JNI手册英文版

    JNI手册中文版

    Oracleg官方JNI文档

    1. NDK安装以及环境配置

    环境配置完成后,使用ndk-build指令查看一下是否配置成功。

    mac下执行效果:

    > ndk-build                                                                                                                                         
    Android NDK: Could not find application project directory !    
    Android NDK: Please define the NDK_PROJECT_PATH variable to point to it.    
    

    ndk环境搭建好后就可以开始动手了。

    2. 定义带有本地方法Java类

    public class JNIDemoUtil {
        private native String getString(String input);
    }
    

    3. 生成class文件

    build -> rebuild重新构建项目,生成class文件,class文件在<壳工程>/build/classes/debug目录下。

    图-1 class目录结构图

    4. 生成.h头文件

    进入到<壳工程>/build/classes/debug目录下执行以下指令

    # -classpath指定类的路径
    javah -classpath . -jni {包名.类名}
    

    例如, javah -classpath . -jni com.kyo.jnidemo.jni.JNIDemoUtil,会生成.h文件。

    图-2 h文件目录图

    5. 编写c/c++文件

    native的具体实现

    #include <stdlib.h>
    #include <jni.h>
    #include "com_kyo_jnidemo_jni_JNIDemoUtil.h"
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    jstring Java_com_kyo_jnidemo_jni_JNIDemoUtil_getString(JNIEnv *env, jclass obj) {
        return (*env)->NewStringUTF(env, "hello world");
    }
    
    #ifdef __cplusplus
    }
    #endif
    

    6. 生成.so动态库

    上面拿到了h头文件和具体实现的c/c++文件,接下来是生成.so动态库,根据放的目录不同有三种方式。

    • 放在工程的根目录jni中
    • 放在工程的内部子文件中
    • 放在/src/main/jni目录中

    6.1 C/C++代码在根目录jni

    图-3 根目录生成so

    新建mk文件,设置一些属性。

    LOCAL_MODULE := ${call my-dir}/
    include ${CLEAR_VARS}
    LOCAL_SRC_FILES := /Users/wang/WorkPlace/MyWork/JNIDemo/jni/JNIDemoUtil.c
    LOCAL_MODULE = libJNIDemo
    
    include ${BUILD_SHARED_LIBRARY}
    

    进入jni目录,使用ndk-build指令,会在工程根目录中自动生成libsobj两个目录,其中libs,目录下就有so库。

    图-4 运行结果图

    注意:只有C/C++把放在工程根目录中的jni才可以使用ndk-build指令编译

    6.2 C/CC++代码在工程内部其他目录

    图-5 非根目录接结构图

    在工程根目录新建Application.mk文件。

    APP_BUILD_SCRIPT := /Users/wang/WorkPlace/MyWork/JNIDemo/jni_c/src/Android.mk
    # 因为针对多个CPU架构会生成多个so库,使用APP_ABI限定生成支持某种CPU架构的so库
    APP_ABI := armeabi
    

    在C/CC++代码目录新建Android.mk文件

    LOCAL_MODULE := ${call my-dir}/
    include ${CLEAR_VARS}
    LOCAL_SRC_FILES := /Users/wang/WorkPlace/MyWork/JNIDemo/jni_c/src/JNIDemoUtil.c
    LOCAL_MODULE = libJNIDemo
    
    include ${BUILD_SHARED_LIBRARY}
    

    在工程的根目录下执行以下指令(可以直接进入C/C++源代码目录执行ndk-build生成so库),会在工程根目录中自动生成libsobj两个目录,其中libs,目录下就有so库。

    ndk-build NDK_PROJECT_PATH={工程目录} NDK_APPLICATION_MK={工程的Applicaion.mk目录}
    

    指令运行结果图:

    图-6 运行结果图

    Application.mk指定了编译的mk,而Android.mk指定了编译的一些属性,包括编译源文件等等,这些都可以灵活变化

    6.3 C/C++放在src/main/jni目录下

    图-7 自动生成so目录图

    local.properties中添加ndk路径,我的如下。

    sdk.dir=/Users/wang/Android/Android_SDK
    ndk.dir=/Users/wang/Android/Android_NDK_r13b
    

    在壳工程(通常是app/build.gradle)的build.gradle配置生成的so库名,找到defaultConfig这个节点,添加如下内容。

    defaultConfig {
        ...
        ndk {
            moduleName "libJNIDemoJni" // so库名
            abiFilters "armeabi" // 指定生成的CPU架构对应的so库
        }
    }
    

    gradle.properties文件中添加.

    android.useDeprecatedNdk=true
    

    然后rebuild项目,会自动生成so动态库,在<壳工程>/build/intermedistes/ndk目录下面,目录如下图:

    图-8 自动生成so的目录

    7. 载入so库

    将生成的so库在<壳工程>/src/main/jniLibs目录下。

    同时在JNIDemoUtil中载入so库代码。

    public class JNIDemoUtil {
    
        // 注意库的名字前面没有lib
        static {
            System.loadLibrary("JNIDemo");
        }
    
        public native static String getString();
    }
    

    8. 编译运行

    准备工作做完后,可以直接运行啦。

    public void onClick(View v) {
        int id = v.getId();
        if(id == R.id.jni_demo_btn){
            mInfoTv.setText(JNIDemoUtil.getString());
        }
    }
    

    运行结果图:

    图-9 运行结果图

    9. 问题解答

    java.lang.UnsatisfiedLinkError: Couldn't load XXX indLibrary

    Android NDK: Could not find application project directory !
    Android NDK: Please define the NDK_PROJECT_PATH variable to point to it.

    使用以下指令编译

    ndk-build NDK_PROJECT_PATH={工程目录} NDK_APPLICATION_MK={工程的Applicaion.mk目录}
    

    附上demo地址:https://github.com/Kyogirante/JNIDemo

    10. 结束语

    本篇几乎没有涉及C/C++与java之间变量以及语法等等一些知识点,这些会在下篇来介绍。本人也是在学习探索过程中,如果有错误希望大家指出来。

    相关文章

      网友评论

          本文标题:Android之从零开始JNI研发

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