1.1 Android開機動畫實現方式
目前實現Android開機動畫的方式主要是逐幀動畫和OpenGL動畫。
?逐幀動畫
逐幀動畫是一種常見的動畫形式(Frame By Frame),其原理是在“連續的關鍵幀”中分解動畫動作,也就是在時間軸的每幀上逐幀繪制不同的內容,使其連續播放而成動畫。 因為逐幀動畫的幀序列內容不一樣,不但給制作增加了負擔而且最終輸出的文件量也很大,但它的優勢也很明顯:逐幀動畫具有非常大的靈活性,幾乎可以表現任何想表現的內容,而它類似與電影的播放模式,很適合於表演細膩的動畫。
逐幀動畫是網上廣泛流傳的一種實現方法。實現原理是將一系列圖片打包成bootanimation.zip放入/system/media/目錄,系統將圖片一幀一幀循環播放形成一個動畫效果。理論上講這種方法應該是可以實現一切動畫需求的,但是實踐後你會發現當bootanimation.zip大於5M的時候,動畫將有明顯卡頓,文件越大動畫越不流暢。所以細心的同學會發現手機上的開機動畫大多都是只有中間一小部分在變化,而四周全是黑色,這樣做是為了使得可以采用100*50(甚至更小)分辨率的圖片,這樣100幀也才幾M的大小。
?OpenGL動畫
OpenGL(英語:Open Graphics Library)是個定義了一個跨編程語言、跨平台的應用程序接口(API)的規范,它用於生成二維、三維圖像。這個接口由近三百五十個不同的函數調用組成,用來從簡單的圖形比特繪制復雜的三維景象。
比較兩種方式我們不難發現,電視平台上和手機有所不同,特別是開機廣告,1920*1080分辨率的圖片一張就幾百KB,由於大小的限制導致動畫幀數很少,所以電視平台采用“逐幀動畫”方法無法做出復雜而流暢的動畫,本文將主要討論OpenGL的實現方式。
1.2 原生開機動畫的源碼分析
在制作自己的開機動畫之前,我們先分析一下Android原生的開機動畫源碼。Android系統的開機動畫源碼位於framework/base/cmds/bootanimation(表1)。
Bootanimation程序目錄:android-x.x/framework/base/cmds/bootanimation/
Android.mk
Android編譯定義
bootanimation_main.cpp
入口文件
BootAnimation.h
BootAnimation類的聲明
BootAnimation.cpp
BootAnimation類的定義和實現
先來看一下bootanimation_main.cpp,這個文件定義了main函數,代碼如下:
[bootanimation_main.cpp]
int main(int argc, char** argv)
{
……
sp
proc(ProcessState::self());
ProcessState::self()->startThreadPool();
// create the boot animation object
sp boot = new BootAnimation();
IPCThreadState::self()->joinThreadPool();
……
}
Main函數的代碼很簡單,首先啟動Process的線程池,然後創建一個BootAnimation對象,最後將BootAnimation對象加入到剛才的線程池中, startThreadPool()和joinThreadPool()的用法可以參考android的binder機制。下面就一起來看一下BootAnimation類:
[BootAnimation.h]
class BootAnimation : public Thread, public IBinder::DeathRecipient
{
……
private:
virtual bool threadLoop();
virtual status_t readyToRun();
virtual void onFirstRef();
virtual void binderDied(const wp& who);
……
status_t initTexture(Texture* texture, AssetManager& asset, const char* name);
status_t initTexture(void* buffer, size_t len);
bool android();
bool movie();
void checkExit();
……
}
BootAnimation類繼承了Thread類和IBinder::DeathRecipient類,幾個override函數的作用如下:
?onFirstRef()屬於其父類RefBase,該函數在強引用sp新增引用計數時調用,就是當 有sp包裝的類初始化的時候調用。
?binderDied(),當對象死掉時或者其他情況導致該Binder發生結束了,就會回調binderDied()方法;
?readyToRun()定義Thread執行前的初始化工作;
?threadLoop()是每個線程類都要實現的,在這裡定義thread的執行內容,這個函數如果返回true,則函數會不停地執行threadloop中的內容,如果這個函數返回false,則threadloop中的內容僅僅執行一次線程就會退出;
[BootAnimation::onFirstRef()]
void BootAnimation::onFirstRef()
{
status_t err = mSession->linkToComposerDeath(this);
ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
if (err == NO_ERROR) {
run("BootAnimation", PRIORITY_DISPLAY);
}
}
void BootAnimation::binderDied(const wp& who)
{
kill( getpid(), SIGKILL );
requestExit();
}
onFirstRef函數中執行了run BootAnimation線程。
[BootAnimation::readyToRun()]
status_t BootAnimation::readyToRun() {
......
// create the native surface
......
// initialize opengl and egl
......
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
......
surface = eglCreateWindowSurface(display, config, s.get(), NULL);
......
mDisplay = display;
......
mSurface = surface;
......
}
readyToRun函數主要是為了得到EGLDisplay mDisplay和EGLDisplay mSurface對象,這兩個對象後面再介紹,最後函數還會查詢是否存在bootanimation.zip並初始化mAndroidAnimation的值。
[BootAnimation::threadLoop()]
#define USER_BOOTANIMATION_FILE "/data/local/bootanimation.zip"
#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
#define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"
......
bool BootAnimation::threadLoop()
{
bool r;
if (mAndroidAnimation) {
r = android();
} else {
r = movie();
}
......
}
BootAnimation::readyToRun中會檢查是否存在bootanimation.zip,如果存在bootanimation.zip這裡就執行movie(),否則執行android()。movie()為逐幀動畫的實現,android()為opengl動畫的實現,本文只考慮無bootanimation.zip的情況。
[BootAnimation::android()]
bool BootAnimation::android()
{
initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");
......
do {
......
glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);
EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);
......
checkExit();
} while (!exitPending());
......
}
終於到了繪圖的步驟了,BootAnimation::android函數包含了整個opengl繪圖的過程,因為android使用的是標准opengl es api,所以opengl的初始化和繪圖過程這裡就不詳細介紹了,這裡需要注意的是eglSwapBuffers(mDisplay, mSurface),這個方法將mSruface投遞到屏幕,這裡用到了兩個EGLDisplay對象,是由於使用了雙緩沖機制,配合sleep實現穩定的刷新率。最後當檢測到系統初始化完成時退出程序。