【NDK】【010】通过Android.mk编译Native代码为so库

前面我们已经讲解过,如何在Java中使用JNI和DLL,这和在Android中使用JNI和SO原理是完全一致的,流程也相似,只是编译的平台和工具不同
如果我们前面都学透了,现在就会很轻松了。下面我们开始讲解,如何在Android中使用JNI

安装NDK开发环境

Tools - SDK Manager - 安装CMake,NDK,LLDB等组件
在这里插入图片描述
编写Java调用JNI的接口
在这里插入图片描述


	package com.easing.android;
	
	public class JniHello {
	
	    static {
	        System.loadLibrary("hello");
	    }
	
	    public native void hello();
	
	    public native void printMessage(String message);
	
	    public native int sum(int a, int b);
	}

根据Java接口生成JNI头文件

在java目录下打开Terminal面板,输入javah com.easing.android.JniHello指令,根据Java接口自动生成对应的JNI头文件
在这里插入图片描述


	#include <jni.h>
	
	#ifndef _Included_com_easing_android_JniHello
	#define _Included_com_easing_android_JniHello
	extern "C" {
	    JNIEXPORT void JNICALL Java_com_easing_android_JniHello_hello(JNIEnv *, jobject);
	    JNIEXPORT void JNICALL Java_com_easing_android_JniHello_printMessage(JNIEnv *, jobject, jstring);
	    JNIEXPORT jint JNICALL Java_com_easing_android_JniHello_sum(JNIEnv *, jobject, jint, jint);
	}
	#endif

编写CPP实现JNI头文件

在src下新建一个jni目录,专门用来编写c++代码
将刚才生成的头文件剪切到jni目录下,创建一个cpp文件,实现头文件中的接口
在这里插入图片描述


	#include <com_easing_android_JniHello.h>
	#include <android/log.h>
	#include <jni.h>
	#include <stdio.h>
	
	#define LOG_TAG "JniHello"
	#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
	#define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
	#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
	
	extern "C" JNIEXPORT void JNICALL Java_com_easing_android_JniHello_hello(JNIEnv *env, jobject obj) {
	    LOGD("JniHello");
	}
	
	extern "C" JNIEXPORT void JNICALL Java_com_easing_android_JniHello_printMessage(JNIEnv *env, jobject obj, jstring message) {
	    const char *str = env->GetStringUTFChars(message, NULL);
	    LOGI(str);
	}
	
	extern "C" JNIEXPORT jint JNICALL Java_com_easing_android_JniHello_sum(JNIEnv *env, jobject obj, jint a, jint b) {
	    return a + b;
	}

NDK出于安全性的考虑,默认限制了jstring到char*的转换,要经过适当配置才能使用,这个我们下篇博客我们再细讲

编写Android.mk和Application.mk文件

c++代码想编译成so库,必须通过Android.mk和Application.mk来进行配置
Android.mk指定so库名称,要编译的文件
Application.mk则指定要适配的CPU架构,最低的安卓系统版本
关于这两者更详细的配置,我们后面再细讲,现在重要的学会调用JNI完整流程
在这里插入图片描述

	
	#Android.mk
	
	LOCAL_PATH := $(call my-dir)
	include $(CLEAR_VARS)
	LOCAL_MODULE := libhello
	LOCAL_LDLIBS    := -lm -llog
	LOCAL_SRC_FILES := com_easing_android_JniHello.cpp
	include $(BUILD_SHARED_LIBRARY)

	
	#Application.mk
	
	APP_ABI := all
	APP_PLATFORM := android-23

设置支持的CPU架构

不同的CPU架构需要不同的so文件,如果我们要支持所有CPU架构的话,就需要编译并打包多个so文件,这样会大幅增加安装包体积
所以一般我们只会支持用户量最大(arm64-v8a)或兼容性最好(armeabi-v7a)的CPU架构
现在的新机子一般都是arm64-v8a架构的,使用arm64-v8a库运行效率最高
但是很多旧机子或旧的so库都是armeabi-v7a版本的,为了兼容一般都会添加armeabi-v7a支持
arm64-v8a机型也是可以使用armeabi-v7a的库的,如果不在意性能损失,可以只添加armeabi-v7a的so库,这样体积比较小,so包较少管理也方便

支持哪些CPU架构可以通过Gradle中的abiFilters选项来配置


	android {
	
	    defaultConfig {
	
	        //过滤CPU架构,只使用armv7的库
	        ndk {
	            abiFilters "armeabi-v7a"
	        }
	    }
	
	}

开启Gradle中的JNI编译选项

c++代码和mk文件编写完成后,我们有两种方式来使用jni代码
一种是让gradle自动将jni代码编译成为so库并引用
一种是通过ndk指令手动将其编译为so库,手动添加引用
第一种方式由于可以对c++代码自动编译自动引用,很适合经常修改c++源码的情景
第二种方式需要手动编译,麻烦一点,如果需要将so库分享给别人使用,则必须这么做

如果我们想Gradle帮我们自动编译C++代码,需要开启externalNativeBuild选项,并指定Android.mk位置
编译成功后的so库存放在build/intermediates/ndkBuild目录下,可以直接拷贝出来供其它用途
在这里插入图片描述


	android {
		
	    defaultConfig {
	    
	    }
	
	    //编译jni目录,开启这个选项后,会自动编译C++代码生成so文件,并自动引用
	    //开启此选项后,就不需要通过sourceSets选项来指定so库位置了,否则会发生so库冲突
	    externalNativeBuild {
	        ndkBuild {
	            path 'src/jni/Android.mk'
	        }
	    }
	
	}

在Gradle中指定so库加载目录

如果想直接使用编译好的so库,可以通过Gradle中的sourceSets选项来指定so库的加载位置
但是要注意,同样的so文件,不能既通过externalNativeBuild来编译,又通过sourceSets来引用
这样会有两份同名的so库,造成冲突,从而最终无法编译通过
在这里插入图片描述


	android {
	
	    defaultConfig {
	
	    }
	
	    //加载指定位置的so库
	    sourceSets {
	        main {
	            jniLibs.srcDirs = ['libs']
	        }
	    }
	
	}

使用NDK编译SO库

我们可以使用externalNativeBuild指令来编译出so文件,但这样生成的so文件会被直接使用
我们也可以手动执行ndk指令来编译so文件,这样可以自己决定如何使用

在jni目录下打开Terminal面板,输入ndk-build指令,就会在jni同级目录生成一个名为libs的so库文件夹
在这里插入图片描述
在Java代码中使用JNI接口

这个就简单了,new出一个JniHello对象,直接调用就行了,主要是验证下结果
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这些都是 Android NDK 内部的 `Android.mk` 文件。其中,`./android-ndk-r25c/sources/android/native_app_glue/Android.mk` 是用于编译 Native Activity 示例应用程序的 `Android.mk` 文件;`./android-ndk-r25c/sources/android/support/Android.mk` 是包含一些 Android 支持的 `Android.mk` 文件;`./android-ndk-r25c/sources/android/ndk_helper/Android.mk` 是包含一些辅助函数和类的 `Android.mk` 文件;`./android-ndk-r25c/sources/android/cpufeatures/Android.mk` 是用于编译 `cpufeatures` 的 `Android.mk` 文件,该提供了一些 CPU 相关的信息和功能;`./android-ndk-r25c/sources/cxx-stl/llvm-libc++abi/Android.mk` 和 `./android-ndk-r25c/sources/cxx-stl/llvm-libc++/Android.mk` 是用于编译 C++ STL 的 `Android.mk` 文件,分别对应 libc++abi 和 libc++ 两个 STL ;`./android-ndk-r25c/sources/third_party/googletest/Android.mk` 是用于编译 Google Test 测试框架的 `Android.mk` 文件;`./android-ndk-r25c/sources/third_party/shaderc/Android.mk` 是用于编译 Shaderc 编译器的 `Android.mk` 文件,该编译器可以将 GLSL 代码编译成 SPIR-V 代码;`./android-ndk-r25c/sources/third_party/shaderc/libshaderc/Android.mk` 是用于编译 Shaderc 的 `Android.mk` 文件;`./android-ndk-r25c/sources/third_party/shaderc/libshaderc_util/Android.mk` 是用于编译 Shaderc Util 的 `Android.mk` 文件,该提供了一些辅助函数和类;`./android-ndk-r25c/sources/third_party/shaderc/third_party/Android.mk` 是用于编译 Shaderc 编译器依赖的第三方的 `Android.mk` 文件,包括 glslang 和 spirv-tools 两个;`./android-ndk-r25c/sources/third_party/shaderc/third_party/glslang/Android.mk` 是用于编译 glslang 的 `Android.mk` 文件;`./android-ndk-r25c/sources/third_party/shaderc/third_party/spirv-tools/Android.mk` 是用于编译 spirv-tools 的 `Android.mk` 文件;`./android-ndk-r25c/sources/third_party/vulkan/src/build-android/jni/Android.mk` 是用于编译 Vulkan 的 `Android.mk` 文件。 如果您要在 Android NDK 中编写自己的 `Android.mk` 文件,可以参考这些示例文件。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值