天天看點

[Android5.1]開機動畫desc.txt描述檔案的分析

在上篇文章《Android5.1開機畫面顯示工作流程分析》中,詳細分析了Android開機動畫顯示的工作流程。其中提到了每個開機動畫壓縮檔案中必須包含一個描述檔案desc.txt,該檔案用來描述開機動畫具體是怎麼樣顯示的。這篇文章就對desc.txt進行一個詳細的解讀。

1 desc.txt檔案格式分析

desc.txt檔案由若幹行組成,每一行代表一種描述。下面以一個具體的例子為例,具體說明

480 640 20
p 1 0 folder1
p 2 20 folder2
c 0 0 folder3
c 1 0 folder4
 
           

第1行用來描述開機動畫在螢幕顯示的大小及速度。具體為:開機動畫的寬度為480個像素,高度為640個像素,顯示頻率為每秒20幀,即每幀顯示1/20秒。

下面的每一行代表一個片段,顯示的時候會按照順序從上到下依次顯示。第1個字元為片段類型,有'c'和'p'兩種,兩者的差別後面會結合代碼說明。

第2個數字為該片段重複顯示的次數,如果為‘0’,表示會無限重複顯示;第3個數字為兩次顯示之間的間隔,機關為第一行中定義的每幀顯示的時間;第4個字元串為該片段所在的檔案夾,一個片段可以由多個png圖檔組成,都存放在folder檔案夾中。

“p 1 0 folder1”代表該片段顯示1次,與下一個片段間隔0s,該片段的顯示圖檔路徑為bootanimation.zip/folder1。

“p 2 20 folder2”代表該片段顯示2次,且兩次之間顯示的間隔為20*(1/20)=1s,與下一個片段間隔20*(1/20)=1s,該片段的顯示圖檔路徑為bootanimation.zip/folder2。

“c 0 0 folder3”代表該片段無限循環顯示,且兩次顯示的間隔為0s,與下一個片段間隔0s,該片段的顯示圖路徑為bootanimation.zip/folder3。

“c 1 10 folder4”代表該片段顯示1次,顯示後暫停10*(1/20)=0.5s,該片段的顯示圖路徑為bootanimation.zip/folder4。

2 "p"片段和“c”片段的差別

在早期Android版本中隻有“p”片段,且movie()中的顯示代碼如下:

for (int i=0 ; i<pcount && !exitPending() ; i++) {  
        const Animation::Part& part(animation.parts[i]);  
        const size_t fcount = part.frames.size();  
        glBindTexture(GL_TEXTURE_2D, 0);  
  
        for (int r=0 ; !part.count || r<part.count ; r++) {  
            for (int j=0 ; j<fcount && !exitPending(); j++) {  
                const Animation::Frame& frame(part.frames[j]);  

	        .......

    .....
}
           

裡面的主要參數和函數說嗎如下:

pcount---顯示片段的數量,比如上面的例子,pcount=4

p.count---該片段的重複顯示次數。

fcount---該片段中png圖檔的數量

exitPending()---如果SurfaceFlinger服務通知bootanimation停止顯示動畫,則該函數傳回值為true,否則為false。

第一個for循環用于順序顯示所有片段,第二個for循環用于重複顯示該片段,第三個for循環用于順序顯示該片段中所有的png圖檔。

分析代碼,可知:若exitPending()傳回值為true,即SurfaceFlinger服務要求bootanimation停止顯示動畫,則不管目前顯示到哪個片段或png圖檔,都會導緻退出for循環,進而停止開機動畫的顯示。

在Android5.1中,加入了“c”片段。對與以"c"辨別的片段,即使exitPending()傳回值為true,也會繼續顯示。

我們分析一下源碼,首先看一下movie()中解析desc.txt的代碼:

// Parse the description file  
    for (;;) {  
        ...... 
        if (sscanf(l, "%d %d %d %d", &width, &height, &fps, &flg) >= 3) {  
            animation.width = width;  
            animation.height = height;  
            animation.fps = fps;  
        }  
        else if (sscanf(l, " %c %d %d %s #%6s", &pathType, &count, &pause, path, color) >= 4) {  
            Animation::Part part;  
            part.playUntilComplete = pathType == 'c';  
            part.count = count;  
            part.pause = pause;  
            part.path = path;  
            part.audioFile = NULL;  
            if (!parseColor(color, part.backgroundColor)) {  
                ALOGE("> invalid color '#%s'", color);  
                part.backgroundColor[0] = 0.0f;  
                part.backgroundColor[1] = 0.0f;  
                part.backgroundColor[2] = 0.0f;  
            }  
            animation.parts.add(part);  
        }  
  
        s = ++endl;  
    }
           

可以看到,如果pathType==‘c’,part.playUntilComplete等于true,否則為false。接着,看一下顯示代碼:

for (size_t i=0 ; i<pcount ; i++) {  
        const Animation::Part& part(animation.parts[i]);  
        const size_t fcount = part.frames.size();  
        glBindTexture(GL_TEXTURE_2D, 0);  
  
        for (int r=0 ; !part.count || r<part.count ; r++) {  
            // Exit any non playuntil complete parts immediately  
            if(exitPending() && !part.playUntilComplete)  
                break;  
 
            ......

            for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {  
                
		......
  
                checkExit();  
            }  
  
            usleep(part.pause * ns2us(frameDuration));  
  
            // For infinite parts, we've now played them at least once, so perhaps exit  
            if(exitPending() && !part.count)  
                break;  
        }  
  
        ......  
    } 
           

可以看到,如果exitPending()傳回值為true且part.playUntilComplete=false,則會break。即:當SurfaceFlinger服務要求bootanimation停止顯示動畫時,以‘p’辨別的片段會停止,而以'c'辨別的片段會繼續顯示。這就是兩者之間的主要差別。

這裡有個問題:重複循環顯示的'c'辨別片段,會不受任何限制的一直顯示下去,這顯然是不合适的。

于是在第二個for循環體最後,有如下代碼:

// For infinite parts, we've now played them at least once, so perhaps exit  
            if(exitPending() && !part.count)  
                break; 
           

意思是,如果檢測到SurfaceFlinger服務要求bootanimation停止顯示,且該片段的顯示次數為'0',即重複循環顯示,則會break停止顯示。

我猜想"c"辨別的意思是continue,即:即使SurfaceFlinger要求bootanimation停止動畫,bootanimation也不會立刻停止動畫,它會等c辨別片段都顯示完畢後,再停止。

這樣,我們可以利用'c'和'p'片段的差別,設計出更靈活的開機動畫。