Android NDK(C++)在module中通过CMake配置生成so库

发布于 2022-03-20  296 次阅读


有关NDK配置的教程大多数的配置都是在app中直接实现,但实际项目中如此混杂开发会引发项目的管理混乱等问题,可以考虑在Module中,利用JNI去统一封装实现。

一、配置

配置主要为安装NDK库、CMacke插件等,本文不作说明。

二、实现步骤

2.1 创建Java类

package com.ganahe.routealglib;

/**
 * @brief: JNI Test
 *
 * @auther: GanAHE
 * @date: 2022/3/20
 */
public class JNITest {
    static {
        System.loadLibrary("pilotGSO");
    }
/*
    public static void main(String args[]){
        JNITest jniTest = new JNITest();
        System.out.println(jniTest.jniGet());
        jniTest.jniSet("hello world");
    }*/

    public native String jniGet();
    public native void jniSet(String str);

}

pilotGSO为即将生成的so库名。
native类型的方法即为JNI中实现的方法。

随后通过javac生成JNI所必须的头文件:
(1)在Module根目录中打开终端Terminal、PowerShell或命令行CMD,根据上一步创建的java类路径键入命令javac path/JNITest.java

示例: javac main/java/***/JNITest.java

此步骤将生成JNITest.class文件;

(2)进入到java文件所在位置,输入javac -encoding utf8 -h . JNITest.java;
(3)此时会得到包名_包名+JNITest.h的头文件,示例如下:

com_ganahe_routealglib_JNITest.h

2.2 C/C++示例编写

新建test.cpp文件,内容如下:

//
// Created by dingg on 2022/3/20.
//

#include "include/com_ganahe_routealglib_JNITest.h"
#include<cstdio>

extern "C" {
JNIEXPORT jstring JNICALL Java_com_ganahe_routealglib_JNITest_jniGet(JNIEnv *env, jobject thiz) {
    printf("invoke get in c++\n");
    return (*env).NewStringUTF( "Hello from JNI!");
}


JNIEXPORT void JNICALL
Java_com_ganahe_routealglib_JNITest_jniSet(JNIEnv *env, jobject thiz, jstring string) {
    printf("invoke set in c++\n");
    char *str = (char *) (*env).GetStringUTFChars(string, NULL);
    printf("%s\n", str);
    (*env).ReleaseStringUTFChars( string, str);
}

}

根据自己的头文件所在位置与cpp文件的位置自行引入头文件。
说明:
JNIEnv*:表示一个指向JNI环境的指针,可以通过它来访问JNI提供的接口方法
jobect:表示Java对象中的this

2.3 CMake文件编写

在Module根目录下新建CMakeLists.txt文件,根据cpp在Module的路径配置如下:

cmake_minimum_required(VERSION 3.4.1)
add_library( # Sets the name of the library.
    PilotGSO
    # Sets the library as a shared library.
    SHARED
    # Provides a relative path to your source file(s).
    src/main/***/test.cpp)

#Searches for a specified prebuilt library and stores the path as a
#variable. Because CMake includes system libraries in the search path by
#default, you only need to specify the name of the public NDK library
#you want to add. CMake verifies that the library exists before
#completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )

#Specifies libraries CMake should link to your target library. You
#can link multiple libraries, such as libraries you define in this
#build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
    PilotGSO
    # Links the target library to the log library
    # included in the NDK.
    ${log-lib} )

src/main/***/test.cpp需要替换为自己项目的文件路径,PilotGSO即为要生成的so文件名。

2.4 构建与引用

本次构建gradle版本为4.0,

在当前Module的build.gradle中加入:

android {
        --xxx代表省略的默认生成代码--
        ndk{
            abiFilters "arm64-v8a","armeabi-v7a"/*,"armeabi"*//*,"x86"*/
        }

        externalNativeBuild{
            cmake{
                cppFlags ""
                abiFilters 'armeabi-v7a',"arm64-v8a"
            }
        }
    }

    externalNativeBuild{
        cmake{
            path "../routeALGLib/CMakeLists.txt"//编译后so文件的名字
        }
    }
}

在app的build.gradle引入该module:

implementation project(":routeAlGLib")

完成后点击Android studio上的Make Project,开始构建生成,随后在app中调用测试:

findViewById(R.id.appCompatImageView3).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                JNITest jniTest = new JNITest();
                Toast.makeText(getApplicationContext(),"Click Me to Link JIN." + jniTest.jniGet(),Toast.LENGTH_SHORT).show();
            }
        });

结果如下:

三、参考文献

Android:通过CMake方式生成动态库so文件