Android NDK开发——打通任督二脉

2023-11-22 10:20

本文主要是介绍Android NDK开发——打通任督二脉,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一个始终不被善待的人,最能识得善良,也最能珍视善良 —— 《芳华》

这里不会从头开始讲如何搭建环境之类的,因为Google早有详细文档说明供你起步了向您的项目添加 C 和 C++ 代码;但是可能又有人说了Google无法在国内访问呢,我只想说朋友方法总比困难多,购买VPN修改Host都可以并且网上也有很多环境搭建的教程;有兴趣自己搭建的可以移步这里vultr;

为什么使用NDK

对于常年奋斗在应用层的同学来说,可能会很少接触NDK,最多也是使用别人提供的so直接通过JNI调用(说白了其实还是Java的接口);但是Android博大精深远远非应用层能概括的,相信大家都知道Android体系结构;有大量的开源的库都是非Java写的为了使用这些已有的C库我们只能用JNI的方式,或者如果我们想从事驱动开发,又或者我们自己辛苦开发的代码,不被别人轻易的破解窃取等等(当然我本人是很推崇open source,但是商业上难从之);其本质原因都是为了让自己的水平更上一层楼 ^_^;

此处输入图片的描述

Hello Jni

接下来我们来从Jni中获取字符串,当然自带的项目中已经添加了Hello Jni的方法。但是我测试的方式却有些不同

玩转指针

Jni接口定义如下

public class JniTest {public static native long getStringFromJni();public static native String getStringFromJni(long p);
}

Jni实现如下

#include <jni.h>extern "C" {JNIEXPORT jlong JNICALL
Java_com_xiaozhi_jni_JniTest_getStringFromJni__(JNIEnv *env, jobject instance) {char *p = "Hello from Jni!";return (jlong) p;
}JNIEXPORT jstring JNICALL
Java_com_xiaozhi_jni_JniTest_getStringFromJni__J(JNIEnv *env, jobject instance, jlong in) {char *p = (char *) in;return env->NewStringUTF(p);
}}

MainActivity中调用如下

public class MainActivity extends AppCompatActivity {// Used to load the 'native-lib' library on application startup.static {System.loadLibrary("native-lib");}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// Example of a call to a native methodTextView tv = (TextView) findViewById(R.id.sample_text);long p = JniTest.getStringFromJni();tv.setText(JniTest.getStringFromJni(p));}
}

看到上面的测试方法我们并没有直接返回字符,而是在c层面将字符的指针地址传出再进行传入获取字符串,是不是很有意思^_^(此种方式多用于c中申请的内存空间的句柄交由java层保留处理)

C反射调用Java

既然题目是说打通任督二脉,那我们就不可能只说Java调用C了,来来来兄弟干了这碗热翔咱们继续聊。

Java方法签名

在用c反射java时我们一定要先了解方法签名这个知识点,方法签名用来干嘛呢?主要用来标识方法中的返回值以及参数的说明,举例如下:
public void test1(){}
签名:()V
public void test2(String str)
签名:(Ljava/lang/String;)V
public byte[] encryB(byte[] src, int srcLen, byte[] dst,int dstLen, long env)
签名:([BI[BIJ)[B

()中表示方法的参数说明,()外表示返回值的类型说明

方法签名JNIjava
Vvoidvoid
Zjbooleanboolean
Ijintint
Jjlonglong
Djdoubledouble
Fjfloatfloat
Bjbytebyte
Cjcharchar
Sjshortshort
[ZjbooleanArrayboolean[]
[IjintArrayint[]
[JjlongArraylong[]
[DjdoubleArraydouble[]
[FjfloatArrayfloat[]
[BjbyteArraybyte[]
[CjcharArraychar[]
[SjshortArrayshort[]
L全类名jobjectclass

[ 数组 以[开头,配合其他的特殊字符,表示对应数据类型的数组,几个[表示几维数组
L全类名; 引用类型 以L开头、;结尾,中间是引用类型的全类名

Jni中获取App包名

调用Java中对象的方法总共分几步?
1.找到方法所在的类
2.找到方法ID
3.通过实例对象调用ID的方法

jni接口定义

public static native String getPackagenameFromJni(Context context);

jni实现

JNIEXPORT jstring JNICALL
Java_com_xiaozhi_jni_JniTest_getPackagenameFromJni(JNIEnv *env, jclass type, jobject context) {// 1.Get Context Class descriptorjclass contextClass = env->FindClass("android/content/Context");// 2.Get methodId from Context classjmethodID getPackageNameMethodId = env->GetMethodID(contextClass, "getPackageName","()Ljava/lang/String;");// 3.Call the method using contextjstring stringObject = (jstring) env->CallObjectMethod(context, getPackageNameMethodId);// TODOconst char *packageName = env->GetStringUTFChars(stringObject, 0);int isOk = strcmp(packageName, "com.xiaozhi.jni");LOGI("包名 isOK:%d", isOk);return stringObject;
}

android调用

public class MainActivity extends AppCompatActivity {// Used to load the 'native-lib' library on application startup.static {System.loadLibrary("native-lib");}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// Example of a call to a native methodTextView tv = (TextView) findViewById(R.id.sample_text);long p = JniTest.getStringFromJni();tv.setText(JniTest.getStringFromJni(p) + "\n");// 获取包名tv.append(JniTest.getPackagenameFromJni(this) + "\n");}
}

JNI_OnLoad注册本地方法

与传统的方式生成jni头文件的形式不同,本地方法注册有以下优点

  • 效率更高(Android官方建议的方式)
  • 避免了的繁琐的命名方式,更加灵活

具体注册请看以下方式

/*** 声明映射本地方法和java层方法的映射关系*/
static JNINativeMethod methods[]{{"getStringFromJni",      "()J",                                           (void *) getStringFromJni__},{"getStringFromJni",      "(J)Ljava/lang/String;",                         (void *) getStringFromJni__J},{"getPackagenameFromJni", "(Landroid/content/Context;)Ljava/lang/String;", (void *) getPackagenameFromJni}
};/*** 注册本地方法** @param env* @param class_name* @param methods* @param num_methods* @return*/
jint registerNativeMethods(JNIEnv *env, const char *class_name, JNINativeMethod *methods,int num_methods) {jclass clazz = env->FindClass(class_name);int result = env->RegisterNatives(clazz, methods, num_methods);return result;
}/*** 与对应java类关联** @param env* @return*/
int registerNative(JNIEnv *env) {const char *className = "com/xiaozhi/jni1/JniTest";return registerNativeMethods(env, className, methods,((int) sizeof(methods) / sizeof(methods[0])));
}//当动态库被加载时这个函数被系统调用(调用System.loadLibrary时回掉)
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {JNIEnv *env;if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {return JNI_ERR;}// register native methodsif (registerNative(env)) {return JNI_ERR;}// return jni versionreturn JNI_VERSION_1_6;
}//当动态库被卸载时这个函数被系统调用
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
}

以上便是最近使用NDK的一些小记,不算太有深度的东西,但是也算在集成别的C库是必须要懂的姿势 :);后续会写一些Android下具体使用c库的例子,如libyuv,ffmpeg等;

项目源码:NDKTest

Google官方NDK示例

这篇关于Android NDK开发——打通任督二脉的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/409452

相关文章

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

C#图表开发之Chart详解

《C#图表开发之Chart详解》C#中的Chart控件用于开发图表功能,具有Series和ChartArea两个重要属性,Series属性是SeriesCollection类型,包含多个Series对... 目录OverviChina编程ewSeries类总结OverviewC#中,开发图表功能的控件是Char

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

鸿蒙开发搭建flutter适配的开发环境

《鸿蒙开发搭建flutter适配的开发环境》文章详细介绍了在Windows系统上如何创建和运行鸿蒙Flutter项目,包括使用flutterdoctor检测环境、创建项目、编译HAP包以及在真机上运... 目录环境搭建创建运行项目打包项目总结环境搭建1.安装 DevEco Studio NEXT IDE

Python开发围棋游戏的实例代码(实现全部功能)

《Python开发围棋游戏的实例代码(实现全部功能)》围棋是一种古老而复杂的策略棋类游戏,起源于中国,已有超过2500年的历史,本文介绍了如何用Python开发一个简单的围棋游戏,实例代码涵盖了游戏的... 目录1. 围棋游戏概述1.1 游戏规则1.2 游戏设计思路2. 环境准备3. 创建棋盘3.1 棋盘类

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设