国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

寫給 Android 應(yīng)用工程師的 Binder 原理剖析

這篇具有很好參考價(jià)值的文章主要介紹了寫給 Android 應(yīng)用工程師的 Binder 原理剖析。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

一. 前言

這篇文章我醞釀了很久,參考了很多學(xué)習(xí)文檔,讀了很多源碼,卻依舊不敢下筆。生怕自己理解上還有偏差,對(duì)大家造成誤解,貽笑大方。又怕自己理解不夠透徹,無(wú)法用清晰直白的文字準(zhǔn)確的表達(dá)出 Binder 的設(shè)計(jì)精髓。直到今天提筆寫作時(shí)還依舊戰(zhàn)戰(zhàn)兢兢。

Binder 之復(fù)雜遠(yuǎn)遠(yuǎn)不是一篇文章就能說(shuō)清楚的,本文想站在一個(gè)更高的維度來(lái)俯瞰 Binder 的設(shè)計(jì),最終幫助大家形成一個(gè)完整的概念。對(duì)于應(yīng)用層開發(fā)的同學(xué)來(lái)說(shuō),理解到本文這個(gè)程度也就差不多了。

二. Binder 概述

簡(jiǎn)單介紹下什么是 Binder。Binder 是一種進(jìn)程間通信機(jī)制,基于開源的 OpenBinder 實(shí)現(xiàn);OpenBinder 起初由 Be Inc. 開發(fā),后由 Plam Inc. 接手。從字面上來(lái)解釋 Binder 有膠水、粘合劑的意思,顧名思義就是粘和不同的進(jìn)程,使之實(shí)現(xiàn)通信。對(duì)于 Binder 更全面的定義,等我們介紹完 Binder 通信原理后再做詳細(xì)說(shuō)明。

2.1 為什么必須理解 Binder ?

作為 Android 工程師的你,是不是常常會(huì)有這樣的疑問(wèn):

  • 為什么 Activity 間傳遞對(duì)象需要序列化?
  • Activity 的啟動(dòng)流程是什么樣的?
  • 四大組件底層的通信機(jī)制是怎樣的?
  • AIDL 內(nèi)部的實(shí)現(xiàn)原理是什么?
  • 插件化編程技術(shù)應(yīng)該從何學(xué)起?等等…

這些問(wèn)題的背后都與 Binder 有莫大的關(guān)系,要弄懂上面這些問(wèn)題理解 Bidner 通信機(jī)制是必須的。

我們知道 Android 應(yīng)用程序是由 Activity、Service、Broadcast Receiver 和 Content Provide 四大組件中的一個(gè)或者多個(gè)組成的。有時(shí)這些組件運(yùn)行在同一進(jìn)程,有時(shí)運(yùn)行在不同的進(jìn)程。這些進(jìn)程間的通信就依賴于 Binder IPC 機(jī)制。不僅如此,Android 系統(tǒng)對(duì)應(yīng)用層提供的各種服務(wù)如:ActivityManagerService、PackageManagerService 等都是基于 Binder IPC 機(jī)制來(lái)實(shí)現(xiàn)的。Binder 機(jī)制在 Android 中的位置非常重要,毫不夸張的說(shuō)理解 Binder 是邁向 Android 高級(jí)工程的第一步。

2.2 為什么是 Binder ?

Android 系統(tǒng)是基于 Linux 內(nèi)核的,Linux 已經(jīng)提供了管道、消息隊(duì)列、共享內(nèi)存和 Socket 等 IPC 機(jī)制。那為什么 Android 還要提供 Binder 來(lái)實(shí)現(xiàn) IPC 呢?主要是基于性能穩(wěn)定性安全性幾方面的原因。

性能

首先說(shuō)說(shuō)性能上的優(yōu)勢(shì)。Socket 作為一款通用接口,其傳輸效率低,開銷大,主要用在跨網(wǎng)絡(luò)的進(jìn)程間通信和本機(jī)上進(jìn)程間的低速通信。消息隊(duì)列和管道采用存儲(chǔ)-轉(zhuǎn)發(fā)方式,即數(shù)據(jù)先從發(fā)送方緩存區(qū)拷貝到內(nèi)核開辟的緩存區(qū)中,然后再?gòu)膬?nèi)核緩存區(qū)拷貝到接收方緩存區(qū),至少有兩次拷貝過(guò)程。共享內(nèi)存雖然無(wú)需拷貝,但控制復(fù)雜,難以使用。Binder 只需要一次數(shù)據(jù)拷貝,性能上僅次于共享內(nèi)存。

IPC方式 數(shù)據(jù)拷貝次數(shù)
共享內(nèi)存 0
Binder 1
Socket/管道/消息隊(duì)列 2
穩(wěn)定性

再說(shuō)說(shuō)穩(wěn)定性,Binder 基于 C/S 架構(gòu),客戶端(Client)有什么需求就丟給服務(wù)端(Server)去完成,架構(gòu)清晰、職責(zé)明確又相互獨(dú)立,自然穩(wěn)定性更好。共享內(nèi)存雖然無(wú)需拷貝,但是控制負(fù)責(zé),難以使用。從穩(wěn)定性的角度講,Binder 機(jī)制是優(yōu)于內(nèi)存共享的。

安全性

另一方面就是安全性。Android 作為一個(gè)開放性的平臺(tái),市場(chǎng)上有各類海量的應(yīng)用供用戶選擇安裝,因此安全性對(duì)于 Android 平臺(tái)而言極其重要。作為用戶當(dāng)然不希望我們下載的 APP 偷偷讀取我的通信錄,上傳我的隱私數(shù)據(jù),后臺(tái)偷跑流量、消耗手機(jī)電量。傳統(tǒng)的 IPC 沒(méi)有任何安全措施,完全依賴上層協(xié)議來(lái)確保。首先傳統(tǒng)的 IPC 接收方無(wú)法獲得對(duì)方可靠的進(jìn)程用戶ID/進(jìn)程ID(UID/PID),從而無(wú)法鑒別對(duì)方身份。Android 為每個(gè)安裝好的 APP 分配了自己的 UID,故而進(jìn)程的 UID 是鑒別進(jìn)程身份的重要標(biāo)志。傳統(tǒng)的 IPC 只能由用戶在數(shù)據(jù)包中填入 UID/PID,但這樣不可靠,容易被惡意程序利用。可靠的身份標(biāo)識(shí)只有由 IPC 機(jī)制在內(nèi)核中添加。其次傳統(tǒng)的 IPC 訪問(wèn)接入點(diǎn)是開放的,只要知道這些接入點(diǎn)的程序都可以和對(duì)端建立連接,不管怎樣都無(wú)法阻止惡意程序通過(guò)猜測(cè)接收方地址獲得連接。同時(shí) Binder 既支持實(shí)名 Binder,又支持匿名 Binder,安全性高。

基于上述原因,Android 需要建立一套新的 IPC 機(jī)制來(lái)滿足系統(tǒng)對(duì)穩(wěn)定性、傳輸性能和安全性方面的要求,這就是 Binder。

最后用一張表格來(lái)總結(jié)下 Binder 的優(yōu)勢(shì):

優(yōu)勢(shì) 描述
性能 只需要一次數(shù)據(jù)拷貝,性能上僅次于共享內(nèi)存
穩(wěn)定性 基于 C/S 架構(gòu),職責(zé)明確、架構(gòu)清晰,因此穩(wěn)定性好
安全性 為每個(gè) APP 分配 UID,進(jìn)程的 UID 是鑒別進(jìn)程身份的重要標(biāo)志

三. Linux 下傳統(tǒng)的進(jìn)程間通信原理

了解 Linux IPC 相關(guān)的概念和原理有助于我們理解 Binder 通信原理。因此,在介紹 Binder 跨進(jìn)程通信原理之前,我們先聊聊 Linux 系統(tǒng)下傳統(tǒng)的進(jìn)程間通信是如何實(shí)現(xiàn)。

3.1 基本概念介紹

這里我們先從 Linux 中進(jìn)程間通信涉及的一些基本概念開始介紹,然后逐步展開,向大家說(shuō)明傳統(tǒng)的進(jìn)程間通信的原理。

寫給 Android 應(yīng)用工程師的 Binder 原理剖析,Android,移動(dòng)開發(fā),Framework,android,binder,移動(dòng)開發(fā),Framework,java

上圖展示了 Liunx 中跨進(jìn)程通信涉及到的一些基本概念:

  • 進(jìn)程隔離
  • 進(jìn)程空間劃分:用戶空間(User Space)/內(nèi)核空間(Kernel Space)
  • 系統(tǒng)調(diào)用:用戶態(tài)/內(nèi)核態(tài)
進(jìn)程隔離

簡(jiǎn)單的說(shuō)就是操作系統(tǒng)中,進(jìn)程與進(jìn)程間內(nèi)存是不共享的。兩個(gè)進(jìn)程就像兩個(gè)平行的世界,A 進(jìn)程沒(méi)法直接訪問(wèn) B 進(jìn)程的數(shù)據(jù),這就是進(jìn)程隔離的通俗解釋。A 進(jìn)程和 B 進(jìn)程之間要進(jìn)行數(shù)據(jù)交互就得采用特殊的通信機(jī)制:進(jìn)程間通信(IPC)。

進(jìn)程空間劃分:用戶空間(User Space)/內(nèi)核空間(Kernel Space)

現(xiàn)在操作系統(tǒng)都是采用的虛擬存儲(chǔ)器,對(duì)于 32 位系統(tǒng)而言,它的尋址空間(虛擬存儲(chǔ)空間)就是 2 的 32 次方,也就是 4GB。操作系統(tǒng)的核心是內(nèi)核,獨(dú)立于普通的應(yīng)用程序,可以訪問(wèn)受保護(hù)的內(nèi)存空間,也可以訪問(wèn)底層硬件設(shè)備的權(quán)限。為了保護(hù)用戶進(jìn)程不能直接操作內(nèi)核,保證內(nèi)核的安全,操作系統(tǒng)從邏輯上將虛擬空間劃分為用戶空間(User Space)和內(nèi)核空間(Kernel Space)。針對(duì) Linux 操作系統(tǒng)而言,將最高的 1GB 字節(jié)供內(nèi)核使用,稱為內(nèi)核空間;較低的 3GB 字節(jié)供各進(jìn)程使用,稱為用戶空間。

簡(jiǎn)單的說(shuō)就是,內(nèi)核空間(Kernel)是系統(tǒng)內(nèi)核運(yùn)行的空間,用戶空間(User Space)是用戶程序運(yùn)行的空間。為了保證安全性,它們之間是隔離的。

寫給 Android 應(yīng)用工程師的 Binder 原理剖析,Android,移動(dòng)開發(fā),Framework,android,binder,移動(dòng)開發(fā),Framework,java

系統(tǒng)調(diào)用:用戶態(tài)與內(nèi)核態(tài)

雖然從邏輯上進(jìn)行了用戶空間和內(nèi)核空間的劃分,但不可避免的用戶空間需要訪問(wèn)內(nèi)核資源,比如文件操作、訪問(wèn)網(wǎng)絡(luò)等等。為了突破隔離限制,就需要借助系統(tǒng)調(diào)用來(lái)實(shí)現(xiàn)。系統(tǒng)調(diào)用是用戶空間訪問(wèn)內(nèi)核空間的唯一方式,保證了所有的資源訪問(wèn)都是在內(nèi)核的控制下進(jìn)行的,避免了用戶程序?qū)ο到y(tǒng)資源的越權(quán)訪問(wèn),提升了系統(tǒng)安全性和穩(wěn)定性。

Linux 使用兩級(jí)保護(hù)機(jī)制:0 級(jí)供系統(tǒng)內(nèi)核使用,3 級(jí)供用戶程序使用。

當(dāng)一個(gè)任務(wù)(進(jìn)程)執(zhí)行系統(tǒng)調(diào)用而陷入內(nèi)核代碼中執(zhí)行時(shí),稱進(jìn)程處于內(nèi)核運(yùn)行態(tài)(內(nèi)核態(tài))。此時(shí)處理器處于特權(quán)級(jí)最高的(0級(jí))內(nèi)核代碼中執(zhí)行。當(dāng)進(jìn)程處于內(nèi)核態(tài)時(shí),執(zhí)行的內(nèi)核代碼會(huì)使用當(dāng)前進(jìn)程的內(nèi)核棧。每個(gè)進(jìn)程都有自己的內(nèi)核棧。

當(dāng)進(jìn)程在執(zhí)行用戶自己的代碼的時(shí)候,我們稱其處于用戶運(yùn)行態(tài)(用戶態(tài))。此時(shí)處理器在特權(quán)級(jí)最低的(3級(jí))用戶代碼中運(yùn)行。

系統(tǒng)調(diào)用主要通過(guò)如下兩個(gè)函數(shù)來(lái)實(shí)現(xiàn):

copy_from_user() //將數(shù)據(jù)從用戶空間拷貝到內(nèi)核空間
copy_to_user() //將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間

3.2 Linux 下的傳統(tǒng) IPC 通信原理

理解了上面的幾個(gè)概念,我們?cè)賮?lái)看看傳統(tǒng)的 IPC 方式中,進(jìn)程之間是如何實(shí)現(xiàn)通信的。

通常的做法是消息發(fā)送方將要發(fā)送的數(shù)據(jù)存放在內(nèi)存緩存區(qū)中,通過(guò)系統(tǒng)調(diào)用進(jìn)入內(nèi)核態(tài)。然后內(nèi)核程序在內(nèi)核空間分配內(nèi)存,開辟一塊內(nèi)核緩存區(qū),調(diào)用 copy_from_user() 函數(shù)將數(shù)據(jù)從用戶空間的內(nèi)存緩存區(qū)拷貝到內(nèi)核空間的內(nèi)核緩存區(qū)中。同樣的,接收方進(jìn)程在接收數(shù)據(jù)時(shí)在自己的用戶空間開辟一塊內(nèi)存緩存區(qū),然后內(nèi)核程序調(diào)用 copy_to_user() 函數(shù)將數(shù)據(jù)從內(nèi)核緩存區(qū)拷貝到接收進(jìn)程的內(nèi)存緩存區(qū)。這樣數(shù)據(jù)發(fā)送方進(jìn)程和數(shù)據(jù)接收方進(jìn)程就完成了一次數(shù)據(jù)傳輸,我們稱完成了一次進(jìn)程間通信。如下圖:

寫給 Android 應(yīng)用工程師的 Binder 原理剖析,Android,移動(dòng)開發(fā),Framework,android,binder,移動(dòng)開發(fā),Framework,java

這種傳統(tǒng)的 IPC 通信方式有兩個(gè)問(wèn)題:

  1. 性能低下,一次數(shù)據(jù)傳遞需要經(jīng)歷:內(nèi)存緩存區(qū) --> 內(nèi)核緩存區(qū) --> 內(nèi)存緩存區(qū),需要 2 次數(shù)據(jù)拷貝;
  2. 接收數(shù)據(jù)的緩存區(qū)由數(shù)據(jù)接收進(jìn)程提供,但是接收進(jìn)程并不知道需要多大的空間來(lái)存放將要傳遞過(guò)來(lái)的數(shù)據(jù),因此只能開辟盡可能大的內(nèi)存空間或者先調(diào)用 API 接收消息頭來(lái)獲取消息體的大小,這兩種做法不是浪費(fèi)空間就是浪費(fèi)時(shí)間。

四. Binder 跨進(jìn)程通信原理

理解了 Linux IPC 相關(guān)概念和通信原理,接下來(lái)我們正式介紹下 Binder IPC 的原理。

4.1 動(dòng)態(tài)內(nèi)核可加載模塊 && 內(nèi)存映射

正如前面所說(shuō),跨進(jìn)程通信是需要內(nèi)核空間做支持的。傳統(tǒng)的 IPC 機(jī)制如管道、Socket 都是內(nèi)核的一部分,因此通過(guò)內(nèi)核支持來(lái)實(shí)現(xiàn)進(jìn)程間通信自然是沒(méi)問(wèn)題的。但是 Binder 并不是 Linux 系統(tǒng)內(nèi)核的一部分,那怎么辦呢?這就得益于 Linux 的動(dòng)態(tài)內(nèi)核可加載模塊(Loadable Kernel Module,LKM)的機(jī)制;模塊是具有獨(dú)立功能的程序,它可以被單獨(dú)編譯,但是不能獨(dú)立運(yùn)行。它在運(yùn)行時(shí)被鏈接到內(nèi)核作為內(nèi)核的一部分運(yùn)行。這樣,Android 系統(tǒng)就可以通過(guò)動(dòng)態(tài)添加一個(gè)內(nèi)核模塊運(yùn)行在內(nèi)核空間,用戶進(jìn)程之間通過(guò)這個(gè)內(nèi)核模塊作為橋梁來(lái)實(shí)現(xiàn)通信。

在 Android 系統(tǒng)中,這個(gè)運(yùn)行在內(nèi)核空間,負(fù)責(zé)各個(gè)用戶進(jìn)程通過(guò) Binder 實(shí)現(xiàn)通信的內(nèi)核模塊就叫 Binder 驅(qū)動(dòng)(Binder Dirver)。

那么在 Android 系統(tǒng)中用戶進(jìn)程之間是如何通過(guò)這個(gè)內(nèi)核模塊(Binder 驅(qū)動(dòng))來(lái)實(shí)現(xiàn)通信的呢?難道是和前面說(shuō)的傳統(tǒng) IPC 機(jī)制一樣,先將數(shù)據(jù)從發(fā)送方進(jìn)程拷貝到內(nèi)核緩存區(qū),然后再將數(shù)據(jù)從內(nèi)核緩存區(qū)拷貝到接收方進(jìn)程,通過(guò)兩次拷貝來(lái)實(shí)現(xiàn)嗎?顯然不是,否則也不會(huì)有開篇所說(shuō)的 Binder 在性能方面的優(yōu)勢(shì)了。

這就不得不通道 Linux 下的另一個(gè)概念:內(nèi)存映射。

Binder IPC 機(jī)制中涉及到的內(nèi)存映射通過(guò) mmap() 來(lái)實(shí)現(xiàn),mmap() 是操作系統(tǒng)中一種內(nèi)存映射的方法。內(nèi)存映射簡(jiǎn)單的講就是將用戶空間的一塊內(nèi)存區(qū)域映射到內(nèi)核空間。映射關(guān)系建立后,用戶對(duì)這塊內(nèi)存區(qū)域的修改可以直接反應(yīng)到內(nèi)核空間;反之內(nèi)核空間對(duì)這段區(qū)域的修改也能直接反應(yīng)到用戶空間。

內(nèi)存映射能減少數(shù)據(jù)拷貝次數(shù),實(shí)現(xiàn)用戶空間和內(nèi)核空間的高效互動(dòng)。兩個(gè)空間各自的修改能直接反映在映射的內(nèi)存區(qū)域,從而被對(duì)方空間及時(shí)感知。也正因?yàn)槿绱?,?nèi)存映射能夠提供對(duì)進(jìn)程間通信的支持。

4.2 Binder IPC 實(shí)現(xiàn)原理

Binder IPC 正是基于內(nèi)存映射(mmap)來(lái)實(shí)現(xiàn)的,但是 mmap() 通常是用在有物理介質(zhì)的文件系統(tǒng)上的。

比如進(jìn)程中的用戶區(qū)域是不能直接和物理設(shè)備打交道的,如果想要把磁盤上的數(shù)據(jù)讀取到進(jìn)程的用戶區(qū)域,需要兩次拷貝(磁盤–>內(nèi)核空間–>用戶空間);通常在這種場(chǎng)景下 mmap() 就能發(fā)揮作用,通過(guò)在物理介質(zhì)和用戶空間之間建立映射,減少數(shù)據(jù)的拷貝次數(shù),用內(nèi)存讀寫取代I/O讀寫,提高文件讀取效率。

而 Binder 并不存在物理介質(zhì),因此 Binder 驅(qū)動(dòng)使用 mmap() 并不是為了在物理介質(zhì)和用戶空間之間建立映射,而是用來(lái)在內(nèi)核空間創(chuàng)建數(shù)據(jù)接收的緩存空間。

一次完整的 Binder IPC 通信過(guò)程通常是這樣:

  1. 首先 Binder 驅(qū)動(dòng)在內(nèi)核空間創(chuàng)建一個(gè)數(shù)據(jù)接收緩存區(qū);
  2. 接著在內(nèi)核空間開辟一塊內(nèi)核緩存區(qū),建立內(nèi)核緩存區(qū)內(nèi)核中數(shù)據(jù)接收緩存區(qū)之間的映射關(guān)系,以及內(nèi)核中數(shù)據(jù)接收緩存區(qū)接收進(jìn)程用戶空間地址的映射關(guān)系;
  3. 發(fā)送方進(jìn)程通過(guò)系統(tǒng)調(diào)用 copy_from_user() 將數(shù)據(jù) copy 到內(nèi)核中的內(nèi)核緩存區(qū),由于內(nèi)核緩存區(qū)和接收進(jìn)程的用戶空間存在內(nèi)存映射,因此也就相當(dāng)于把數(shù)據(jù)發(fā)送到了接收進(jìn)程的用戶空間,這樣便完成了一次進(jìn)程間的通信。

如下圖:
寫給 Android 應(yīng)用工程師的 Binder 原理剖析,Android,移動(dòng)開發(fā),Framework,android,binder,移動(dòng)開發(fā),Framework,java

五. Binder 通信模型

介紹完 Binder IPC 的底層通信原理,接下來(lái)我們看看實(shí)現(xiàn)層面是如何設(shè)計(jì)的。

一次完整的進(jìn)程間通信必然至少包含兩個(gè)進(jìn)程,通常我們稱通信的雙方分別為客戶端進(jìn)程(Client)和服務(wù)端進(jìn)程(Server),由于進(jìn)程隔離機(jī)制的存在,通信雙方必然需要借助 Binder 來(lái)實(shí)現(xiàn)。

5.1 Client/Server/ServiceManager/驅(qū)動(dòng)

前面我們介紹過(guò),Binder 是基于 C/S 架構(gòu)的。由一系列的組件組成,包括 Client、Server、ServiceManager、Binder 驅(qū)動(dòng)。其中 Client、Server、Service Manager 運(yùn)行在用戶空間,Binder 驅(qū)動(dòng)運(yùn)行在內(nèi)核空間。其中 Service Manager 和 Binder 驅(qū)動(dòng)由系統(tǒng)提供,而 Client、Server 由應(yīng)用程序來(lái)實(shí)現(xiàn)。Client、Server 和 ServiceManager 均是通過(guò)系統(tǒng)調(diào)用 open、mmap 和 ioctl 來(lái)訪問(wèn)設(shè)備文件 /dev/binder,從而實(shí)現(xiàn)與 Binder 驅(qū)動(dòng)的交互來(lái)間接的實(shí)現(xiàn)跨進(jìn)程通信。

寫給 Android 應(yīng)用工程師的 Binder 原理剖析,Android,移動(dòng)開發(fā),Framework,android,binder,移動(dòng)開發(fā),Framework,java

Client、Server、ServiceManager、Binder 驅(qū)動(dòng)這幾個(gè)組件在通信過(guò)程中扮演的角色就如同互聯(lián)網(wǎng)中服務(wù)器(Server)、客戶端(Client)、DNS域名服務(wù)器(ServiceManager)以及路由器(Binder 驅(qū)動(dòng))之前的關(guān)系。

通常我們?cè)L問(wèn)一個(gè)網(wǎng)頁(yè)的步驟是這樣的:首先在瀏覽器輸入一個(gè)地址,如 www.google.com 然后按下回車鍵。但是并沒(méi)有辦法通過(guò)域名地址直接找到我們要訪問(wèn)的服務(wù)器,因此需要首先訪問(wèn) DNS 域名服務(wù)器,域名服務(wù)器中保存了 www.google.com 對(duì)應(yīng)的 ip 地址 10.249.23.13,然后通過(guò)這個(gè) ip 地址才能放到到 www.google.com 對(duì)應(yīng)的服務(wù)器。

寫給 Android 應(yīng)用工程師的 Binder 原理剖析,Android,移動(dòng)開發(fā),Framework,android,binder,移動(dòng)開發(fā),Framework,java

Android Binder 設(shè)計(jì)與實(shí)現(xiàn)一文中對(duì) Client、Server、ServiceManager、Binder 驅(qū)動(dòng)有很詳細(xì)的描述,以下是部分摘錄:

Binder 驅(qū)動(dòng)
Binder 驅(qū)動(dòng)就如同路由器一樣,是整個(gè)通信的核心;驅(qū)動(dòng)負(fù)責(zé)進(jìn)程之間 Binder 通信的建立,Binder 在進(jìn)程之間的傳遞,Binder 引用計(jì)數(shù)管理,數(shù)據(jù)包在進(jìn)程之間的傳遞和交互等一系列底層支持。

ServiceManager 與實(shí)名 Binder
ServiceManager 和 DNS 類似,作用是將字符形式的 Binder 名字轉(zhuǎn)化成 Client 中對(duì)該 Binder 的引用,使得 Client 能夠通過(guò) Binder 的名字獲得對(duì) Binder 實(shí)體的引用。注冊(cè)了名字的 Binder 叫實(shí)名 Binder,就像網(wǎng)站一樣除了除了有 IP 地址意外還有自己的網(wǎng)址。Server 創(chuàng)建了 Binder,并為它起一個(gè)字符形式,可讀易記得名字,將這個(gè) Binder 實(shí)體連同名字一起以數(shù)據(jù)包的形式通過(guò) Binder 驅(qū)動(dòng)發(fā)送給 ServiceManager ,通知 ServiceManager 注冊(cè)一個(gè)名為“張三”的 Binder,它位于某個(gè) Server 中。驅(qū)動(dòng)為這個(gè)穿越進(jìn)程邊界的 Binder 創(chuàng)建位于內(nèi)核中的實(shí)體節(jié)點(diǎn)以及 ServiceManager 對(duì)實(shí)體的引用,將名字以及新建的引用打包傳給 ServiceManager。ServiceManger 收到數(shù)據(jù)后從中取出名字和引用填入查找表。

細(xì)心的讀者可能會(huì)發(fā)現(xiàn),ServierManager 是一個(gè)進(jìn)程,Server 是另一個(gè)進(jìn)程,Server 向 ServiceManager 中注冊(cè) Binder 必然涉及到進(jìn)程間通信。當(dāng)前實(shí)現(xiàn)進(jìn)程間通信又要用到進(jìn)程間通信,這就好像蛋可以孵出雞的前提卻是要先找只雞下蛋!Binder 的實(shí)現(xiàn)比較巧妙,就是預(yù)先創(chuàng)造一只雞來(lái)下蛋。ServiceManager 和其他進(jìn)程同樣采用 Bidner 通信,ServiceManager 是 Server 端,有自己的 Binder 實(shí)體,其他進(jìn)程都是 Client,需要通過(guò)這個(gè) Binder 的引用來(lái)實(shí)現(xiàn) Binder 的注冊(cè),查詢和獲取。ServiceManager 提供的 Binder 比較特殊,它沒(méi)有名字也不需要注冊(cè)。當(dāng)一個(gè)進(jìn)程使用 BINDER_SET_CONTEXT_MGR 命令將自己注冊(cè)成 ServiceManager 時(shí) Binder 驅(qū)動(dòng)會(huì)自動(dòng)為它創(chuàng)建 Binder 實(shí)體(這就是那只預(yù)先造好的那只雞)。其次這個(gè) Binder 實(shí)體的引用在所有 Client 中都固定為 0 而無(wú)需通過(guò)其它手段獲得。也就是說(shuō),一個(gè) Server 想要向 ServiceManager 注冊(cè)自己的 Binder 就必須通過(guò)這個(gè) 0 號(hào)引用和 ServiceManager 的 Binder 通信。類比互聯(lián)網(wǎng),0 號(hào)引用就好比是域名服務(wù)器的地址,你必須預(yù)先動(dòng)態(tài)或者手工配置好。要注意的是,這里說(shuō)的 Client 是相對(duì)于 ServiceManager 而言的,一個(gè)進(jìn)程或者應(yīng)用程序可能是提供服務(wù)的 Server,但對(duì)于 ServiceManager 來(lái)說(shuō)它仍然是個(gè) Client。

Client 獲得實(shí)名 Binder 的引用
Server 向 ServiceManager 中注冊(cè)了 Binder 以后, Client 就能通過(guò)名字獲得 Binder 的引用了。Client 也利用保留的 0 號(hào)引用向 ServiceManager 請(qǐng)求訪問(wèn)某個(gè) Binder: 我申請(qǐng)?jiān)L問(wèn)名字叫張三的 Binder 引用。ServiceManager 收到這個(gè)請(qǐng)求后從請(qǐng)求數(shù)據(jù)包中取出 Binder 名稱,在查找表里找到對(duì)應(yīng)的條目,取出對(duì)應(yīng)的 Binder 引用作為回復(fù)發(fā)送給發(fā)起請(qǐng)求的 Client。從面向?qū)ο蟮慕嵌瓤?,Server 中的 Binder 實(shí)體現(xiàn)在有兩個(gè)引用:一個(gè)位于 ServiceManager 中,一個(gè)位于發(fā)起請(qǐng)求的 Client 中。如果接下來(lái)有更多的 Client 請(qǐng)求該 Binder,系統(tǒng)中就會(huì)有更多的引用指向該 Binder ,就像 Java 中一個(gè)對(duì)象有多個(gè)引用一樣。

5.2 Binder 通信過(guò)程

至此,我們大致能總結(jié)出 Binder 通信過(guò)程:

  1. 首先,一個(gè)進(jìn)程使用 BINDER_SET_CONTEXT_MGR 命令通過(guò) Binder 驅(qū)動(dòng)將自己注冊(cè)成為 ServiceManager;
  2. Server 通過(guò)驅(qū)動(dòng)向 ServiceManager 中注冊(cè) Binder(Server 中的 Binder 實(shí)體),表明可以對(duì)外提供服務(wù)。驅(qū)動(dòng)為這個(gè) Binder 創(chuàng)建位于內(nèi)核中的實(shí)體節(jié)點(diǎn)以及 ServiceManager 對(duì)實(shí)體的引用,將名字以及新建的引用打包傳給 ServiceManager,ServiceManger 將其填入查找表。
  3. Client 通過(guò)名字,在 Binder 驅(qū)動(dòng)的幫助下從 ServiceManager 中獲取到對(duì) Binder 實(shí)體的引用,通過(guò)這個(gè)引用就能實(shí)現(xiàn)和 Server 進(jìn)程的通信。

我們看到整個(gè)通信過(guò)程都需要 Binder 驅(qū)動(dòng)的接入。下圖能更加直觀的展現(xiàn)整個(gè)通信過(guò)程(為了進(jìn)一步抽象通信過(guò)程以及呈現(xiàn)上的方便,下圖我們忽略了 Binder 實(shí)體及其引用的概念):

寫給 Android 應(yīng)用工程師的 Binder 原理剖析,Android,移動(dòng)開發(fā),Framework,android,binder,移動(dòng)開發(fā),Framework,java

5.3 Binder 通信中的代理模式

我們已經(jīng)解釋清楚 Client、Server 借助 Binder 驅(qū)動(dòng)完成跨進(jìn)程通信的實(shí)現(xiàn)機(jī)制了,但是還有個(gè)問(wèn)題會(huì)讓我們困惑。A 進(jìn)程想要 B 進(jìn)程中某個(gè)對(duì)象(object)是如何實(shí)現(xiàn)的呢?畢竟它們分屬不同的進(jìn)程,A 進(jìn)程 沒(méi)法直接使用 B 進(jìn)程中的 object。

前面我們介紹過(guò)跨進(jìn)程通信的過(guò)程都有 Binder 驅(qū)動(dòng)的參與,因此在數(shù)據(jù)流經(jīng) Binder 驅(qū)動(dòng)的時(shí)候驅(qū)動(dòng)會(huì)對(duì)數(shù)據(jù)做一層轉(zhuǎn)換。當(dāng) A 進(jìn)程想要獲取 B 進(jìn)程中的 object 時(shí),驅(qū)動(dòng)并不會(huì)真的把 object 返回給 A,而是返回了一個(gè)跟 object 看起來(lái)一模一樣的代理對(duì)象 objectProxy,這個(gè) objectProxy 具有和 object 一摸一樣的方法,但是這些方法并沒(méi)有 B 進(jìn)程中 object 對(duì)象那些方法的能力,這些方法只需要把把請(qǐng)求參數(shù)交給驅(qū)動(dòng)即可。對(duì)于 A 進(jìn)程來(lái)說(shuō)和直接調(diào)用 object 中的方法是一樣的。

當(dāng) Binder 驅(qū)動(dòng)接收到 A 進(jìn)程的消息后,發(fā)現(xiàn)這是個(gè) objectProxy 就去查詢自己維護(hù)的表單,一查發(fā)現(xiàn)這是 B 進(jìn)程 object 的代理對(duì)象。于是就會(huì)去通知 B 進(jìn)程調(diào)用 object 的方法,并要求 B 進(jìn)程把返回結(jié)果發(fā)給自己。當(dāng)驅(qū)動(dòng)拿到 B 進(jìn)程的返回結(jié)果后就會(huì)轉(zhuǎn)發(fā)給 A 進(jìn)程,一次通信就完成了。

寫給 Android 應(yīng)用工程師的 Binder 原理剖析,Android,移動(dòng)開發(fā),Framework,android,binder,移動(dòng)開發(fā),Framework,java

5.4 Binder 的完整定義

現(xiàn)在我們可以對(duì) Binder 做個(gè)更加全面的定義了:

  • 從進(jìn)程間通信的角度看,Binder 是一種進(jìn)程間通信的機(jī)制;
  • 從 Server 進(jìn)程的角度看,Binder 指的是 Server 中的 Binder 實(shí)體對(duì)象;
  • 從 Client 進(jìn)程的角度看,Binder 指的是對(duì) Binder 代理對(duì)象,是 Binder 實(shí)體對(duì)象的一個(gè)遠(yuǎn)程代理
  • 從傳輸過(guò)程的角度看,Binder 是一個(gè)可以跨進(jìn)程傳輸?shù)膶?duì)象;Binder 驅(qū)動(dòng)會(huì)對(duì)這個(gè)跨越進(jìn)程邊界的對(duì)象對(duì)一點(diǎn)點(diǎn)特殊處理,自動(dòng)完成代理對(duì)象和本地對(duì)象之間的轉(zhuǎn)換。

六. 手動(dòng)編碼實(shí)現(xiàn)跨進(jìn)程調(diào)用

通常我們?cè)谧鲩_發(fā)時(shí),實(shí)現(xiàn)進(jìn)程間通信用的最多的就是 AIDL。當(dāng)我們定義好 AIDL 文件,在編譯時(shí)編譯器會(huì)幫我們生成代碼實(shí)現(xiàn) IPC 通信。借助 AIDL 編譯以后的代碼能幫助我們進(jìn)一步理解 Binder IPC 的通信原理。

但是無(wú)論是從可讀性還是可理解性上來(lái)看,編譯器生成的代碼對(duì)開發(fā)者并不友好。比如一個(gè) BookManager.aidl 文件對(duì)應(yīng)會(huì)生成一個(gè) BookManager.java 文件,這個(gè) java 文件包含了一個(gè) BookManager 接口、一個(gè) Stub 靜態(tài)的抽象類和一個(gè) Proxy 靜態(tài)類。Proxy 是 Stub 的靜態(tài)內(nèi)部類,Stub 又是 BookManager 的靜態(tài)內(nèi)部類,這就造成了可讀性和可理解性的問(wèn)題。

Android 之所以這樣設(shè)計(jì)其實(shí)是有道理的,因?yàn)楫?dāng)有多個(gè) AIDL 文件的時(shí)候把 BookManager、Stub、Proxy 放在同一個(gè)文件里能有效避免 Stub 和 Proxy 重名的問(wèn)題。

因此便于大家理解,下面我們來(lái)手動(dòng)編寫代碼來(lái)實(shí)現(xiàn)跨進(jìn)程調(diào)用。

6.1 各 Java 類職責(zé)描述

在正式編碼實(shí)現(xiàn)跨進(jìn)程調(diào)用之前,先介紹下實(shí)現(xiàn)過(guò)程中用到的一些類。了解了這些類的職責(zé),有助于我們更好的理解和實(shí)現(xiàn)跨進(jìn)程通信。

  • IBinder : IBinder 是一個(gè)接口,代表了一種跨進(jìn)程通信的能力。只要實(shí)現(xiàn)了這個(gè)借口,這個(gè)對(duì)象就能跨進(jìn)程傳輸。

  • IInterface : IInterface 代表的就是 Server 進(jìn)程對(duì)象具備什么樣的能力(能提供哪些方法,其實(shí)對(duì)應(yīng)的就是 AIDL 文件中定義的接口)

  • Binder : Java 層的 Binder 類,代表的其實(shí)就是 Binder 本地對(duì)象。BinderProxy 類是 Binder 類的一個(gè)內(nèi)部類,它代表遠(yuǎn)程進(jìn)程的 Binder 對(duì)象的本地代理;這兩個(gè)類都繼承自 IBinder, 因而都具有跨進(jìn)程傳輸?shù)哪芰?;?shí)際上,在跨越進(jìn)程的時(shí)候,Binder 驅(qū)動(dòng)會(huì)自動(dòng)完成這兩個(gè)對(duì)象的轉(zhuǎn)換。

  • Stub : AIDL 的時(shí)候,編譯工具會(huì)給我們生成一個(gè)名為 Stub 的靜態(tài)內(nèi)部類;這個(gè)類繼承了 Binder, 說(shuō)明它是一個(gè) Binder 本地對(duì)象,它實(shí)現(xiàn)了 IInterface 接口,表明它具有 Server 承諾給 Client 的能力;Stub 是一個(gè)抽象類,具體的 IInterface 的相關(guān)實(shí)現(xiàn)需要開發(fā)者自己實(shí)現(xiàn)。

6.2 實(shí)現(xiàn)過(guò)程講解

一次跨進(jìn)程通信必然會(huì)涉及到兩個(gè)進(jìn)程,在這個(gè)例子中 RemoteService 作為服務(wù)端進(jìn)程,提供服務(wù);ClientActivity 作為客戶端進(jìn)程,使用 RemoteService 提供的服務(wù)。如下圖:

寫給 Android 應(yīng)用工程師的 Binder 原理剖析,Android,移動(dòng)開發(fā),Framework,android,binder,移動(dòng)開發(fā),Framework,java

那么服務(wù)端進(jìn)程具備什么樣的能力?能為客戶端提供什么樣的服務(wù)呢?還記得我們前面介紹過(guò)的 IInterface 嗎,它代表的就是服務(wù)端進(jìn)程具體什么樣的能力。因此我們需要定義一個(gè) BookManager 接口,BookManager 繼承自 IIterface,表明服務(wù)端具備什么樣的能力。

/**
 * 這個(gè)類用來(lái)定義服務(wù)端 RemoteService 具備什么樣的能力
 */
public interface BookManager extends IInterface {

    void addBook(Book book) throws RemoteException;
}

只定義服務(wù)端具備什么要的能力是不夠的,既然是跨進(jìn)程調(diào)用,那么接下來(lái)我們得實(shí)現(xiàn)一個(gè)跨進(jìn)程調(diào)用對(duì)象 Stub。Stub 繼承 Binder, 說(shuō)明它是一個(gè) Binder 本地對(duì)象;實(shí)現(xiàn) IInterface 接口,表明具有 Server 承諾給 Client 的能力;Stub 是一個(gè)抽象類,具體的 IInterface 的相關(guān)實(shí)現(xiàn)需要調(diào)用方自己實(shí)現(xiàn)。

public abstract class Stub extends Binder implements BookManager {

    ...

    public static BookManager asInterface(IBinder binder) {
        if (binder == null)
            return null;
        IInterface iin = binder.queryLocalInterface(DESCRIPTOR);
        if (iin != null && iin instanceof BookManager)
            return (BookManager) iin;
        return new Proxy(binder);
    }

    ...

    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {

            case INTERFACE_TRANSACTION:
                reply.writeString(DESCRIPTOR);
                return true;

            case TRANSAVTION_addBook:
                data.enforceInterface(DESCRIPTOR);
                Book arg0 = null;
                if (data.readInt() != 0) {
                    arg0 = Book.CREATOR.createFromParcel(data);
                }
                this.addBook(arg0);
                reply.writeNoException();
                return true;

        }
        return super.onTransact(code, data, reply, flags);
    }

    ...
}

Stub 類中我們重點(diǎn)介紹下 asInterfaceonTransact

先說(shuō)說(shuō) asInterface,當(dāng) Client 端在創(chuàng)建和服務(wù)端的連接,調(diào)用 bindService 時(shí)需要?jiǎng)?chuàng)建一個(gè) ServiceConnection 對(duì)象作為入?yún)ⅰT?ServiceConnection 的回調(diào)方法 onServiceConnected 中 會(huì)通過(guò)這個(gè) asInterface(IBinder binder) 拿到 BookManager 對(duì)象,這個(gè) IBinder 類型的入?yún)?binder 是驅(qū)動(dòng)傳給我們的,正如你在代碼中看到的一樣,方法中會(huì)去調(diào)用 binder.queryLocalInterface() 去查找 Binder 本地對(duì)象,如果找到了就說(shuō)明 Client 和 Server 在同一進(jìn)程,那么這個(gè) binder 本身就是 Binder 本地對(duì)象,可以直接使用。否則說(shuō)明是 binder 是個(gè)遠(yuǎn)程對(duì)象,也就是 BinderProxy。因此需要我們創(chuàng)建一個(gè)代理對(duì)象 Proxy,通過(guò)這個(gè)代理對(duì)象來(lái)是實(shí)現(xiàn)遠(yuǎn)程訪問(wèn)。

接下來(lái)我們就要實(shí)現(xiàn)這個(gè)代理類 Proxy 了,既然是代理類自然需要實(shí)現(xiàn) BookManager 接口。

public class Proxy implements BookManager {

    ...

    public Proxy(IBinder remote) {
        this.remote = remote;
    }

    @Override
    public void addBook(Book book) throws RemoteException {

        Parcel data = Parcel.obtain();
        Parcel replay = Parcel.obtain();
        try {
            data.writeInterfaceToken(DESCRIPTOR);
            if (book != null) {
                data.writeInt(1);
                book.writeToParcel(data, 0);
            } else {
                data.writeInt(0);
            }
            remote.transact(Stub.TRANSAVTION_addBook, data, replay, 0);
            replay.readException();
        } finally {
            replay.recycle();
            data.recycle();
        }
    }

    ...
}

我們看看 addBook() 的實(shí)現(xiàn);在 Stub 類中,addBook(Book book) 是一個(gè)抽象方法,Client 端需要繼承并實(shí)現(xiàn)它。

  • 如果 Client 和 Server 在同一個(gè)進(jìn)程,那么直接就是調(diào)用這個(gè)方法。
  • 如果是遠(yuǎn)程調(diào)用,Client 想要調(diào)用 Server 的方法就需要通過(guò) Binder 代理來(lái)完成,也就是上面的 Proxy。

在 Proxy 中的 addBook() 方法中首先通過(guò) Parcel 將數(shù)據(jù)序列化,然后調(diào)用 remote.transact()。正如前文所述 Proxy 是在 Stub 的 asInterface 中創(chuàng)建,能走到創(chuàng)建 Proxy 這一步就說(shuō)明 Proxy 構(gòu)造函數(shù)的入?yún)⑹?BinderProxy,即這里的 remote 是個(gè) BinderProxy 對(duì)象。最終通過(guò)一系列的函數(shù)調(diào)用,Client 進(jìn)程通過(guò)系統(tǒng)調(diào)用陷入內(nèi)核態(tài),Client 進(jìn)程中執(zhí)行 addBook() 的線程掛起等待返回;驅(qū)動(dòng)完成一系列的操作之后喚醒 Server 進(jìn)程,調(diào)用 Server 進(jìn)程本地對(duì)象的 onTransact()。最終又走到了 Stub 中的 onTransact() 中,onTransact() 根據(jù)函數(shù)編號(hào)調(diào)用相關(guān)函數(shù)(在 Stub 類中為 BookManager 接口中的每個(gè)函數(shù)中定義了一個(gè)編號(hào),只不過(guò)上面的源碼中我們簡(jiǎn)化掉了;在跨進(jìn)程調(diào)用的時(shí)候,不會(huì)傳遞函數(shù)而是傳遞編號(hào)來(lái)指明要調(diào)用哪個(gè)函數(shù));我們這個(gè)例子里面,調(diào)用了 Binder 本地對(duì)象的 addBook() 并將結(jié)果返回給驅(qū)動(dòng),驅(qū)動(dòng)喚醒 Client 進(jìn)程里剛剛掛起的線程并將結(jié)果返回。

這樣一次跨進(jìn)程調(diào)用就完成了。

最后建議大家在不借助 AIDL 的情況下手寫實(shí)現(xiàn) Client 和 Server 進(jìn)程的通信,加深對(duì) Binder 通信過(guò)程的理解。

通過(guò)上文對(duì)Binder 原理剖析,小編整理了一份Binder相關(guān)的面試學(xué)習(xí)文檔給大家進(jìn)行參考學(xué)習(xí),如果大家有需要進(jìn)行參考學(xué)習(xí)的話,可以直接去我 GitHub地址:https://github.com/733gh/Android-T3 中查閱。

寫給 Android 應(yīng)用工程師的 Binder 原理剖析,Android,移動(dòng)開發(fā),Framework,android,binder,移動(dòng)開發(fā),Framework,java文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-642223.html

到了這里,關(guān)于寫給 Android 應(yīng)用工程師的 Binder 原理剖析的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【第6章】認(rèn)證技術(shù)原理與應(yīng)用(信息安全工程師)--軟考筆記

    第6章 認(rèn)證技術(shù)原理與應(yīng)用 6.1.1 認(rèn)證概念 認(rèn)證是一個(gè)實(shí)體向另外一個(gè)實(shí)體證明其所聲稱的身份的過(guò)程。在認(rèn)證過(guò)程中,需要被證實(shí)的實(shí)體是聲稱者,負(fù)責(zé)檢查確認(rèn)聲稱者的實(shí)體是驗(yàn)證者。 認(rèn)證一般由標(biāo)識(shí) (Identification) 和鑒別 (Authentication) 兩部分組成。 標(biāo)識(shí)是用來(lái)代表實(shí)體對(duì)

    2024年02月15日
    瀏覽(26)
  • 【第14章】惡意代碼防范技術(shù)原理@信息安全工程師 軟考筆記

    【第14章】惡意代碼防范技術(shù)原理@信息安全工程師 軟考筆記

    第14章 惡意代碼防范技術(shù)原理 14.1 惡意代碼概述 14.1.1 惡意代碼定義與分類 惡意代碼的英文是 Malicious Code,它是一種違背目標(biāo)系統(tǒng)安全策略的程序代碼,會(huì)造成目標(biāo)系統(tǒng)信息泄露、資源濫用,破壞系統(tǒng)的完整性及可用性。它能夠經(jīng)過(guò)存儲(chǔ)介質(zhì)或網(wǎng)絡(luò)進(jìn)行傳播,從一 臺(tái)計(jì)算機(jī)系

    2023年04月08日
    瀏覽(19)
  • CEAC之《計(jì)算機(jī)應(yīng)用助理工程師》3

    CEAC之《計(jì)算機(jī)應(yīng)用助理工程師》3

    ? ??????個(gè)人主頁(yè):@微微的豬食小窩 歡迎 點(diǎn)贊?? 收藏? 留言?? 加關(guān)注?! 本文由? 微微的豬食小窩 ?原創(chuàng) 收錄于專欄?【CEAC證書】 1 在子數(shù)據(jù)表中最多可以嵌套多少級(jí)子數(shù)據(jù)表? A、5級(jí) B、6級(jí) C、7級(jí) D、8級(jí) D 2 在窗體中有一個(gè)圖片框(picEx)和一個(gè)計(jì)時(shí)器(tmrEx),運(yùn)

    2024年02月05日
    瀏覽(26)
  • CEAC之《計(jì)算機(jī)應(yīng)用助理工程師》2

    CEAC之《計(jì)算機(jī)應(yīng)用助理工程師》2

    ? ??????個(gè)人主頁(yè):@微微的豬食小窩 歡迎 點(diǎn)贊?? 收藏? 留言?? 加關(guān)注?! 本文由? 微微的豬食小窩 ?原創(chuàng) 收錄于專欄?【CEAC證書】 1 組合框的常用屬性有 ____________ 。 A、Index B、Text C、Caption D、ListCount A,B,D 2 在下面的對(duì)象中,哪些對(duì)象提供了數(shù)據(jù)錄入的功能? A、表對(duì)

    2024年02月12日
    瀏覽(24)
  • CEAC 之《計(jì)算機(jī)應(yīng)用助理工程師》1

    CEAC 之《計(jì)算機(jī)應(yīng)用助理工程師》1

    ? ??????個(gè)人主頁(yè):@微微的豬食小窩 歡迎 點(diǎn)贊?? 收藏? 留言?? 加關(guān)注?! 本文由? 微微的豬食小窩 ?原創(chuàng) 收錄于專欄?【CEAC證書】 1 組合框有3種不同的類型,這3種類型是下拉式組合框、簡(jiǎn)單組合框、下拉式列表框,分別通過(guò)把 Style 屬性設(shè)置為、 0、1、2 來(lái)實(shí)現(xiàn)。

    2024年02月05日
    瀏覽(29)
  • AIGC文本生成智能應(yīng)用(ChatGPT)提示工程師技巧

    AIGC文本生成智能應(yīng)用(ChatGPT)提示工程師技巧

    我是荔園微風(fēng),作為一名在IT界整整25年的老兵,今天來(lái)看一下AIGC文本生成智能提示工程師技巧。 當(dāng)你在使用類似于ChatGPT這樣的AIGC文本生成智能應(yīng)用時(shí),有沒(méi)有想過(guò),你所問(wèn)的問(wèn)題中的每一個(gè)詞語(yǔ)對(duì)AIGC文本生成智能應(yīng)用給你的回答的好與不好都有著很重要的聯(lián)系。 我們?cè)谑?/p>

    2024年02月13日
    瀏覽(32)
  • 硬件工程師必會(huì)電路模塊之MOS管應(yīng)用

    硬件工程師必會(huì)電路模塊之MOS管應(yīng)用

    MOS管開關(guān)電路 學(xué)習(xí)過(guò)模擬電路的人都知道三極管是流控流器件,也就是由基極電流控制集電極與發(fā)射極之間的電流;而MOS管是壓控流器件,也就是由柵極上所加的電壓控制漏極與源極之間電流。 MOSFET管是FET的一種,可以被制造為增強(qiáng)型或者耗盡型,P溝道或N溝道共四種類型,

    2024年02月10日
    瀏覽(43)
  • Prompt工程師指南[應(yīng)用篇]:Prompt應(yīng)用、ChatGPT|Midjouney Prompt Engineering

    主題: 與 ChatGPT 對(duì)話 Python 筆記本 Topics: ChatGPT介紹 審查對(duì)話任務(wù) 與ChatGPT對(duì)話 Python筆記本 ChatGPT介紹 ChatGPT是OpenAI訓(xùn)練的一種新型模型,可以進(jìn)行對(duì)話交互。該模型經(jīng)過(guò)訓(xùn)練,可以按照提示中的指令,在對(duì)話上下文中提供適當(dāng)?shù)幕貞?yīng)。ChatGPT 可以幫助回答問(wèn)題、建議菜譜、按

    2024年02月04日
    瀏覽(25)
  • 機(jī)器人應(yīng)用工程師的入職思考day3

    機(jī)器人應(yīng)用工程師的入職思考day3

    [1]學(xué)習(xí)高級(jí)弧焊內(nèi)容,了解機(jī)器人在導(dǎo)軌與變位機(jī)等設(shè)備的配合下進(jìn)行弧焊的方式 [2]了解專家焊內(nèi)容,學(xué)會(huì)如何用兩個(gè)機(jī)器人進(jìn)行同步焊接 [3]場(chǎng)景應(yīng)用 1、高級(jí)弧焊的運(yùn)動(dòng)學(xué)相關(guān)操作 2、兩個(gè)或多個(gè)機(jī)器人之間進(jìn)行同步焊接的方法 3、之前的疑惑點(diǎn)答疑 周二 2024/4/2 上午9:3

    2024年04月28日
    瀏覽(37)
  • HCIA-HarmonyOS應(yīng)用開發(fā)工程師 V2.0 模擬考試

    低代碼開發(fā)提供UI可視化開發(fā)能力,支持自由拖拽組件和可視化數(shù)據(jù)綁定,可快速預(yù)覽效果,所見(jiàn)即所得。通過(guò)拖拽式編排、可視化配置的方式,幫助開發(fā)者減少重復(fù)性的代碼編寫,快速地構(gòu)建多端應(yīng)用程序。 正確(True) 在使用ArkTS語(yǔ)言開發(fā)界面UI代碼過(guò)程中,如果添加或刪除

    2024年02月04日
    瀏覽(27)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包