Android NDK开发1——开发流程+依赖外部so+生成自实现so+静态注册JNI+动态注册JNI
背景:
在越来越卷的安卓生态中,一名安卓开发不仅要懂四大组件、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相关推荐
- 【Android NDK 开发】JNI 动态注册 ( 动态注册流程 | JNI_OnLoad 方法 | JNINativeMethod 结构体 | GetEnv | RegisterNatives )
文章目录 I . 动态注册流程 ( 总结 ) II . JNI_OnLoad 方法 III . 被注册的本地 C/C++ 方法参数 IV . JNINativeMethod 结构体 ( 核心重点 ) ...
- 【我的C语言学习进阶之旅】介绍一下NDK开发中关于JNI函数的两种注册方式:静态注册和动态注册
目录 一.要介绍本篇博客的原因 二.静态注册 2.1 实现原理 2.2 实现过程 2.3 弊端 2.4 示例 三.动态注册 3.1 实现原理 3.2 实现过程 3.3 优点 3.4 示例 一.要介绍本 ...
- android的动态注册,Android应用开发之BroadcastReceiver(广播)的静态注册和动态注册 --Android开发...
本文将带你了解Android应用开发之BroadcastReceiver(广播)的静态注册和动态注册 --Android开发,希望本文对大家学Android有所帮助 BroadcastReceiver ...
- NDK 开发之 JNI 方法静态注册与动态注册
1 前言 上文说到,进行 NDK 开发的时候,我们首先需要把 Java 方法声明为 native,然后编写对应的 C/C++ 代码,并编译成为动态链接库,在调用 Java 方法前加载动态链接库即可调用 ...
- Android复习12【广播接收者-BroadcastReceiver(简单案例-发送广播、静态注册、动态注册、本地广播、代码示例(别处登陆踢用户下线)、常用系统广播总结、音乐播放器)】
2020-04-28[11周-周二] 音乐播放器Android代码下载:https://wws.lanzous.com/ifqzihaxvij 目 录 简单案例-发送广播 2)动态注册实例(监听网 ...
- Android之JNI动态注册native方法和JNI数据简单使用
1.爆结果照片 2.介绍JNI注册方式 JVM 查找 native 方法有两种方式: 1).按照 JNI 规范的命名规则(静态注册) 2) .调用 JNI 提供的 RegisterNat ...
- Android 实现JNI动态注册
Android 实现JNI的动态注册 前景 什么是静态注册 静态注册的优缺点 优点 缺点 什么是动态注册 实现 创建步骤 用到的方法 结束 前景 JNI可以说是Java 和Native 的桥梁 起承上 ...
- Android NDK学习(3)使用Javah命令生成JNI头文件 .
转:http://www.cnblogs.com/fww330666557/archive/2012/12/14/2817387.html 第一步: 在Eclipse中创建android项目,并声明N ...
- 安卓逆向_15( 三 ) --- Android NDK 开发【 jni 静态注册、JNI_OnLoad 动态注册】
Android Studio开发JNI示例:https://blog.csdn.net/wzhseu/article/details/79683045 JNI_动态注册_静态注册.zip : http ...
- Android逆向 学习Android安全和逆向开发的路线总结,啃下这些Framework技术笔记
此篇整理了最完整的–Android逆向学习线路知识体系.希望给迷糊的入门者指出一个明确的方向. 真心建议:先正向开发几年再搞逆向吧--正向都不会破解的是啥?不看代码只会脱壳?只会xposed ?远远不 ...
最新文章
- vagrant 简单使用
- ASP.NET Core 3.x API版本控制
- MachineLearning(7)-决策树基础+sklearn.DecisionTreeClassifier简单实践
- 机器视觉:系统不稳定性因素分析
- 计算机视觉和机器学习,代码,论文大全
- Mac查看占用端口进程
- IPMP、PMP、ACP、PRINCE2的区别
- xmind zen 用法
- java session时间_java设置session过期时间的实现方法
- 中国支付清算系统简介
- 前端开发常见的英语词汇整理
- rust服务器人数查询网站,Rust Web框架列表
- 图的同构识别算法——C++代码实现
- 蓝桥杯单片机——PWM脉宽调制(10)
- html方框打勾字段,HTML+CSS入门 如何设置 checkbox复选框控件的对勾√样式
- 【论文笔记】Deep Learning on Graphs: A Survey
- dellr420部署os_戴尔dell poweredge r730服务器系统安装配置详解教程
- 【Markdown语法】字体颜色大小及文字底色设置
- SQLite.Interop.dll 没有拷贝到输出目录
- 基于Matlab 实现螺旋线 轨迹曲线绘制
热门文章
- 计算机科学导论实验,《计算机科学导论》实验.doc
- Python xlwt 操作 excel 表格基础(一):单元格写入、合并、插入位图等
- 微信H5多级分佣开心刮刮乐源码
- 【HMS core】【Analytics Kit】华为分析服务常见问题FAQ 2
- 网页html生成图片的常用方案
- 测试开发是什么?什么是测试开发工程师?
- 华硕路由器无线打印服务器怎么开启,华硕ASUS路由器无线中继模式设置教程
- dorado7.x argument type mismatch
- java怎么解析json_基于java解析JSON的三种方式详解
- Git Windows下配置Merge工具DiffMerge