Android-ContentProvider的跨應用通信學習總結

一、ContentProvider的概念

????????1. ContentProvider 是什么?(核心概念)

????????ContentProvider 是 Android 四大組件之一。它的核心職責是管理和共享應用的結構化數據

我們可以把它想象成一個應用的**“數據大使館”**。在一個國家里(Android 系統),不同的城市(應用進程)有自己的法律和領土(私有數據目錄),不能隨意闖入。如果你想從 B 城市獲取信息,你不能直接派人去 B 城市的檔案室里翻找(直接訪問文件/數據庫),而是需要去 B 城市設立的“大使館”(ContentProvider),通過標準的外交辭令(URI)和流程(query, insert, call 等方法),提出你的請求。大使館會驗證你的身份和權限,然后決定給你什么信息,以及給多少。

它通過一套基于 URI (統一資源標識符) 的機制工作:

  • 提供方 (Provider):實現 ContentProvider 類,定義自己能響應的 URI,并實現對這些 URI 的增刪改查等操作。

  • 請求方 (Client):使用 ContentResolver 對象,傳入 Provider 定義好的 URI 來發起請求。

這個機制保證了進程隔離安全性,是 Android 系統中跨進程通信(IPC)和數據共享的基石。


2. 為什么在這個項目中要用 ContentProvider?(設計意圖和意義)

在車載系統或任何復雜的 Android 系統中,應用通常不是孤立運行的。你的愛奇藝媒體應用需要和系統的其他部分進行深度交互。例如:

  • 系統桌面 (Dashboard):需要在桌面上顯示一個“每日推薦”的卡片。

  • 全局搜索:用戶在系統的搜索框里輸入“周杰倫”,需要能搜出你應用里的相關視頻。

  • 系統媒體服務:在播放音樂或視頻時,系統需要在鎖屏界面或通知欄顯示封面圖。

  • 賬號中心:系統的賬號中心可能需要查詢或觸發你應用的登錄狀態。

這些“系統桌面”、“全局搜索”、“媒體服務”和“賬號中心”很可能都是獨立的應用或進程。你的媒體應用無法直接調用它們的方法,反之亦然。

意義就在于ContentProvider 提供了一套標準、穩定且安全的跨進程通信(IPC)解決方案。它不是唯一的 IPC 方式(還有 AIDL/Binder、Broadcast等),但對于數據共享和功能調用來說,它是最規范、最被系統原生支持的方式。使用 ContentProvider 意味著愛奇藝應用能夠以一種標準化的方式“融入”到整個車載系統中,實現深度集成。


二、 四種不同的應用模式(結合代碼詳解)

這四個文件恰好展示了 ContentProvider 的四種典型且高級的用法,而不僅僅是簡單的數據庫查詢。

A. 數據查詢模式 (IqiyiSearchProvider.kt)

這是最經典、最傳統的 ContentProvider 用法。

  • 作用:響應系統的全局搜索請求,返回匹配關鍵詞的媒體內容。

  • 核心方法queryWithKeyword() (內部調用了 query())。

  • 代碼分析

    1. 當系統搜索框架調用 query() 時,這個 Provider 會被喚醒。

    2. 它使用 runBlocking 啟動一個協程來執行耗時操作(iqiyiDataRepository.globalSearch(keyword)),這通常是一個網絡請求。

    3. 它沒有使用真實的數據庫,而是創建了一個 MatrixCursor。這是一個內存中的 Cursor,非常適合將 List 或其他內存數據結構包裝成 Cursor 對象返回。

    4. 最后,它將搜索結果逐行添加到 MatrixCursor 中并返回。

  • 入口:當系統搜索框架或其他應用調用 ContentResolver.query(uri, ...) 時,IqiyiSearchProviderquery() 方法被觸發,進而調用 queryWithKeyword(keyword)

  • 異步處理:搜索通常涉及網絡請求,是耗時操作。這里使用了 runBlocking 啟動一個協程,避免阻塞主線程。

// IqiyiSearchProvider.kt
override fun queryWithKeyword(keyword: String): Cursor? {// ...return runBlocking { // 啟動協程執行耗時操作try {val result = iqiyiDataRepository.globalSearch(keyword) // 網絡請求// ...}}
}
  • 這樣做(使用 MatrixCursor)的意義:極大地簡化了數據提供過程。你無需為了提供數據而專門創建一個數據庫。任何可以轉換成二維表格的數據(比如網絡請求返回的 JSON 列表),都可以通過 MatrixCursor 輕松地提供給其他應用,完全符合 ContentProviderCursor 返回規范。

// IqiyiSearchProvider.kt
val cursor = MatrixCursor(MEDIA_COLUMNS) // 1. 定義列名
result.forEach { mediaItem ->// ...if (!listItem.isNullOrEmpty()) {for (item in listItem) {// ...cursor.addRow( // 2. 將List中的每一項數據,作為一行添加到MatrixCursorarrayOf(item.mediaId,item.description.title,subtitle,// ...))}}
}
return cursor // 3. 返回構建好的Cursor
B. 文件共享模式 (IqiyiArtworkProvider.kt)
  • 作用:向其他應用(如系統UI)提供圖片文件(專輯或視頻封面)。

  • 核心方法openFile()(在 AbstractArtworkProvider 基類中,由 imageNetLoader 實現)。

  • 代碼分析

    1. 客戶端(如系統媒體服務)請求一個 content://... 格式的圖片 URI。

    2. IqiyiArtworkProvider 接收到請求,從 URI 中解析出真實的圖片網絡地址(http/https)。

    3. 它使用 Glide 這個強大的圖片加載庫來下載圖片,并將其緩存到應用的私有緩存目錄中。

    4. 最關鍵的一步,它調用 ParcelFileDescriptor.open() 返回一個指向該緩存文件的文件描述符 (File Descriptor)

  • URI 約定:客戶端請求的不是一個普通的文件路徑,而是一個特殊的 content:// URI,例如: content://com.jidouauto.media.iqiyiartwork/general?url=http://.../pic.jpg&...

  • 圖片加載與緩存:Provider 接收到請求后,從 URI 中解析出真實的圖片網絡地址。然后,它使用強大的圖片加載庫 Glide 來下載和緩存圖片。這利用了 Glide 成熟的緩存策略,避免重復下載。

// IqiyiArtworkProvider.kt -> imageNetLoader()
// 使用 Glide 下載圖片文件
val cacheFile = Glide.with(context).asFile().load(finalUrl).submit().get(30000, TimeUnit.MILLISECONDS)// 將Glide下載的臨時文件重命名為我們期望的緩存文件
cacheFile.renameTo(file)

返回文件描述符:這是最關鍵的一步。Provider 不會返回文件的真實路徑(因為其他應用可能沒有權限訪問愛奇藝的私有緩存目錄),而是返回一個 ParcelFileDescriptor

// IqiyiArtworkProvider.kt -> imageNetLoader()
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
  • 這樣做的意義

    • 安全:它沒有直接暴露文件的真實路徑(其他應用可能沒有權限訪問)。而是通過文件描述符授權。系統會為持有這個描述符的客戶端進程臨時授予對該文件的只讀權限。這是一種受控、臨時的授權,比暴露文件路徑或賦予存儲權限要安全得多。

    • 高效:利用了 Glide 的強大緩存機制,避免了重復下載。


C. 遠程過程調用 (RPC) 模式 (IqiyiAccountProvider.kt)

這個 Provider 幾乎完全顛覆了 ContentProvider 是“數據提供者”的傳統印象。

  • 作用:對外暴露一系列功能接口,而不是數據。其他應用可以通過它來執行登錄、登出、獲取二維碼、檢查登錄狀態等操作。

  • 核心方法call()

  • 代碼分析

    1. 這個類的 query(), insert(), delete(), update() 方法都直接返回 null0,表明它不處理傳統的 CRUD(增刪改查)請求。

    2. 所有的邏輯都集中在 call(method: String, ...) 方法中。

    3. 它使用一個巨大的 when 語句,根據傳入的 method 字符串來判斷客戶端想要調用哪個功能,就像一個路由器。

    4. 例如,當 method"getQRCodeToken",它就調用 accountRepository.getQrCodeAndToken(),并將結果放入 Bundle 中返回。

  • 忽略 CRUD:這個類的 query(), insert(), delete(), update() 方法都直接返回 null0,表明它不處理傳統的數據庫增刪改查請求。

  • 方法分發器:所有的邏輯都集中在 call(method: String, ...) 方法中。它使用一個巨大的 when 語句,根據客戶端傳入的 method 字符串來判斷具體要調用哪個功能,就像一個API 路由器

    // IqiyiAccountProvider.kt
    override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {logd(TAG) { "jidouauto call, method: $method, arg:$arg, extras: $extras" }return runBlocking {when (method) { // 像一個路由器,根據 method 分發到不同的處理邏輯"getQRCodeToken" -> {Bundle().apply {try {// 調用內部業務邏輯putParcelable(EXTRA_RESULT, accountRepository.getQrCodeAndToken())putBoolean(EXTRA_SUCCESS, true) // 返回成功標志} catch (e: Exception) {putParcelable(EXTRA_RESULT, null)putBoolean(EXTRA_SUCCESS, false) // 返回失敗標志putSerializable(EXTRA_ERROR, e) // 返回錯誤信息}}}"checkLoginResult" -> { /* ... */ }"logout" -> { /* ... */ }// ... 更多的方法else -> null}}
    }
    
  • Bundle 作為通用容器:所有方法的輸入參數(arg, extras)和返回值(Bundle)都通過 Bundle 來傳遞。Bundle 可以攜帶各種類型的數據(布爾值、字符串、序列化對象等),通用性極強。

  • 這樣做的意義:巧妙地利用 call() 方法將 ContentProvider 變成了一個輕量級的**遠程過程調用(RPC)**框架。對于那些不需要持續雙向通信、僅需“調用-返回”的簡單跨進程功能調用,這種方式比實現復雜的 AIDL/Binder 服務要簡單得多,也更穩定。


D. 現代 UI 片段模式 (IqiyiSliceProvider.kt)

這是基于 ContentProvider 的一個非常現代的應用。

  • 作用:提供一個被稱為 Slice可交互的 UI 片段,供其他應用(如系統桌面 Dashboard)直接嵌入和顯示。

  • 核心方法onBindSliceForDashboardNotification() (內部調用 onBindSlice())。

  • 代碼分析

    1. 它繼承自 AbsSliceProvider,而 SliceProvider 本身就是 ContentProvider 的一個特殊子類。

    2. 當 Dashboard 應用需要顯示愛奇藝的推薦卡片時,它會請求這個 Provider 的 URI。

    3. onBindSlice 方法被觸發,它從 IqiyiMediaPushHelper 獲取推薦的媒體項,然后調用 MediaSliceUtil.createIqiyiSlice 來構建一個 Slice 對象。

    4. 這個 Slice 對象包含了一個UI模板所需的所有信息(標題、圖片、點擊事件等),然后被返回給 Dashboard 應用進行渲染。

  • 特殊子類IqiyiSliceProvider 繼承自 AbsSliceProvider,而 SliceProvider 本身就是 ContentProvider 的一個特殊子類,專門用于處理 Slice 的綁定請求。

  • URI 匹配:和普通 Provider 一樣,它也通過 UriMatcher 來識別客戶端請求的是哪個 Slice

// IqiyiSliceProvider.kt
uriMatcher.addURI(authority,"$SLICE_PATH/$SLICE_ID", // content://<authority>/daily_recommend/1001SLICE_CODE
)

構建 Slice:當 Dashboard 需要顯示愛奇藝的推薦卡片時,它會請求這個 Provider 的 URI。onBindSlice 方法被觸發,它從 IqiyiMediaPushHelper 獲取推薦的媒體項,然后調用工具類 MediaSliceUtil.createIqiyiSlice 來構建一個 Slice 對象。

// IqiyiSliceProvider.kt
private fun createNotification(sliceUri: Uri): Slice {// 1. 獲取要展示的數據val recommend = IqiyiMediaPushHelper.getInstance()?.getSliceMediaItem()// 2. 使用工具類構建Slice對象return MediaSliceUtil.createIqiyiSlice(context!!,sliceUri,recommend?.description?.title?.toString() ?: "",false,recommend)
}
  • 這樣做的意義Slice 機制讓你的應用內容可以“走出”應用本身,以一種原生的、高性能的方式嵌入到系統的其他界面中,極大地提升了應用內容的曝光度和用戶觸達率。而 ContentProvider 正是實現這一切的底層技術基礎。


三、?客戶端的交互方式(IqiyiAccountManager.kt

IqiyiAccountManager 展示了作為客戶端如何與 IqiyiAccountProvider 交互。

  • ContentResolver:它是客戶端的代理,所有對 Provider 的請求都通過它發出。例如 contentResolver.call(uri, "loginUid", ...)

// IqiyiAccountManager.kt -> bindLogin()
suspend fun bindLogin(): Boolean {return withContext(Dispatchers.IO) {val uri = Uri.parse("content://${AUTHORITIES}/")// 使用 resolver 發起 call 請求,就像調用一個遠程函數val bundle = contentResolver.call(uri, "bindLogin", null, null)// ... 處理返回的 bundle}
}
  • ContentObserver:這是一個觀察者。IqiyiAccountManager 通過 contentResolver.registerContentObserver(...) 注冊了多個觀察者來監聽不同的 URI。

    • IqiyiAccountProvider 中的數據發生變化時(例如用戶登錄成功,loginUid 改變),Provider 會調用 getContext().getContentResolver().notifyChange(uri, null)

    • 這個通知會通過系統廣播給所有監聽了該 uriContentObserver,觸發其 onChange() 方法。

    • 這樣,IqiyiAccountManager 就能被動地、響應式地收到數據變化的通知,并更新自己的狀態,而不需要不停地去輪詢查詢。

// IqiyiAccountManager.kt -> init
init {// 監聽代表“用戶ID”變化的URIcontentResolver.registerContentObserver(Uri.parse("content://${AUTHORITIES}/loginUid"),false, uidObserver)// ... 監聽其他URI
}// 當URI變化時,onChange會被回調
private val uidObserver: ContentObserver = object : ContentObserver(handler) {override fun onChange(selfChange: Boolean) {// ... 更新UI或內部狀態}
}

通知與響應的閉環:這個模式如何工作的?

  • Provider (數據變化方):當 IqiyiAccountProvider 內部的用戶狀態發生變化時(例如,用戶登錄成功,loginUid 從 0 變為一個具體的值),它會主動發出通知。

// IqiyiAccountProvider.kt
private val uidObserver: Observer<Long> = Observer<Long> {// ...context?.contentResolver?.notifyChange( // 關鍵:通知系統這個URI的數據已變更Uri.parse("content://${AUTHORITIES}/loginUid"),null)
}
  • Client (監聽方):系統收到通知后,會喚醒所有監聽了 content://${AUTHORITIES}/loginUid 這個 URI 的 ContentObserver,觸發它們的 onChange() 方法。

  • 結果IqiyiAccountManager 就能被動地、響應式地收到數據變化的通知,并更新自己的狀態,而不需要設置定時器去不停地輪詢查詢,極大地節省了系統資源

總結

它是 Android 系統中一個功能強大、用途廣泛的跨進程通信和數據共享的瑞士軍刀。通過這幾個例子,我們可以看到它如何被巧妙地用于:

  1. 提供標準化的查詢數據 (IqiyiSearchProvider):經典的跨進程數據查詢。

  2. 安全地共享私有文件 (IqiyiArtworkProvider):通過文件描述符實現安全可控的文件訪問。

  3. 封裝功能調用接口 (IqiyiAccountProvider):作為輕量級的 RPC 框架,實現跨進程方法調用。

  4. 支撐現代化的嵌入式 UI (IqiyiSliceProvider):作為提供可交互 UI 片段的后端。

理解并掌握這些模式,對于開發需要在復雜系統中與其他應用深度集成的高質量 Android 應用至關重要。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/93978.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/93978.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/93978.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java數據結構第二十六期:解密位圖,海量數據處理的 “空間魔法”

專欄&#xff1a;Java數據結構秘籍 個人主頁&#xff1a;手握風云 目錄 一、位圖 1.1. 概念 1.2. 面試題 1.3. 位圖的實現 1.4. 位圖的應用 一、位圖 1.1. 概念 在數據結構中&#xff0c;位圖&#xff08;也稱為位數組、位向量或位集&#xff09;是一種緊湊的方式來表示一…

芯科科技即將重磅亮相IOTE 2025深圳物聯網展,以全面的無線技術及生態覆蓋賦能萬物智聯

作為低功耗無線連接領域的創新性領導廠商&#xff0c;Silicon Labs&#xff08;亦稱“芯科科技”&#xff09;將于8月27至29日攜其最前沿的人工智能&#xff08;AI&#xff09;和物聯網&#xff08;IoT&#xff09;解決方案在深圳舉辦的IOTE 2025國際物聯網展中盛大展出。這場亞…

Linux上安裝多個JDK版本,需要配置環境變量嗎

簡短回答&#xff1a;不需要同時配置多個 JDK 的 JAVA_HOME 和 PATH&#xff0c;但你可以安裝多個版本&#xff0c;并通過靈活的方式在它們之間切換。 文章目錄? 正確做法&#xff1a;安裝多個 JDK&#xff0c;但只讓一個生效&#xff08;通過環境變量或 alternatives&#xf…

MySQL有哪些高可用方案

大家好&#xff0c;我是鋒哥。今天分享關于【MySQL有哪些高可用方案】面試題。希望對大家有幫助&#xff1b; MySQL有哪些高可用方案? 超硬核AI學習資料&#xff0c;現在永久免費了&#xff01; MySQL 高可用方案是指確保 MySQL 數據庫在面對硬件故障、網絡故障、負載過重等…

【Windows】Windows平臺基于加速地址安裝vcpkg并集成到Visual Studio 2017

基礎運行環境 啟動&#xff1a; 適用于 VS 2017 的 x64 本機工具命令提示 ninja 下載壓縮包 https://gh-proxy.com/https:/github.com/ninja-build/ninja/releases/download/v1.13.1/ninja-win.zip 直接解壓到c:/Windows (無需配置環境變量) CMake 下載安裝包 https://gh-proxy…

LLMs之MCP:Chrome MCP的簡介、安裝和使用方法、案例應用之詳細攻略

LLMs之MCP&#xff1a;Chrome MCP的簡介、安裝和使用方法、案例應用之詳細攻略 目錄 Chrome MCP的簡介 1、特點 2、與類似項目的比較 Chrome MCP的安裝和使用方法 1、安裝 2、使用方法 加載 Chrome 擴展 與 MCP 協議客戶端一起使用 使用 STDIO 連接&#xff08;替代方…

【Java EE】多線程-初階 synchronized 關鍵字 - 監視器鎖 monitor lock

synchronized 關鍵字 - 監視器鎖 monitor lock5. synchronized 關鍵字 - 監視器鎖 monitor lock5.1 synchronized 的特性5.2 synchronized 使??例5.3 Java 標準庫中的線程安全類本節?標? 掌握 synchronized關鍵字5. synchronized 關鍵字 - 監視器鎖 monitor lock &#xf…

Java多線程:從基礎到實戰

引言多線程是Java并發編程的核心技術之一&#xff0c;廣泛應用于服務器開發、數據處理、實時系統等領域。通過多線程&#xff0c;程序可以充分利用CPU資源&#xff0c;提高執行效率&#xff0c;同時處理多個任務。本文將從多線程的基本概念、實現方式、線程狀態、同步與通信到常…

list集合可以一邊遍歷一遍修改元素嗎?

今天看來一下Java中list集合部分的八股&#xff0c;發現了一個以前沒注意過的問題&#xff0c;記錄一下list可以一邊遍歷一邊修改元素嗎&#xff1f;答&#xff1a;在 Java 中&#xff0c;List在遍歷過程中是否可以修改元素取決于遍歷方式和具體的List實現類。①&#xff1a;對…

Infusing fine-grained visual knowledge to Vision-Language Models

Infusing fine-grained visual knowledge to Vision-Language Models Authors: Nikolaos-Antonios Ypsilantis, Kaifeng Chen, Andr Araujo, Ond?ej Chum Deep-Dive Summary: 視覺-語言模型中注入細粒度視覺知識 摘要 大規模對比預訓練產生了強大的視覺-語言模型&#xf…

RK3576賦能無人機巡檢:多路視頻+AI識別引領智能化變革

隨著工業巡檢任務的復雜度不斷提升&#xff0c;無人機逐漸取代傳統人工&#xff0c;成為電力、能源、林業、農業等行業的“高空作業主力”。然而&#xff0c;巡檢并非簡單的拍攝和回放&#xff0c;它要求無人機實時采集多路畫面、快速分析異常&#xff0c;并穩定回傳數據。這對…

ollama Modelfile 文件生成

輸入 根據如下TEMPLATE和params寫一個modelfile文件&#xff0c;TEMPLATE為&#xff1a;{{- $lastUserIdx : -1 -}} {{- range $idx, $msg : .Messages -}} {{- if eq $msg.Role “user” }}{{ $lastUserIdx $idx }}{{ end -}} {{- end }} {{- if or .System .Tools }}<|i…

關聯規則挖掘2:FP-growth算法(Frequent Pattern Growth,頻繁模式增長)

目錄 一、核心思想&#xff1a;一個形象的比喻 二、核心思想的具體拆解 步驟一&#xff1a;構建FP-tree&#xff08;頻繁模式樹&#xff09; 步驟二&#xff1a;從FP-tree中挖掘頻繁項集 為什么這很高效&#xff1f; 三、總結 核心思想與優勢 適用場景與缺點 四、例題…

在IDEA中DEBUG調試時查看MyBatis-Plus動態生成的SQL語句

在IDEA中DEBUG調試時查看MyBatis-Plus動態生成的SQL語句前言&#xff1a;動態SQL調試的痛與解決方案一、準備工作&#xff1a;調試前的檢查清單二、基礎方法&#xff1a;SqlSessionTemplate斷點調試步驟1&#xff1a;定位SqlSessionTemplate類步驟2&#xff1a;在invoke方法上設…

Linux 文本處理三劍客:awk、grep、sed 完全指南

Linux 文本處理三劍客&#xff1a;awk、grep、sed 完全指南 1. 概述 Linux 系統提供了三個強大的文本處理工具&#xff1a;awk、grep 和 sed&#xff0c;它們各有所長&#xff0c;結合使用可以高效地處理文本數據。 awk&#xff1a;擅長文本分析和格式化輸出&#xff0c;是一…

pyecharts可視化圖表組合組件_Grid:打造專業數據儀表盤

pyecharts可視化圖表組合組件_Grid&#xff1a;打造專業數據儀表盤 目錄pyecharts可視化圖表組合組件_Grid&#xff1a;打造專業數據儀表盤引言圖表1&#xff1a;Grid-Overlap-多X/Y軸示例代碼解析1. 圖表創建2. 多軸配置3. 圖表重疊4. Grid布局效果與應用圖表2&#xff1a;Gri…

【電氣工程學習】

三極管中&#xff1a;集電極C,基極B&#xff0c;發射極E接線&#xff1a;棕正藍負黑信號NPN開關輸出的是我們的0V,也叫低電平PNP開關輸出的是24V,也就是高電平&#xff08;NPN開關導通時&#xff0c;相當于把輸出端“拉”到0V&#xff08;低電平&#xff09;&#xff0c;稱為“…

【嵌入式】CAN通信

CAN 總線最初由博世于1980年代為汽車行業開發&#xff0c;能夠簡化復雜的布線網絡&#xff0c;還確保可靠和安全的數據傳輸。 1.CAN技術解釋 CAN網絡中的每個節點&#xff0c;都是平等的&#xff0c;沒有主次之分&#xff0c;這一點和SPI和I2C不同。每個節點都可以在需要的時…

Apache ShenYu網關與Nacos的關聯及如何配合使用

Apache ShenYu 網關與 Nacos 之間的關系可以概括為 “協作互補”:Nacos 作為 服務注冊與配置中心,為 ShenYu 提供動態的服務發現和配置管理能力,而 ShenYu 作為 流量網關,依賴 Nacos 實現路由信息的動態更新和實時生效。以下是詳細解析: 1. 核心關系圖解 拉取服務列表/路…

【CPP】一個CPP的Library(libXXXcore)和測試程序XXX_main的Demo

一個CPP的Library和測試程序Demo 1. 思路描述 目錄結構 總控CMakeList.txt文件 2. Library代碼實現 2.1 XXXLib.hpp文件(對外的接口定義文件)和XXXLib.cpp文件 2.1.1 XXXLib.hpp文件 2.1.2 XXXLib.cpp文件 2.2 CXXXLibApi.hpp文件和CXXXLibApi.cpp文件(內部的API基類) 2.2.1 CX…