AndroidStudio-NDK开发(一)cmake编译c代码提供so库和jar包

AndroidStudio-NDK开发(一)cmake编译c代码提供so库和jar包


版本说明

版本作者日期备注
0.1loon2020.8.28初稿

目录


总共分如下几步:

  • 1、创建项目,将测试module和生成so和jar的module分开;
  • 2、创建java类,外部直接调用时的接口;
  • 3、创建jni文件夹,并在其下创建CMakeLists.txt文件、cpp文件夹以及include文件夹等,这样结构更清晰;
  • 4、利用命令生成.h文件获取jni层的c接口;这样生成的接口不容易出问题;
  • 5、完成C接口;
  • 6、配置CMake;(用于编译c程序,链接第三方库等)
  • 7、关联Gradle;(指定CMakeLists路径,ndk编译的abi等)
  • 8、生成so;
  • 9、生成jar包;

一、创建项目

创建一个默认的empty activity模块一会用于测试我们编译好的so和jar包:
在这里插入图片描述
在这里插入图片描述
然后我们再单独创建一个module用于编译生成so和jar(先切换成Project,然后创建module):
在这里插入图片描述
这里选择Android Library:
在这里插入图片描述
我们直接叫sdklibrary:
在这里插入图片描述
项目结构如下,后续的so和jar包的生成在sdk module下,生成后在app module中进行调用测试:
在这里插入图片描述
注意: 下面的这些操作都是基于sdk module的,下一篇我们再说如何调用so和jar,后续不再赘述。

二、创建java类

这里创建SDK类,后续我们的库也叫sdk,然后写一个getString和add的简单接口:
在这里插入图片描述
SDK类的内容:

package com.knowyou.sdklibrary;

public class SDK {
    //调用sdk库
    static {
        System.loadLibrary("sdk");
    }

    //调用sdk原生接口getString
    public static native String getString();

    //调用原生接口getAdd
    public static native int getAdd(int numOne, int numTwo);
}

三、创建jni文件夹

1、创建jni文件夹

在这里插入图片描述

2、创建cpp文件夹及CMakeLists.txt等

创建cpp、include等,这样结构更清晰一些(new 普通的文件夹及文件即可):
在这里插入图片描述

四、生成c接口头文件

在terminal中进入到java目录下,输入javah -jni “包名.类名”,即会在java目录下生成头文件:

javah -jni com.knowyou.sdklibrary.SDK

在这里插入图片描述
上面的错误是由于中文注释引起的,不用管,或者可以删除中文注释后重新执行即可。

头文件中有准确的jni c接口名称,这样我们再去实现c接口不容易写错接口名称,实在要自己写的话则命名规则为:Java_包名_类名_接口名

五、完成c/c++代码

将刚才生成的头文件移动到我们创建的include文件夹下,之后在cpp文件夹下创建SDK.cpp复制过来刚才生成的头文件中的接口,然后实现,并包含头文件即可(做了一些简单的修改):

#include "com_knowyou_sdklibrary_SDK.h"

JNIEXPORT jstring JNICALL Java_com_knowyou_sdk_SDK_getString(JNIEnv *env, jclass cls)
{
    char *str = "String from native cpp";

    return (*env).NewStringUTF(str);
}

JNIEXPORT jint JNICALL Java_com_knowyou_sdk_SDK_getAdd(JNIEnv *env, jclass cls, jint numOne, jint numTwo)
{
    return (numOne + numTwo);
}

之后的jni目录结构如下:
在这里插入图片描述

六、配置CMake

已经完成了c/c++ 的代码,那么我们需要完成CMakeLists.txt的编写,以此编译c/c++代码,其实还有一些其它方法,但是这里使用CMake进行跨平台编译是非常方便的,而且之前也用到CMake,算是对CMake有一些了解,所以我们直接使用CMake:
目前Android Studio编写CMakeLists.txt还不能自动补全,高亮也要整体build时才能触发,写的还是有些难受,但是基本是模板,可以自己收集一下自己常用的(或者在外部使用notepad++等其它工具编写好后导入即可):

# 指定最小cmake版本
cmake_minimum_required(VERSION 3.0.0)

# 设置so输出位置
# 这里从CMakeLists.txt的当前位置开始的,会自动创建libs以及abi分类文件夹
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI})

# 设置cpp源文件位置及源文件
# 这里的相对路径从CMakeLists.txt文件位置开始
file(GLOB CPP_FILES "cpp/*.cpp")

# 这里指定头文件的位置
include_directories(include/)

# 设置生成的so名称及库类型
add_library(
    sdk
	SHARED
	${CPP_FILES}
)

执行编译前(CMakeLists.txt未高亮):
在这里插入图片描述

七、关联Gradle

这个是要将c/c++的编译关联到整个模块的编译中,所以修改该module的build.gradle这个脚本,修改后如下:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "com.knowyou.ndkcmakecompilecall"
        minSdkVersion 19
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        ndk {
            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    externalNativeBuild {
        cmake {
            path "src/main/jni/CMakeLists.txt"
        }
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation 'androidx.appcompat:appcompat:1.2.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

}

主要增加了abi过滤:

ndk {
    abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}

以及这里的CMakeLists.txt的相对路径:

externalNativeBuild {
    cmake {
        path "src/main/jni/CMakeLists.txt"
    }
}

八、编译so

上面的配置完成后在sdk module上右键run all Tests即可:
在这里插入图片描述
常见问题,提示ndk未设置则在项目结构中设置ndk即可:
在这里插入图片描述
编译成功后结果如下(在cmake指定的位置生成过滤abi的so库):
在这里插入图片描述

九、生成jar包

在模块的build.gradle中添加如下代码:

//设置sdk名称、版本、生成位置,解压的classes.jar包
def SDK_NAME = "SDK";
def SDK_VERSION = "_V1.0";
def sdkDestinationPath = "build";
def zipFile = file('build/intermediates/aar_main_jar/release/classes.jar')

//删除已编译的旧jar包
task deleteBuild(type: Delete) {
    delete sdkDestinationPath + SDK_NAME + SDK_VERSION + ".jar"
}

//编译jar包
task makeJar(type: Jar) {
    from zipTree(zipFile)
    from fileTree(dir: 'src/main',includes: ['assets/**'])
    baseName = SDK_NAME + SDK_VERSION
    destinationDir = file(sdkDestinationPath)
}

//执行上面的两个任务
makeJar.dependsOn(deleteBuild, build)

最终的build.gradle:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "com.knowyou.ndkcmakecompilecall"
        minSdkVersion 19
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        ndk {
            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    externalNativeBuild {
        cmake {
            path "src/main/jni/CMakeLists.txt"
        }
    }
}

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation 'androidx.appcompat:appcompat:1.2.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

}

//设置sdk名称、版本、生成位置,解压的classes.jar包
def SDK_NAME = "SDK";
def SDK_VERSION = "_V1.0";
def sdkDestinationPath = "build";
def zipFile = file('build/intermediates/aar_main_jar/release/classes.jar')

//删除已编译的旧jar包
task deleteBuild(type: Delete) {
    delete sdkDestinationPath + SDK_NAME + SDK_VERSION + ".jar"
}

//编译jar包
task makeJar(type: Jar) {
    from zipTree(zipFile)
    from fileTree(dir: 'src/main',includes: ['assets/**'])
    baseName = SDK_NAME + SDK_VERSION
    destinationDir = file(sdkDestinationPath)
}

//执行上面的两个任务
makeJar.dependsOn(deleteBuild, build)

常见问题(找不到classes.jar):
在这里插入图片描述
指定的classes.jar的位置不对,使用搜索工具在module目录下搜索classes.jar的位置后更改即可,比如我这里的位置:

def zipFile = file('build/intermediates/aar_main_jar/release/classes.jar')

在4.0的as中可以直接执行该打包功能,执行成功后在我们指定的build目录下生成了SDK_V1.0.jar:
在这里插入图片描述

十、最后

生成的so和jar包都成功了,下一次我们试一下看看ndk开发生成so和jar包如何使用。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昵称系统有问题

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值