• Android studio生成so文件

    一、简介

    现在,大部分安卓app的开发几乎都是使用java完成,但是,Java在速度和一些功能上可能不如c/c++程序,并且一些好的开源项目是使用c/c++开发完成的,为了使用这一功能,android拓展了jni,提供这一服务。

    二、 NDK

    2.1 理解ndk

    在Android OS上开发应用程序,Google提供了两种开发包:SDK和NDK,Android 平台从一开就已经支持了C/C++了。我们知道Android的SDK主要是基于Java的,所以导致了在用Android SDK进行开发的工程师们都必须使用Java语言。不过,Google从一开始就说明Android也支持JNI编程方式,也就是第三方应用完成可以通过JNI调用自己的C动态度。于是NDK就应运而生了。

    2.2 为何ndk

    • 1、在平台之间移植其应用

    • 2、重复使用现在库,或者提供其自己的库重复使用

    • 3、在某些情况下提性能,特别是像游戏这种计算密集型应用

    • 4、使用第三方库,现在许多第三方库都是由C/C++库编写的,比如Ffmpeg这样库。

    • 5、不依赖于Dalvik Java虚拟机的设计

    • 6、代码的保护。由于APK的Java层代码很容易被反编译,而C/C++库反编译难度大

    2.3 从NDK到so

    img

    从上图这个Android系统框架来看,我们上层通过JNI来调用NDK层的,使用这个工具可以很方便的编写和调试JNI的代码。因为C语言的不跨平台,在Mac系统的下使用NDK编译在Linux下能执行的函数库——so文件。其本质就是一堆C、C++的头文件和实现文件打包成一个库。目前Android系统支持以下七种不用的CPU架构,每一种对应着各自的应用程序二进制接口ABI:(Application Binary Interface)定义了二进制文件(尤其是.so文件)如何运行在相应的系统平台上,从使用的指令集,内存对齐到可用的系统函数库。对应关系如下:

    • ARMv5——armeabi
    • ARMv7 ——armeabi-v7a
    • ARMv8——arm64- v8a
    • x86——x86
    • MIPS ——mips
    • MIPS64——mips64
    • x86_64——x86_64

    三、JNI

    3.1 解释jni

    JNI,全称为Java Native Interface,即Java本地接口,JNI是Java调用Native 语言的一种特性。通过JNI可以使得Java与C/C++机型交互。即可以在Java代码中调用C/C++等语言的代码或者在C/C++代码中调用Java代码。由于JNI是JVM规范的一部分,因此可以将我们写的JNI的程序在任何实现了JNI规范的Java虚拟机中运行。同时,这个特性使我们可以复用以前用C/C++写的大量代码JNI是一种在Java虚拟机机制下的执行代码的标准机制。代码被编写成汇编程序或者C/C++程序,并组装为动态库。也就允许非静态绑定用法。这提供了一个在Java平台上调用C/C++的一种途径,反之亦然。

    PS:
    开发JNI程序会受到系统环境限制,因为用C/C++ 语言写出来的代码或模块,编译过程当中要依赖当前操作系统环境所提供的一些库函数,并和本地库链接在一起。而且编译后生成的二进制代码只能在本地操作系统环境下运行,因为不同的操作系统环境,有自己的本地库和CPU指令集,而且各个平台对标准C/C++的规范和标准库函数实现方式也有所区别。这就造成了各个平台使用JNI接口的Java程序,不再像以前那样自由的跨平台。如果要实现跨平台, 就必须将本地代码在不同的操作系统平台下编译出相应的动态库。

    3.2 为何jni

    可以使用Java代码和C/C++进行交互,与其它类似接口Microsoft的原始本地接口等相比,JNI的主要竞争优势在于:它在设计之初就确保了二进制的兼容性,JNI编写的应用程序兼容性以及其再某些具体平台上的Java虚拟机兼容性(当谈及JNI时,这里并不特比针对Davik虚拟机,JNI适用于所有JVM虚拟机)。这就是为什么C/C++编译后的代码无论在任何平台上都能执行。不过,一些早期版本并不支持二进制兼容。二进制兼容性是一种程序兼容性类型,允许一个程序在不改变其可执行文件的条件下在不同的编译环境中工作。

    3.3 工作原理

    img

    img

    JNI是作为一个桥梁的作用,链接java和c/c++

    3.4 命名

    例子

    1
    JNIExport jstring JNICALL Java_com_example_hellojni_MainActivity_stringFromJNI( JNIEnv* env,jobject thiz ) 

    jstring返回值类型
    Java_com_example_hellojni包名
    MainActivity类名
    stringFromJNI方法名

    其中**JNIExportJNICALL**是不固定保留的关键字不要修改

    3.5 JNI开发流程

    • 第1步:在Java中先声明一个native方法
    • 第2步:编译Java源文件javac得到.class文件
    • 第3步:通过javah -jni命令导出JNI的.h头文件
    • 第4步:使用Java需要交互的本地代码,实现在Java中声明的Native方法(如果Java需要与C++交互,那么就用C++实现Java的Native方法。)
    • 第5步:将本地代码编译成动态库(Windows系统下是.dll文件,如果是Linux系统下是.so文件,如果是Mac系统下是.jnilib)
    • 第6步:通过Java命令执行Java程序,最终实现Java调用本地代码。

    PS:javah 是JDK自带的一个命令,-jni参数表示将class 中用到native 声明的函数生成JNI 规则的函数

    img

    四、实现一个JNI例子

    4.1 创建app

    打开android studio,新建一个project, Create Android Project

    image-20211212162000770

    这里可以勾选Include C++ support, Android studio会给出一个使用JNI的例子。

    4.2 创建 测试类

    新建一个testso的java类,在里面声明一个native方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    package com.qqq.sotest.sotest;
    /**
    * Created by admin on 2021/12/12.
    */
    public class testso {
    static {
    System.loadLibrary("so-lib");
    }
    public static native String getJniTestString();
    }

    image-20211212162936951

    注意,loadLibrary的是在CMakeLists.txt设置的library的名字,而不是c文件名

    4.3 创建c文件

    在app/src/main/cpp/目录下新建一个so-test.c的文件,实现c语言方法,如

    1
    2
    3
    4
    5
    #include <jni.h>
    JNIEXPORT jstring JNICALL Java_com_qqq_sotest_sotest_testso_getJniTestString
    (JNIEnv * env, jclass object){
    return (*env)->NewStringUTF(env,"测试 jni");
    }

    image-20211212164349767

    创建c文件里面的JNICALL Java_xxxx_xxxx_xxxx必须遵循命名规则。

    4.4 添加到CMakeLists.txt

    需要把so-test.c文件告诉Android Studio,通过CMake修改

    在app/src/main/CMakeLists.txt添加如下内容

    1
    2
    3
    4
    5
    6
    7
    8
    add_library( # Sets the name of the library.
    so-lib

    # Sets the library as a shared library.
    SHARED

    # Provides a relative path to your source file(s).
    src/main/cpp/so-test.c )

    so-lib设置library的名字,在java代码里引用

    SHARED将其设为共享的library

    src/main/cpp/so-test.c c文件的路径

    image-20211212165436159

    4.5 构建

    点击Build->Make Project构建,或者ctrl+F9构建, 再运行

    这样,c/cpp文件就被打包成so文件了。

    image-20211212171338595

    打包完成后so文件会放在apk的lib/文件夹里

    enbd8-nseor

    参考链接

    上一篇:
    Base64 的算法原理
    下一篇:
    ASCII 转换工具
    本文目录
    本文目录