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.pswp.cn/news/251907.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/251907.shtml
英文地址,請注明出處:http://en.pswp.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…