背景:

在越来越卷的安卓生态中,一名安卓开发不仅要懂四大组件、Handler、View绘制与事件分发、RecyclerView、动画、JetPack、组件化、插件化、热修复、性能优化、Framework、各种开源框架OKhttp、Retrofit、Eventbus、MMKV等等。近年来开始卷到了Native层,NDK开发是安卓领域必备的技能。项目开发过程中,往往有需要在Native层开发的场景:隐私数据加解密、音视频编解码、人脸检测追踪等AI能力......

本文是个人学习NDK开发小结:NDK开发流程、如何依赖外部So以及对自实现的cpp文件生成so、和JNI接口静态注册+动态注册。

一、NDK开发环境搭建

1) 个人使用的Android studio是

2)先加载NDK和CMake

3)新建一个nativeLib  Module,个人比较喜欢用单独的Module 来做Native层逻辑,后续便于做组件化,如果写在app层,后续耦合严重。

4)在nativelib/src下新建一个CMakeLists.txt文件。

#构建库文件所需要CMake最小版本
cmake_minimum_required(VERSION 3.4.1)
# 设置生成的so动态库输出的路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/jniLibs/${ANDROID_ABI})
#添加自己的C/C++源文件
add_library( # Sets the name of the library.native_test# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).src/main/cpp/native_test.cpp)
#添加依赖的NDK 库
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 )#添加外部依赖的so
add_library(aesutilSHAREDIMPORTED)
set_target_properties(aesutilPROPERTIES IMPORTED_LOCATION${PROJECT_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libaesutil.so
)
include_directories(src/main/cpp/include)#将目标库与NDK 中的库链接
target_link_libraries( # Specifies the target library.native_test# Links the target library to the log library# included in the NDK.aesutil${log-lib} )

5) nativelib module的build.gradle文件里需要声明和ndk相关配置

android {compileSdkVersion 30defaultConfig {minSdkVersion 21targetSdkVersion 30versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"consumerProguardFiles "consumer-rules.pro"//1. android-defaultConfig {}下 externalNativeBuild  cmake pathexternalNativeBuild {cmake {cppFlags ""arguments "-DANDROID_STL=c++_shared"}}//2. android-defaultConfig {}下 选择生成的CPU 架构,  android defaultConfig下定义ndk{abiFilters "armeabi-v7a", "arm64-v8a"}}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}//3. android{}下 externalNativeBuild  cmake pathexternalNativeBuild {cmake {path "CMakeLists.txt"}}sourceSets {main {jniLibs.srcDirs = ['libs']}}}

二、依赖外部so和编译自实现的so以及JNI接口调用

1)在nativelib/src/main/java/包名 下 新建一个类JNIHelper.java类 用于写JNI接口。

package com.mikel.nativelib;public class JNIHelper {static {System.loadLibrary("native_test");}/**** 静态注册* @return*/public static native String testJNI();public static native String encryptJNI(String originStr);public static native String decryptJNI(String enCodeStr);/*** 动态注册*/public static native String dynamicMethodTest(int intValue, float floatValue, double doubleValue, byte bteValue,String strValue, boolean boolValue,int[] intArrayValue, float[] floatArrayValue, double[] doubleArrayValue,byte[] byteArrayValue, boolean[] boolArrayValue);
}

2)在nativelib/src/main 下 新建一个cpp文件夹,并且新建目录include用于存放第三方依赖的cpp头文件,新建一个native_test.cpp用于实现JNI接口。

静态注册

优点:实现简单;缺点:JNI函数名很长,需要捆绑包名和类名,运行时进行匹配 影响性能

动态注册

优点:System.loadLibrary的时候调用JNI_onLoad进行注册,提高性能;

#include <jni.h>
#include <string.h>
#include <stdlib.h>
#include <android/log.h>
#include "aes.h"
#include <cassert>
static const char *TAG = "native_test";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)extern "C" {/**********************************静态注册****************************************/JNIEXPORT jstring JNICALL Java_com_mikel_nativelib_JNIHelper_testJNI(JNIEnv *env, jclass thiz) {char *helloworld = "hello world in C++";return env->NewStringUTF(helloworld);}JNIEXPORT jstring JNICALLJava_com_mikel_nativelib_JNIHelper_encryptJNI(JNIEnv *env, jobject thiz, jstring origin) {const char *origin_str;const char *key_str = "123456789abcdef";origin_str = env->GetStringUTFChars(origin, 0);char encrypt_str[1024] = {0};AES aes_en((unsigned char *) key_str);aes_en.Cipher((char *) origin_str, encrypt_str);return env->NewStringUTF(encrypt_str);}JNIEXPORT jstring JNICALLJava_com_mikel_nativelib_JNIHelper_decryptJNI(JNIEnv *env, jobject thiz, jstring des) {const char *des_str;const char *key_str = "123456789abcdef";des_str = env->GetStringUTFChars(des, 0);char decrypt_str[1024] = {0};AES aes_de((unsigned char *) key_str);aes_de.InvCipher((char *) des_str, decrypt_str);return env->NewStringUTF(decrypt_str);}/****************************************动态注册*********************************************************/jstring dynamic_method_test(JNIEnv *env, jobject thiz,jint intValue, jfloat floatValue, jdouble doubleValue, jbyte byteValue,jstring strValue, jboolean boolValue, jintArray intArrayValue,jfloatArray floatArrayValue, jdoubleArray doubleArrayValue,jbyteArray byteArrayValue, jbooleanArray boolArrayValue) {//转指针const char* strBuf = env->GetStringUTFChars(strValue, nullptr);jint* intBuf = env->GetIntArrayElements(intArrayValue, nullptr);jfloat* floatBuf = env->GetFloatArrayElements(floatArrayValue, nullptr);jdouble* doubleBuf = env->GetDoubleArrayElements(doubleArrayValue, nullptr);jbyte* byteBuf = env->GetByteArrayElements(byteArrayValue, nullptr);jboolean* boolBuf = env->GetBooleanArrayElements(boolArrayValue, nullptr);//handle buf//释放指针env->ReleaseStringUTFChars(strValue, strBuf);env->ReleaseIntArrayElements(intArrayValue, intBuf, 0);env->ReleaseFloatArrayElements(floatArrayValue, floatBuf, 0);env->ReleaseDoubleArrayElements(doubleArrayValue, doubleBuf, 0);env->ReleaseByteArrayElements(byteArrayValue, byteBuf, 0);env->ReleaseBooleanArrayElements(boolArrayValue, boolBuf, 0);return env->NewStringUTF(strBuf);}static JNINativeMethod dynamicMethods[] = {//public String dynamicMethodTest(int a, float b, double c, byte d, String e, boolean f, int[] g,//                             float[] h, double[] i, byte[] j, boolean[] l){"dynamicMethodTest","(IFDBLjava/lang/String;Z[I[F[D[B[Z)Ljava/lang/String;",(void*)dynamic_method_test},};static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* dynamicMethods,int methodsNum){jclass clazz;clazz = env->FindClass(className);if(clazz == NULL){return JNI_FALSE;}//env->RegisterNatives 注册函数需要参数:java类,动态注册函数的数组,动态注册函数的个数if(env->RegisterNatives(clazz, dynamicMethods, methodsNum) < 0){return JNI_FALSE;}return JNI_TRUE;}//指定java层的类路径,然后动态注册函数static int registerNatives(JNIEnv* env){const char* className  =  "com/mikel/nativelib/JNIHelper";return registerNativeMethods(env, className, dynamicMethods, sizeof(dynamicMethods)/ sizeof(dynamicMethods[0]));}JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){JNIEnv* env = NULL;//通过Java虚拟机获取JNIEnvint result = vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);if (result != JNI_OK) {return -1;}assert(env != NULL);if(!registerNatives(env)){return -1;}//返回JNI版本return JNI_VERSION_1_6;}
}

3) 在nativelib/src/main 下新建一个文件夹jniLibs用于存放外部依赖的so。这里选择v7a和v8a的cpu架构,基本可以涵盖绝大部分机型。

4) build-make project 可以生成自己的so

 三、效果:

参考:

Android中利用C语言进行AES加解密_水月洞天-CSDN博客

Android开发NDK调用三方so库 - 简书

Demo地址:

GitHub - mikelhm/MikelProjectDemo: Personal Android Demo

Android NDK开发1——开发流程+依赖外部so+生成自实现so+静态注册JNI+动态注册JNI相关推荐

  1. 【Android NDK 开发】JNI 动态注册 ( 动态注册流程 | JNI_OnLoad 方法 | JNINativeMethod 结构体 | GetEnv | RegisterNatives )

    文章目录 I . 动态注册流程 ( 总结 ) II . JNI_OnLoad 方法 III . 被注册的本地 C/C++ 方法参数 IV . JNINativeMethod 结构体 ( 核心重点 ) ...

  2. 【我的C语言学习进阶之旅】介绍一下NDK开发中关于JNI函数的两种注册方式:静态注册和动态注册

    目录 一.要介绍本篇博客的原因 二.静态注册 2.1 实现原理 2.2 实现过程 2.3 弊端 2.4 示例 三.动态注册 3.1 实现原理 3.2 实现过程 3.3 优点 3.4 示例 一.要介绍本 ...

  3. android的动态注册,Android应用开发之BroadcastReceiver(广播)的静态注册和动态注册 --Android开发...

    本文将带你了解Android应用开发之BroadcastReceiver(广播)的静态注册和动态注册 --Android开发,希望本文对大家学Android有所帮助 BroadcastReceiver ...

  4. NDK 开发之 JNI 方法静态注册与动态注册

    1 前言 上文说到,进行 NDK 开发的时候,我们首先需要把 Java 方法声明为 native,然后编写对应的 C/C++ 代码,并编译成为动态链接库,在调用 Java 方法前加载动态链接库即可调用 ...

  5. Android复习12【广播接收者-BroadcastReceiver(简单案例-发送广播、静态注册、动态注册、本地广播、代码示例(别处登陆踢用户下线)、常用系统广播总结、音乐播放器)】

    2020-04-28[11周-周二] 音乐播放器Android代码下载:https://wws.lanzous.com/ifqzihaxvij 目   录 简单案例-发送广播 2)动态注册实例(监听网 ...

  6. Android之JNI动态注册native方法和JNI数据简单使用

    1.爆结果照片 2.介绍JNI注册方式 JVM 查找 native 方法有两种方式:     1).按照 JNI 规范的命名规则(静态注册)    2) .调用 JNI 提供的 RegisterNat ...

  7. Android 实现JNI动态注册

    Android 实现JNI的动态注册 前景 什么是静态注册 静态注册的优缺点 优点 缺点 什么是动态注册 实现 创建步骤 用到的方法 结束 前景 JNI可以说是Java 和Native 的桥梁 起承上 ...

  8. Android NDK学习(3)使用Javah命令生成JNI头文件 .

    转:http://www.cnblogs.com/fww330666557/archive/2012/12/14/2817387.html 第一步: 在Eclipse中创建android项目,并声明N ...

  9. 安卓逆向_15( 三 ) --- Android NDK 开发【 jni 静态注册、JNI_OnLoad 动态注册】

    Android Studio开发JNI示例:https://blog.csdn.net/wzhseu/article/details/79683045 JNI_动态注册_静态注册.zip : http ...

  10. Android逆向 学习Android安全和逆向开发的路线总结,啃下这些Framework技术笔记

    此篇整理了最完整的–Android逆向学习线路知识体系.希望给迷糊的入门者指出一个明确的方向. 真心建议:先正向开发几年再搞逆向吧--正向都不会破解的是啥?不看代码只会脱壳?只会xposed ?远远不 ...

最新文章

  1. vagrant 简单使用
  2. ASP.NET Core 3.x API版本控制
  3. MachineLearning(7)-决策树基础+sklearn.DecisionTreeClassifier简单实践
  4. 机器视觉:系统不稳定性因素分析
  5. 计算机视觉和机器学习,代码,论文大全
  6. Mac查看占用端口进程
  7. IPMP、PMP、ACP、PRINCE2的区别
  8. xmind zen 用法
  9. java session时间_java设置session过期时间的实现方法
  10. 中国支付清算系统简介
  11. 前端开发常见的英语词汇整理
  12. rust服务器人数查询网站,Rust Web框架列表
  13. 图的同构识别算法——C++代码实现
  14. 蓝桥杯单片机——PWM脉宽调制(10)
  15. html方框打勾字段,HTML+CSS入门 如何设置 checkbox复选框控件的对勾√样式
  16. 【论文笔记】Deep Learning on Graphs: A Survey
  17. dellr420部署os_戴尔dell poweredge r730服务器系统安装配置详解教程
  18. 【Markdown语法】字体颜色大小及文字底色设置
  19. SQLite.Interop.dll 没有拷贝到输出目录
  20. 基于Matlab 实现螺旋线 轨迹曲线绘制

热门文章

  1. 计算机科学导论实验,《计算机科学导论》实验.doc
  2. Python xlwt 操作 excel 表格基础(一):单元格写入、合并、插入位图等
  3. 微信H5多级分佣开心刮刮乐源码
  4. 【HMS core】【Analytics Kit】华为分析服务常见问题FAQ 2
  5. 网页html生成图片的常用方案
  6. 测试开发是什么?什么是测试开发工程师?
  7. 华硕路由器无线打印服务器怎么开启,华硕ASUS路由器无线中继模式设置教程
  8. dorado7.x argument type mismatch
  9. java怎么解析json_基于java解析JSON的三种方式详解
  10. Git Windows下配置Merge工具DiffMerge