赞
踩
前言:
本文章以Android Studio为IDE,以手动编译不使用cmake的方式为例,在某个已有普通android项目基础上,创建jni程序并运行。详细介绍以下内容:
1、环境配置(Android Studio、SDK、JDK、NDK、Gradle)
2、创建java中的jni程序
3、根据java中的jni程序生成头文件,创建c/c++文件,创建配置文件
4、打包生成 *.so 库文件
5、配置项目引用创建好的 *.so 库文件并运行
6、External Tools 工具的配置和使用
准备好了吗?现在开干!
1、环境配置:首先电脑环境为 Windows10 64位操作系统,mac系统暂不涉及。由于Android Studio与Gradle,SDK,NDK,JDK的版本兼容问题,特意使用如下版本进行配置
Android Studio 4.0.2
SDK 30
NDK 21.3.6528147
JDK 1.8
1.1、Android Studio
Android Studio 下载文件归档 | Android 开发者 | Android Developers (google.cn)
进入网站,滚动到底部,点击“我同意这些条款”
然后在浏览器打开的界面中搜索 4.0.2
下载并安装好Android Studio后,第一次打开可能报错,不要急,复制错误内容搜索一下,很简单就能解决。正常打开Android Studio后,新建一个项目或者导入一个已有项目,在此不做过多说明。
1.2、SDK:这里只对我的SDK版本做说明,不代表其他版本不可
1.3、JDK:这里我使用JDK 1.8,不代表其他版本不可
官网下载地址:Java Downloads | Oracle
1.4、NDK:具体的NDK配置流程可以自行查询,网上很多。我使用的NDK版本为 21.3.6528147
1.5、Gradle:其他的Gradle版本未测试,这里贴出我使用的构建版本
1.5.1、gradle-wrapper.properties
打开文件,配置项如下
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
1.5.2、Project -> build.gradle
打开文件,配置项如下
classpath 'com.android.tools.build:gradle:4.0.2'
2、创建java中的jni程序:先创建一个类,在类中分别加入“静态加载so文件的程序”和“定义的native方法”
注意:包名类名方法名定义好后,不要随意更改,后面需要生成对应的头文件
- package com.ndktest.jnilib;
-
- /**
- * jni方法工具类
- *
- * @author Wesley Yan
- */
- public class MyJniTest {
-
- static {
- System.loadLibrary("jniTestSo");
- }
-
- /**
- * 计算 a+b 的和
- * @param a int
- * @param b int
- *
- * @return int 返回 a+b 的结果值
- */
- public static native int getAddData(int a, int b);
-
- /**
- * 获取so库中的字符串
- */
- public static native String getStringFromJni();
-
- }
3、根据java中的jni程序生成头文件,创建c/c++文件,再创建两个配置文件Application.mk、Android.mk
3.1、根据 MyJniTest 类生成对应的头文件
在terminal中cd到 app/src/main/java 目录下
执行以下命令
javah -encoding utf-8 -d ../jni -jni com.ndktest.jnilib.MyJniTest
正确执行后,切换Android Studio中的导览图类型为project,在src/main/java同级别的jni目录下就能看到生成的头文件 com_ndktest_jnilib_MyJniTest.h
打开 com_ndktest_jnilib_MyJniTest.h 文件,可以看到里面会定义出我们定义的native方法所对应的头文件中的方法,这里不需要做任何修改
3.2、根据头文件创建对应的 c/c++ 文件:
在头文件所在的jni目录:右键 -> New -> File
命名为 demo.c
在 demo.c 文件中导入头文件,加入头文件中方法的实现
- //
- // Created by wct on 2023/5/8.
- //
-
- //#include <jni.h>
- #include "com_ndktest_jnilib_MyJniTest.h"
-
- /*
- * Class: com_ndktest_jnilib_MyJniTest
- * Method: getAddData
- * Signature: (II)I
- */
- JNIEXPORT jint JNICALL
- Java_com_ndktest_jnilib_MyJniTest_getAddData(JNIEnv *env, jclass thiz, jint a, jint b) {
- return a + b;
- }
-
- /*
- * Class: com_ndktest_jnilib_MyJniTest
- * Method: getStringFromJni
- * Signature: ()Ljava/lang/String;
- */
- JNIEXPORT jstring JNICALL
- Java_com_ndktest_jnilib_MyJniTest_getStringFromJni(JNIEnv *env, jclass thiz) {
- return (*env)->NewStringUTF(env, "Hello World from c++");
- }
3.3、创建对应的配置文件 Application.mk
在头文件所在的jni目录:右键 -> New -> File
命名为 Application.mk
再把相关配置属性加入其中,可直接复制下面的代码到 Application.mk
- # Application.mk 参数
-
-
- # 默认生成支持的多种类型.so
- APP_ABI := all
-
- # APP_PLATFORM := android-16不配置,打包.so会出错
- APP_PLATFORM := android-16
-
- # 可选项,如果没有定义,则NDK编译所有Android.mk中的modules.如果定义了,则只编译Android.mk中被APP_MODULES指定的模块以及他们所依赖的模块。
- APP_MODULES := jniTestSo
3.4、创建对应的配置文件 Android.mk
在头文件所在的jni目录:右键 -> New -> File
命名为 Android.mk
再把相关配置属性加入其中,可直接复制下面的代码到 Android.mk
- # Android.mk 参数
-
-
-
- # 设置工作目录,它用于在开发tree中查找源文件。宏my-dir由Build System提供,会返回Android.mk文件所在的目录
- LOCAL_PATH := $(call my-dir)
-
- # CLEAR_VARS变量由Build System提供。指向一个指定的GNU Makefile,由它负责清理LOCAL_xxx类型文件,但不是清理LOCAL_PATH
- # 所有的编译控制文件由同一个GNU Make解析和执行,其变量是全局的。所以清理后才能便面相互影响,这一操作必须有
- include $(CLEAR_VARS)
-
- # LOCAL_MODULE模块必须定义,以表示Android.mk中的每一个模块,名字必须唯一且不包含空格
- # Build System 会自动添加适当的前缀和后缀。例如,demo,要生成动态库,则生成libdemo.so。但请注意:如果模块名字被定义为libabc,则生成libabc.so。不再添加前缀。
- LOCAL_MODULE := jniTestSo
-
- # 指定参与模块编译的C/C++源文件名。不必列出头文件,build System 会自动帮我们找出依赖文件。缺省的C++ 源码的扩展名为.cpp
- LOCAL_SRC_FILES =: demo.c
-
- # BUILD_SHARED_LIBRARY是Build System提供的一个变量,指向一个GUN Makefile Script。它负责收集自从上次调用include $(CLEAR_VARS)后的所有LOCAL_xxxxinx。并决定编译什么类型
- # 1. BUILD_STATIC_LIBRARY:编译为静态库
- # 2. BUILD_SHARED_LIBRARY:编译为动态库
- # 3. BUILD_EXECUTABLE:编译为Native C 可执行程序
- # 4. BUILD_PREBUILT:该模块已经预先编译
- include $(BUILD_SHARED_LIBRARY)
4、打包生成 *.so 库文件:这里我在Android.mk中配置属性 LOCAL_MODULE := jniTestSo,因此生成的so文件名为 libjniTestSo.so,生成的方式如下
在terminal中cd到 app/src/main/java 目录下
执行以下命令
ndk-build
正确执行后,切换Android Studio中的导览图类型为project,在src/main/java同级别的libs目录下就能看到生成的两个文件夹 libs 和 obj,其中src/main/libs文件夹下就能看到不同ABI对应的so库文件
5、配置已有项目引用创建好的 *.so 库文件并运行:
5.1、复制生成的so库文件到项目指定目录:
找到设备对应可用的ABI类型,这里我的设备支持的ABI为 arm64-v8a,因此只需要把 src/main/libs 下的 arm64-v8a 文件夹拷贝到 app/libs 目录中,如果你的设备支持其他的ABI类型,复制对应的文件即可
5.2、打开 Module 中的 build.gradle 文件,在 buildTypes 中增加 sourceSets 配置
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
-
- // 加载本地so文件配置(必选)
- sourceSets {
- main {
- jniLibs.srcDir(['libs'])
- }
- }
- }
5.3、打开 gradle.properties 文件,添加 android.useDeprecatedNdk 属性
- # 添加对旧版本的NDK支持,此项目使用NDK版本=21.3.6528147,不配置以下参数也可
- android.useDeprecatedNdk=true
5.4、测试调用 MyJniTest 类中的 jni 方法
- // 测试jni调用so库方法
- int getAddData = MyJniTest.getAddData(2, 6);
- String getStringFromJni = MyJniTest.getStringFromJni();
- textView_message.setText("getAddData=" + getAddData + "\ngetStringFromJni=" + getStringFromJni);
5.5、运行程序:我这里使用Android Studio连接真机直接运行
上图中输出的结果分别对应我在 demo.c 文件中的方法实现
6、External Tools 工具的配置和使用:配置一次,终生幸福(借用前人的一句话)
在上述 3.1生成头文件 和 4生成so库文件 的过程中,都使用到了Android Studio自带的命令行工具,分别执行了 javah 和 ndk-build 命令。Android Studio 还为我们提供了另一种执行终端命令的工具,就是External Tools,它可以帮助我们更简单的实现 3.1 和 4 中的工作。
6.1、配置 External Tools -> javah
点击 Android Studio 中的 File -> settings,在打开面板的搜索栏中输入 External Tools,选中Tools 下的 External Tools,再点击 +号,填写如下配置,再点击 “OK”按钮保存
- Program:
- // 这里改为你的 JDK 中 bin 目录下面的 javah.exe 文件目录
- C:\Program Files\Java\jdk-1.8\bin\javah.exe
-
- Arguments:
- // 不用改,直接复制即可
- -encoding UTF-8 -d ../jni -jni $FileClass$
-
- Working directory:
- // 不用改,直接复制即可
- $ProjectFileDir$\app\src\main\java
6.2、配置 External Tools -> ndk-build
继续点击 +号,填写如下配置,再点击“OK”按钮保存
- Program:
- // 这里改为你的 NDK 对应的 ndk-build.cmd 文件目录
- $ModuleSdkPath$/ndk-bundle/21.3.6528147/ndk-build.cmd
-
- Arguments:
- // 指定输出so库文件的目录,不用改,直接复制即可
- NDK_LIBS_OUT=$ProjectFileDir$\app\src\main\libs
-
- Working directory:
- // 不用改,直接复制即可
- $ProjectFileDir$\app\src\main
6.4、使用 External Tools -> ndk-build
在上述 3.1 生成头文件时,右键点击要生成头文件对应的类,然后依次选中
External Tools -> javah
6.3、使用 External Tools -> ndk-build
在上述 4 生成so文件时,右键点击 src/main 下面的 jni 目录,然后依次选中
External Tools -> ndk-build
写在最后,如果你跟着上面的步骤,运行出了自己的jni程序,相信过程中也一定有所收获,码字不易,与君共勉!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。