Android應用程式與SurfaceFlinger服務的連接過程分析

来源:https://www.cnblogs.com/linhaostudy/archive/2020/02/29/12382081.html
-Advertisement-
Play Games

[Android應用程式與SurfaceFlinger服務的連接過程分析 ](https://blog.csdn.net/luoshengyang/article/details/7857163) Android系統的開機動畫是由應用程式bootanimation來實現的,它位於/system/bi ...


Android應用程式與SurfaceFlinger服務的連接過程分析

Android系統的開機動畫是由應用程式bootanimation來實現的,它位於/system/bin目錄下,它的具體實現可以參考Android系統的開機畫面顯示過程分析一文。為什麼要選擇Android系統的開機動畫來分析Android應用程式與SurfaceFlinger服務的連接過程呢?首先,負責實現開機動畫的應用程式bootanimation也是一個Android應用程式,只不過它是使用C++語言來開發的;其次,應用程式bootanimation是與UI相關的,即它與使用Java語言來開發的標準Android應用程式一樣,都需要使用SurfaceFlinger服務來創建和渲染自己的Surface,即開機動畫;第三,由於應用程式bootanimation不涉及用戶輸入,即不需要與用戶進行交互(觸摸屏、鍵盤等),因此它能夠以最簡潔的方式來體現Android應用程式與SurfaceFlinger服務的關係。

從前面Android系統的開機畫面顯示過程分析一文可以知道,Android系統的開機動畫是主要一個BootAnimation對象來實現,這個BootAnimation對象在構造的時候,會在內部創建一個SurfaceComposerClient對象來負責創建一個到SurfaceFlinger服務的連接。

BootAnimation類的構造函數實現在文件frameworks/base/cmds/bootanimation/BootAnimation.cpp中,如下所示:

BootAnimation::BootAnimation() : Thread(false)
{
    mSession = new SurfaceComposerClient();
}

 mSession是BootAnimation類的成員變數,它是一個類型為SurfaceComposerClient的強指針,即sp。Android系統的智能指針的相關知識,可以參考Android系統的智能指針(輕量級指針、強指針和弱指針)的實現原理分析一文。

在SurfaceComposerClient類內部,有一個類型為sp的成員變數mClient,如圖1所示:

image

SurfaceComposerClient類的成員變數mClient指向的實際上是一個類型為BpSurfaceComposerClient的Binder代理對象,而這個類型為BpSurfaceComposerClient的Binder代理對象引用的是一個類型為Client的Binder本地對象。在前面Android應用程式與SurfaceFlinger服務的關係概述和學習計劃一文中提到,類型為Client的Binder本地對象是由SurfaceFlinger服務來負責創建的,並且運行在SurfaceFlinger服務中,用來代表使用SurfaceFlinger服務的一個客戶端,即一個與UI相關的Android應用程式。

由於Client類和BpSurfaceComposerClient類分別是一個Binder本地對象類和一個Binder代理對象類,它們都是根據Android系統在應用程式框架層提供的Binder進程間通信庫來實現的,它們的實現結構圖分別如圖2和圖3所示:

image

圖2 Client類的實現結構圖

image

圖3 BpSurfaceComposerClient類的實現結構圖

在圖2和圖3中,涉及到了比較多的Binder進程間通信庫的類,需要讀者對Android系統的Binder進程間通信機制有一定的理解和認識。在前面的Android進程間通信(IPC)機制Binder簡要介紹和學習計劃一系列文章,我們已經學習過Android系統的Binder進程間通信機制了,這裡不再詳述。

圖2和圖3給我們最重要的信息是Client類和BpSurfaceComposerClient類均實現了類型為ISurfaceComposerClient的Binder介面。ISurfaceComposerClient介面有兩個重要的成員函數getControlBlock和createSurface,它們定義在文件frameworks/base/include/surfaceflinger/ISurfaceComposerClient.h中,如下所示:

class ISurfaceComposerClient : public IInterface
{
public:
    ......
 
    virtual sp<IMemoryHeap> getControlBlock() const = 0;
    ......
 
    /*
     * Requires ACCESS_SURFACE_FLINGER permission
     */
    virtual sp<ISurface> createSurface( surface_data_t* data,
                                        int pid,
                                        const String8& name,
                                        DisplayID display,
                                        uint32_t w,
                                        uint32_t h,
                                        PixelFormat format,
                                        uint32_t flags) = 0;
    ......
 
};

其中,成員函數getControlBlock用來獲得由SurfaceFlinger服務創建的一塊用來傳遞UI元數據的匿名共用記憶體,而成員函數createSurface用來請求SurfaceFlinger服務創建一個Surface.從前面Android應用程式與SurfaceFlinger服務的關係概述和學習計劃一文可以知道,用來傳遞UI元數據的匿名共用記憶體最終會被結構化為一個SharedClient對象,這個SharedClient對象在每個應用程式進程中至多存在一個。在接下來的兩篇文章中,我們再詳細分析ISurfaceComposerClient介面的成員函數getControlBlock和createSurface的實現。

理解了SurfaceComposerClient、Client以及BpSurfaceComposerClient這三個類的關係之後,接下來我們就可以分析Android系統的開機動畫應用程式bootanimation是如何與SurfaceFlinger服務建立連接的。

從圖1可以知道,SurfaceComposerClient類繼承了RefBase類,因此,當BootAnimation類在構造函數創建了一個SurfaceComposerClient對象,並且將這個對象賦值給類型為sp的智能指針mSession時,就會導致SurfaceComposerClient類的成員函數onFirstRef被調用,而SurfaceComposerClient類的成員函數onFirstRef在調用的過程中,就會在應用程式bootanimation與SurfaceFlinger服務建立一個連接,這個過程如圖4所示:

image

接下來,我們就詳細分析每一個步驟。

Step 1. SurfaceComposerClient::onFirstRef

void SurfaceComposerClient::onFirstRef()
{
    sp<ISurfaceComposer> sm(getComposerService());
    if (sm != 0) {
        sp<ISurfaceComposerClient> conn = sm->createConnection();
        if (conn != 0) {
            mClient = conn;
            ......
            mStatus = NO_ERROR;
        }
    }
}

SurfaceComposerClient類的成員函數onFirstRef實現在文件frameworks/base/libs/surfaceflinger_client/SurfaceComposerClient.cpp文件中。

SurfaceComposerClient類的成員函數getComposerService用來獲得SurfaceFlinger服務的一個代理介面,它的實現如下所示:

sp<ISurfaceComposer> ComposerService::getComposerService() {
    return ComposerService::getInstance().mComposerService;
}

ComposerService類是單例模式,當我們第一次調用它的靜態函數getInstance的時候,它就會在構造函數中獲得SurfaceFlinger服務的一個代理介面,並且保存在它的成員變數mComposerService中,如下所示:

ComposerService::ComposerService()
: Singleton<ComposerService>() {
    const String16 name("SurfaceFlinger");
    while (getService(name, &mComposerService) != NO_ERROR) {
        usleep(250000);
    }
    mServerCblkMemory = mComposerService->getCblk();
    mServerCblk = static_cast<surface_flinger_cblk_t volatile *>(
            mServerCblkMemory->getBase());
}

在ComposerService類的構造函數中,除了會獲得SurfaceFlinger服務的代理介面之外,還會通過這個代理介面的成員函數getCblk來獲得一塊匿名共用記憶體mServerCblkMemory。這塊匿名共用記憶體是由SurfaceFlinger服務創建的,用來描述系統顯示屏的信息,例如,顯示屏的個數、大小、方向、密度等等信息。由於這些信息可以通過一個surface_flinger_cblk_t對象來描述,因此,ComposerService類的構造函數最後就將前面從SurfaceFlinger服務獲得的一塊匿名共用記憶體結構化為一個surface_flinger_cblk_t對象,並且保存在ComposerService類的成員變數mServerCblk中。

回到SurfaceComposerClient類的成員函數onFirstRef中,由於SurfaceFlinger服務實現了ISurfaceComposer介面,因此,我們可以將前面獲得的SurfaceFlinger服務的代理介面賦值給一個類型為ISurfaceComposer的強指針sm,並且調用它的成員函數createConnection來請求SurfaceFlinger服務創建一個連接,即創建一個類型為Client的Binder對象,並且將這個Binder對象的一個代理介面conn返回來。SurfaceComposerClient類獲得了SurfaceFlinger服務返回來的Client代理介面conn之後,就將它保存自己的成員變數mClient中,這樣開機動畫應用程式bootanimation後續就可以通過它來請求SurfaceFlinger創建和渲染Surface了。

接下來,我們就繼續分析SurfaceFlinger服務的成員函數createConnection的實現,以便可以瞭解它是如何為Android應用程式創建一個連接的。

Step 2. SurfaceFlinger::createConnection

sp<ISurfaceComposerClient> SurfaceFlinger::createConnection()
{
    sp<ISurfaceComposerClient> bclient;
    sp<Client> client(new Client(this));
    status_t err = client->initCheck();
    if (err == NO_ERROR) {
        bclient = client;
    }
    return bclient;
}

SurfaceFlinger類的成員函數createConnection實現在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中,它的實現很簡單,只是創建了一個類型為Client的Binder對象client,並且獲得它的一個ISurfaceComposerClient介面,最後將這個ISurfaceComposerClient介面,即一個Client代理對象,返回給開機動畫應用程式bootanimation。

接下來,我們再繼續分析Client對象的創建過程,,即Client類的構造函數的實現。

Step 3. new Client

Client::Client(const sp<SurfaceFlinger>& flinger)
    : mFlinger(flinger), mNameGenerator(1)
{
}

Client類有兩個成員變數mFlinger和mNameGenerator,它們的類型分別為sp和int32_t,前者指向了SurfaceFlinger服務,而後者用來生成SurfaceFlinger服務為Android應用程式所創建的每一個Surface的名稱。例如,假設一個Android應用程式請求SurfaceFlinger創建了兩個Surface,那麼第一個Surface的名稱就由數字1來描述,而第二個Surface就由數字2來描述,依次類推。從前面Android應用程式與SurfaceFlinger服務的關係概述和學習計劃這篇文章可以知道,一個Android應用程式最多可以創建31個Surface。

回到SurfaceFlinger類的成員函數createConnection中,它將一個指向了一個Client對象的ISurfaceComposerClient介面返回到開機動畫應用程式bootanimation之後,開機動畫應用程式bootanimation就可以將它封裝成一個類型為BpSurfaceComposerClient的Binder代理對象。

Step 4. return BpSurfaceComposerClient

類型為BpSurfaceComposerClient的Binder代理對象的封裝過程實現在SurfaceFlinger服務的Binder代理對象類BpSurfaceComposer的成員函數createConnection中,如下所示:

class BpSurfaceComposer : public BpInterface<ISurfaceComposer>
{
public:
    ......
 
    virtual sp<ISurfaceComposerClient> createConnection()
    {
        uint32_t n;
        Parcel data, reply;
        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
        remote()->transact(BnSurfaceComposer::CREATE_CONNECTION, data, &reply);
        return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder());
    }
 
    ......
}

interface_cast是一個模板函數,它定義在framework/base/include/binder/IInterface.h文件中:

template<typename INTERFACE>  
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)  
{  
    return INTERFACE::asInterface(obj);  
}  

從這裡就可以看出,當模板參數為ISurfaceComposerClient的時候,模板函數interface_cast實際就是通過調用ISurfaceComposerClient類的靜態成員函數asInterface來將參數obj所描述的一個Binder代理對象,即一個BpBinder對象,封裝成一個BpSurfaceComposerClient對象。

 ISurfaceComposerClient類的靜態成員函數asInterface是由frameworks/base/libs/surfaceflinger_client/ISurfaceComposerClient.cpp文件中的IMPLEMENT_META_INTERFACE巨集來定義的,如下所示:

IMPLEMENT_META_INTERFACE(SurfaceComposerClient, "android.ui.ISurfaceComposerClient");

IMPLEMENT_META_INTERFACE巨集展開後,得到ISurfaceComposerClient類的靜態成員函數asInterface的實現如下所示:

android::sp<ISurfaceComposerClient> ISurfaceComposerClient::asInterface(const android::sp<android::IBinder>& obj)       {                                                                                       
    android::sp<ISurfaceComposerClient> intr;                                                      
      
    if (obj != NULL) {                                                                       
        intr = static_cast<ISurfaceComposerClient*>(                                                    
                    obj->queryLocalInterface(ISurfaceComposerClient::descriptor).get());  
          
        if (intr == NULL) {                  
            intr = new BpSurfaceComposerClient(obj);                                          
        }                                            
    }  
    return intr;                                    
}     

 參數obj是從BpSurfaceComposer類的成員函數createConnection傳進來的,它指向的實際上是一個BpBinder對象。當我們調用一個BpBinder對象的成員函數queryLocalInterface時,獲得的是一個NULL指針,因此,ISurfaceComposerClient類的靜態成員函數asInterface最後就會將參數obj所指向的一個BpBinder對象封裝成一個BpSurfaceComposerClient對象,並且返回給調用者。

        BpSurfaceComposerClient對象的更具體封裝過程可以參考前面淺談Android系統進程間通信(IPC)機制Binder中的Server和Client獲得Service Manager介面之路一文中所描述的BpServiceManager對象的封裝過程。

        至此,開機動畫應用程式bootanimation就通過SurfaceComposerClient類來與SurfaceFlinger服務建立一個連接了。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 15.1 查詢表的所有數據行 1、命令語法:select from where 其中,select,from,where 是不能隨便改的,是關鍵字,支持大小寫 2、查看表 test 中所有數據 a.進入指定庫後查詢 mysql use oldboy Database changed mysql sh ...
  • mysql select from test; + + + + + | id | age | name | shouji | + + + + + | 1 | NULL | oldgirl | NULL | | 2 | NULL | 老男孩 | NULL | | 3 | NULL | etiantia ...
  • 1、命令語法: nsert into [()[,.....])]values(值 1)[,(值 n)]​ 2、新建一個簡單的測試表 test mysql use oldboy Reading table information for completion of table and column ...
  • 資料庫索引就象書的目錄一樣,如果在欄位上建立了索引,那麼以索引列為查詢條件時 可以加快查詢數據的速度 查詢資料庫,按主鍵查詢是最快的,每個表只能有一個主鍵列,但是可以有多個普通索 引列,主鍵列要求列的所有內容必須唯一,而普通索引列不要求內容必須唯一 主鍵就類似我們在學校學習時的學號一樣,班級里是唯一 ...
  • 11.1 以預設字元集 latin1 建庫 由於我們併為特別設置資料庫及客戶端字元集(因為編譯是二進位安裝,預設是 latin1 字元集) mysql create database oldboy; Query OK, 1 row affected (0.11 sec) mysql show cre ...
  • 0.1 使用語法: 通過在 mysql 中輸入 help grant 得到如下幫助信息 CREATE USER 'jeffrey'@'localhost' IDENTIFIED BY 'mypass'; GRANT ALL ON db1. TO 'jeffrey'@'localhost'; GRAN ...
  • 9.1 創建資料庫 命令語法:create database show databases like 'oldboy'; + + | Database (oldboy) | + + | oldboy | + + 1 row in set (0.04 sec) 查看建庫的語句 (註意:如果編譯時指定字 ...
  • 8.1 什麼是 SQL? SQL,英文全稱 Structured Query Language,中文意思是結構化查詢語言,它是一種對應關係數據中的數據進行定義和操作的語言方法,是大多數關係資料庫管理系統所支持的工業標準。 資料庫查詢語言 SQL 是一種資料庫查詢和程式設計語言,用於存取數據以及查詢、 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...