NDK 开发实战 - 封装 java 层 sdk 模型

关于 Ndk 开发,网上的资料比较少,这方面的书籍也不多。因为其涉及的知识非常广,时常有哥们问我,东西那么多到底要学到什么程度呢?到底应该怎么学?这期我给大家来做一个简单回答,首先单纯站在 Android 系统的角度来说,我们可以细分为 Java 层和 Native(c/c++) 层。站在 Android 开发的角度来说,我们又可以细分为精通 Android 开发和精通 c/c++ 开发。当然笔者之前在长沙从事 Android 开发,公司是不存在 c/c++ 工程师的,也就是说所有的开发工作调用 Android Framework 层的 Api 就都能实现。

来到深圳做音视频项目,公司有专门的引擎部门,也就是说有专门的 c/c++ 音视频工程师。为了能够让 Android 和 c/c++ 打通,因此就多出来了第三类开发者,熟悉 Android 开发和熟悉 c/c++ 开发,也就是我们通常所说的 Ndk 开发,因此一个合格的 Android 开发者必须要熟悉 c/c++,我们开发个三年五载想要提升,也需要尝试着去熟悉 c/c++。

我们可能又会想,精通一门开发语言至少需要个三年五载,Java 都够我们折腾的了,哪还有时间去学习 c/c++ ,但有些招聘需求上又明确要求开发者需要熟悉 c/c++,比如 Android 音视频开发和 Android 智能识别开发等。那如果我们想要从事 Ndk 开发,得怎么去学又得学哪些东西?这里我简单的罗列一个小清单:

  • 熟悉 c/c++ 基础语法
  • 熟悉 jni 基础知识
  • 熟悉 c/c++ 进阶知识
  • 熟悉 linux 内核
  • 熟悉 shell 脚本
  • 熟悉 cmake 语法

上面的内容看似有点多,但当我们真正下定决心去学时,其实并不难也比较简单。注意上面写的是熟悉但并不是精通,我们得先熟悉然后再去精通,怎么才是算熟悉呢?首先是读,我们能够看懂 Android Native 层的源码,读 native 层源码有助于我们日常的开发和性能优化。其次是我们还要能够写,那怎么写如何写?其实套路也就那么多,这篇文章我们主要来学习如何封装 sdk 给 Java 调用者,这里我以之前所学的 OpenCv 为例来写。

1.封装 Java 层 Mat

在《图形图像处理 - 手写 QQ 说说图片处理效果》 一文中处理油画效果是这么写的:

    /*** 实现图像油画效果** @param bitmap 原图片* @return 油画效果图像*/public static final native Bitmap oilPainting(Bitmap bitmap);// Native 层代码extern "C"JNIEXPORT jobject JNICALLJava_com_darren_ndk_day70_NDKBitmapUtils_oilPainting(JNIEnv *env, jclass type, jobject bitmap) {// 油画基于直方统计// 1. 每个点需要分成 n*n 小块// 2. 统计灰度等级// 3. 选择灰度等级中最多的值// 4. 找到最大等级的像素取平均值// 省略代码部分 ......return bitmap;
}
复制代码

我们不妨来思考一下,在真正开发的过程中,我们基本都是按需定制,简单一点说就是你需要什么功能,我就增加代码封装提供功能。这在 Java 层开发时倒是无所谓,改改代码直接调一下就可以了,但 Ndk 开发所涉及的就不再只是 Java 了,改了代码必须重新编译 so 库。倘若需求稍微有变动,我们需要改 Native 层代码,然后重新编译 so 库,再联调再测试,再改再联调再测试。

相信大家都能听明白我想表达的意思,因此我们在提供 sdk 时一定要考虑周到,尽量不要反复的去改 c/c++ 代码,尽量不要反复编译联调 so 库。接下来我们来思考一下,如何才能有效的避免我以上所说的这些问题,假设刚开始需要提供一个做掩摸操作的功能,那代码可能会是如下这样:

    /*** 掩模操作处理** @param bitmap 原图* @return 掩模效果图*/public native static Bitmap mask(Bitmap bitmap);// native 代码extern "C"JNIEXPORT jobject JNICALLJava_com_darren_ndk_day72_MainActivity_mask(JNIEnv *env, jclass type, jobject bitmap) {// 1. bitmap -> matMat src;cv_helper::bitmap2mat(env, bitmap, src);// bgra -> bgr 否则 filter2D 会报错cvtColor(src, src, COLOR_BGRA2BGR);// 2. 自定义卷积核Mat kernel(3, 3, CV_32FC1);kernel.at<float>(0, 0) = 0;kernel.at<float>(0, 1) = -1;kernel.at<float>(0, 2) = 0;kernel.at<float>(1, 0) = -1;kernel.at<float>(1, 1) = 5;kernel.at<float>(1, 2) = -1;kernel.at<float>(2, 0) = 0;kernel.at<float>(2, 1) = -1;kernel.at<float>(2, 2) = 0;// 3. 卷积运算Mat dst;filter2D(src, dst, src.depth(), kernel);// 4. mat -> bitmapcv_helper::mat2bitmap(env, dst, bitmap);return bitmap;}
复制代码

假设现在又需要提供一个模糊操作,那么我们可能又得新提供 native 方法,得重新编译调试 so ,代码可能会如下:

    /*** 模糊处理** @param bitmap 原图* @param size   模糊半径,半径越大越模糊* @return 模糊效果图*/public native static Bitmap blur(Bitmap bitmap, int size);// native 层代码extern "C"JNIEXPORT jobject JNICALLJava_com_darren_ndk_day72_MainActivity_blur(JNIEnv *env, jclass type, jobject bitmap, jint size) {// 1. bitmap -> matMat src;cv_helper::bitmap2mat(env, bitmap, src);// bgra -> bgr 否则 filter2D 会报错cvtColor(src, src, COLOR_BGRA2BGR);// 2. 模糊卷积核Mat kernel = Mat::ones(Size(size, size), CV_32FC1) / (size * size);// 3. 卷积运算Mat dst;filter2D(src, dst, src.depth(), kernel);// 4. mat -> bitmapcv_helper::mat2bitmap(env, dst, bitmap);return bitmap;}
复制代码

倘若后面又出现了一个其他类似的功能,那么我又得新提供 native 方法,重新编译调试 so ,就出现了我上面所说的,改 Native 层代码,重新编译 so 库,再联调再测试,再改再联调再测试。因此接下来我们需要将这些代码拆分出来封装,我们在 Java 层创建一个 Mat.java 对象用来对应 Native 层的 Mat.cpp 对象,这种思想有点类似于系统的 Bitmap 对象。关于这部分知识大家可以参考这篇文章《JNI 基础 - Android 共享内存的序列化过程》

public class Mat {/*** Native 创建 Mat 的首地址*/public final long mNativePtr;private int rows;private int cols;private CVType type;public Mat(int rows, int cols, CVType type) {this.cols = cols;this.rows = rows;this.type = type;mNativePtr = nMatIII(rows, cols, type.value);}public Mat() {mNativePtr = nMat();}/*** 创建 Native Mat.cpp 对象** @return Mat.cpp 对象头指针*/private native long nMat();/*** 创建 Native Mat.cpp 对象** @param rows 高* @param cols 宽* @param type 类型* @return Mat.cpp 对象头指针*/private native long nMatIII(int rows, int cols, int type);/*** 这个方法提供给 Java 调用者** @param row* @param col* @param value*/public void put(int row, int col, int value) {if (type == CVType.CV_32FC1) {throw new UnsupportedOperationException("Provider value nonsupport and please check CVType.");}nPutI(mNativePtr, row, col, value);}/*** 这个方法提供给 Java 调用者** @param row* @param col* @param value*/public void put(int row, int col, float value) {if (type != CVType.CV_32FC1) {throw new UnsupportedOperationException("Provider value nonsupport and please check CVType.");}nPutF(mNativePtr, row, col, value);}@Overrideprotected void finalize() throws Throwable {super.finalize();// GC 回收该对象时 delete Mat.cpp 对象nDelete(mNativePtr);}public int getCols() {return cols;}public int getRows() {return rows;}public CVType getType() {return type;}public void release() {nRelease(mNativePtr);}private native void nDelete(long nativePtr);private native void nRelease(long nativePtr);private native void nPutI(long nativePtr, int row, int col, int value);private native void nPutF(long nativePtr, int row, int col, float value);
}
复制代码
2. JNI 异常处理

关于 jni 的异常处理这是个技术活,之前的文章也有提到,这里还是要再做一些强调,我们提供的 sdk 代码尽量不要无缘无故的崩掉,适当的地方需要抛 Java 异常。因为 native 崩溃不像 Java 崩溃那样会有 log 日志打印,如果用户只看到闪退却看不到崩溃信息,用户可能根本无法进行调试修改。因此我们要学会抛 java 异常。

void cv_helper::bitmap2mat(JNIEnv *env, jobject &bitmap, cv::Mat &dst) {try {AndroidBitmapInfo bitmapInfo;CV_Assert(AndroidBitmap_getInfo(env, bitmap, &bitmapInfo) >= 0);void *pixels;CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0);CV_Assert(pixels);if (bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {//  ANDROID_BITMAP_FORMAT_RGBA_8888 -> CV_8UC4dst.create(bitmapInfo.height, bitmapInfo.width, CV_8UC4);dst.data = reinterpret_cast<uchar *>(pixels);} else if (bitmapInfo.format == ANDROID_BITMAP_FORMAT_RGB_565) {dst.create(bitmapInfo.height, bitmapInfo.width, CV_8UC2);dst.data = reinterpret_cast<uchar *>(pixels);} else {cv::Exception exception;exception.msg = "Bitmap only support RGBA_8888 and RGB_565";throw exception;}AndroidBitmap_unlockPixels(env, bitmap);} catch (const cv::Exception exception) {jclass ej = env->FindClass("java/lang/Exception");env->ThrowNew(ej, exception.what());} catch (...) {jclass ej = env->FindClass("java/lang/Exception");env->ThrowNew(ej, "Unknown exception in JNI code {mat2bitmap}");}
}
复制代码

测试代码

Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.lbb);
Bitmap dstBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), Bitmap.Config.ALPHA_8);// 模糊卷积核
int size = 9;
Mat kernel = new Mat(size, size, CVType.CV_32FC1);
float value = 1f / (size * size);
for (int rows = 0; rows < size; ++rows) {for (int cols = 0; cols < size; ++cols) {kernel.put(rows, cols, value);}
}Mat srcMat = new Mat();
Utils.bitmap2mat(srcBitmap, srcMat);
Mat dstMat = new Mat();
// 卷积运算
Imgproc.filter2D(srcMat, dstMat, kernel);
Utils.mat2Bitmap(dstMat, dstBitmap);
复制代码

最后大家可以尝试着去了解了解腾讯的开源框架 MMKV,可以去学学其代码的内部实现,既然我们学了 NDK 肯定需要时常拿出来溜溜。我们也可以对其做一些优化,比如支持写入对象,写入共享内存等等。

视频地址:pan.baidu.com/s/17v_gCfNt…

视频密码:vj19

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/251907.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JDK+Tomcat搭建JSP运行环境--JSP基础

一、搭建JSP运行环境之前需要了解的基本知识 配置JSP运行环境之前&#xff0c;我们需要了解JSP的运行机制。只有了解JSP运行机制后&#xff0c;我们才能知道为什么要搭建JSP运行环境?如何去搭建JSP运行环境?为什么要配置Tomcat、JDK&#xff1f; JSP(Java Sever Page)即Java服…

Docker容器的自动化监控实现

本文由 网易云 发布。 近年来容器技术不断成熟并得到应用。Docker作为容器技术的一个代表&#xff0c;目前也在快速发展中&#xff0c;基于 Docker的各种应用也正在普及&#xff0c;与此同时 Docker对传统的运维体系也带来了冲击。我们在建设运维平台的过程中&#xff0c;也需…

robotframework 常用关键字

标准库 第三方库 其他库转载于:https://www.cnblogs.com/Chamberlain/p/10729054.html

身份证的验证

var Wi [ 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, 1 ]; // 加权因子 var ValideCode [ 1, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2 ]; // 身份证验证位值.10代表X function checkIdcard(idCard) { idCard trim(idCard);//去掉字符串头尾空格 if (idCard.length 15…

人工智能实战小程序之语音_前端开发

1. 人工智能实战小程序之准备工作 2. 人工智能实战小程序之语音_前端开发 今天这部分主要讲小程序前端功能的开发由于我偏后端&#xff0c;css是我的弱项&#xff0c;可能很多人和我一样开发小程序不知道如何下手&#xff0c;希望本篇文章对你有帮助我的学习路线是&#xff1a;…

当TFS/VSTS遇上Power BI

引言众所周知&#xff0c;要对TFS进行深入的图表分析&#xff0c;往往需要依赖于SQL Server Analysis Service和SQL Server Reporting Service。虽然随着TFS对敏捷项目的支持&#xff0c;内置了诸如累积流图、燃尽图等快捷图表&#xff1b;并且在最新的版本中还可以在仪表盘和查…

HashMap深度解析:一文让你彻底了解HashMap

写在前面HashMap是Map族中最为常用的一种&#xff0c;也是 Java Collection Framework 的重要成员。本文首先给出了 HashMap 的实质并概述了其与 Map、HashSet 的关系&#xff0c;紧接着给出了 HashMap 在 JDK 中的定义&#xff0c;并结合源码分析了其四种构造方式。最后&#…

Bzoj3628: [JLOI2014]天天酷跑

3628: [JLOI2014]天天酷跑 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 121 Solved: 44[Submit][Status][Discuss]Description 在游戏天天酷跑中&#xff0c;最爽的应该是超级奖励模式了吧&#xff0c;没有一切障碍&#xff0c;可以尽情的吃金币&#xff0c;现在请你控制…

python_线程、进程和协程

线程 Threading用于提供线程相关的操作&#xff0c;线程是应用程序中工作的最小单元。 1 #!/usr/bin/env python2 #codingutf-83 __author__ yinjia4 5 6 import threading,time7 8 def show(arg):9 time.sleep(2) 10 print(线程: str(arg)) 11 12 for i in range(…

AppDelegate瘦身之服务化

有没有觉得你的AppDelegate杂乱无章&#xff1f;代码几百行上千行&#xff1f;集成了无数的功能&#xff0c;如推送、埋点、日志统计、Crash统计等等&#xff0c;感觉AppDelegate无所不能。 来一段一般的AppDelegate代码&#xff0c;来自网上一篇文章&#xff1a; UIApplicatio…

第四章:手机平板要兼顾-探究碎片

碎片是什么&#xff1f; 碎片&#xff08;Fragment&#xff09;是一种可以嵌入在活动&#xff08;Activity&#xff09;中的 UI 片段&#xff0c;它能让程序更加合理和充分的利用大屏幕的空间&#xff0c;因而在平板上应用的非常广泛。 碎片的使用方式 静态嵌入动态加载碎片和活…

Android Studio 3.4增可视化资源管理工具 可管理和预览项目资源

经过6个月的开发时间&#xff0c;网络大厂17日发布了最新版的App开发IDE Android Studio 3.4&#xff0c;现在就能够下载使用&#xff0c;除了有超过300个错误修护和稳定度增强之外&#xff0c;在开发、建置和测试App阶段&#xff0c;都推出了一些小的新功能和工具&#xff0c;…

Python安装、使用MySQL数据库

本机安装的python版本为Python 2.7(win32 bit) 从http://www.codegood.com/archives/129下载MySQL-python-1.2.3.win32-py2.7.exe&#xff0c;点击安装 如果是win版还需要下载&#xff1a;libguide40.dll 和 libmmd.dll这两个文件&#xff0c;下载后放入到到C:\WINDOWS/syste…

pytorch 安装

安装pytorch时&#xff0c;官网不能选择版本。原以为是浏览器问题&#xff0c;换了几个浏览器都不行。 后来FQ之后&#xff0c;就能选择版本了。 sudo pip install torch torchvision转载于:https://www.cnblogs.com/rabitvision/p/8908757.html

《JavaScript 高级程序设计》精读笔记

本系列读书笔记是我通过学习《Javascript 高级程序设计》第3版时结合自己的理解、概括、精炼然后加以一定的拓展&#xff0c;总结而来的&#xff0c;非常适合具有一定基础&#xff0c;同时又想把 JS 基础学更好的童鞋&#xff0c;当然更希望得到大家的反馈于建议&#xff0c;比…

struts2实现文件查看、下载

CreateTime--2017年9月7日10:25:33 Author:Marydon struts2实现文件查看、下载 1.界面展示 <a style"color: #199ED8;" target"_blank" href"<c:url value"/telemedicine/reseCons/viewFile.do?fileName201516529IO.jpg"/>"…

css文本设置

常用的应用文本的css样式&#xff1a; color 设置文字的颜色&#xff0c;如&#xff1a; color:red; font-size 设置文字的大小&#xff0c;如&#xff1a;font-size:12px; font-family 设置文字的字体&#xff0c;如&#xff1a;font-family:微软雅黑; font-style 设置字体…

关键字static

原文出处&#xff1a;http://cmsblogs.com/ 『chenssy』 一、 static代表着什么 在Java中并不存在全局变量的概念&#xff0c;但是我们可以通过static来实现一个“伪全局”的概念&#xff0c;在Java中static表示“全局”或者“静态”的意思&#xff0c;用来修饰成员变量和成员方…

[IoC容器Unity]第三回:依赖注入

上节介绍了&#xff0c;Unity的Lifetime Managers生命周期&#xff0c;Unity具体实现依赖注入包含构造函数注入、属性注入、方法注入&#xff0c;所谓注入相当赋值&#xff0c;下面一个一个来介绍。 2.构造函数注入 Unity利用Resolve方法解析一个对象&#xff0c;都是调用注册类…

Apache CarbonData 1.5.0编译及安装

2019独角兽企业重金招聘Python工程师标准>>> 一、编译环境描述 OpenStack创建五个虚拟机&#xff0c;其中1个主节点&#xff08;hostname为bigdatamaster&#xff09;&#xff0c;4个从节点&#xff08;hostname分别为&#xff0c;bigdataslave1、bigdataslave2、bi…