Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android——開機動畫

Android——開機動畫

編輯:關於Android編程

android系統的開機動畫可分為三個部分,kernel啟動,init進程啟動,android系統服務啟動。這三個開機動畫都是在一個叫做 幀緩沖區(frame buffer)的硬件設備上進行渲染繪制的。

在Linux內核中,每一個硬件設備都有一個主設備號和一個從設備號,它們用來唯一地標識一個硬件設備。對於幀緩沖區硬件設備來說,它們的主設備號定義為FB_MAJOR(29),而從設備號則與注冊的順序有關,它們的值依次等於0,1,2等。

每一個被注冊的幀緩沖區硬件設備在/dev/graphics目錄下都有一個對應的設備文件fb,其中,表示一個從設備號。例如,第一個被注冊的幀緩沖區硬件設備在/dev/graphics目錄下都有一個對應的設備文件fb0。用戶空間的應用程序通過這個

設備文件就可以操作幀緩沖區硬件設備了,即將要顯示的畫面渲染到幀緩沖區硬件設備上去


一.kernel啟動動畫:

kernel的啟動畫面在一般的android系統裡面都是沒有出現的,在kernel的config裡面有這麼兩項:
# CONFIG_FRAMEBUFFER_CONSOLE is not set
# CONFIG_LOGO is not set

我這裡是沒有打開的,第一個代表支持幀緩沖控制台,第二個代表顯示logo。 在編譯控制台的位置: Device Drivers ---> Graphics support ---> Console display driver support ---> Framebuffer Console support Device Drivers ---> Graphics support ---> Bootup logo

kernel中的ainimation 基本上都是在kernel/drivers/video/fbmem.c這個文件中實現。 我的kernel版本是:
VERSION = 3
PATCHLEVEL = 1
SUBLEVEL = 10
EXTRAVERSION =
NAME = "Divemaster Edition"

通過一系列的初始化和准備,最後調用kernel/drivers/video/logo.c中的fb_find_logo(...)來保存kernel 動畫的內容:
const struct linux_logo * __init_refok fb_find_logo(int depth)
{
	const struct linux_logo *logo = NULL;

	if (nologo)
		return NULL;

	if (depth >= 1) {
#ifdef CONFIG_LOGO_LINUX_MONO
		/* Generic Linux logo */
		logo = &logo_linux_mono;
#endif
#ifdef CONFIG_LOGO_SUPERH_MONO
		/* SuperH Linux logo */
		logo = &logo_superh_mono;
#endif
	}
	
	if (depth >= 4) {
#ifdef CONFIG_LOGO_LINUX_VGA16
		/* Generic Linux logo */
		logo = &logo_linux_vga16;
#endif
#ifdef CONFIG_LOGO_BLACKFIN_VGA16
		/* Blackfin processor logo */
		logo = &logo_blackfin_vga16;
#endif
#ifdef CONFIG_LOGO_SUPERH_VGA16
		/* SuperH Linux logo */
		logo = &logo_superh_vga16;
#endif
	}
	
	if (depth >= 8) {
#ifdef CONFIG_LOGO_LINUX_CLUT224
		/* Generic Linux logo */
		logo = &logo_linux_clut224;
#endif
#ifdef CONFIG_LOGO_BLACKFIN_CLUT224
		/* Blackfin Linux logo */
		logo = &logo_blackfin_clut224;
#endif
#ifdef CONFIG_LOGO_DEC_CLUT224
		/* DEC Linux logo on MIPS/MIPS64 or ALPHA */
		logo = &logo_dec_clut224;
#endif
#ifdef CONFIG_LOGO_MAC_CLUT224
		/* Macintosh Linux logo on m68k */
		if (MACH_IS_MAC)
			logo = &logo_mac_clut224;
#endif
#ifdef CONFIG_LOGO_PARISC_CLUT224
		/* PA-RISC Linux logo */
		logo = &logo_parisc_clut224;
#endif
#ifdef CONFIG_LOGO_SGI_CLUT224
		/* SGI Linux logo on MIPS/MIPS64 and VISWS */
		logo = &logo_sgi_clut224;
#endif
#ifdef CONFIG_LOGO_SUN_CLUT224
		/* Sun Linux logo */
		logo = &logo_sun_clut224;
#endif
#ifdef CONFIG_LOGO_SUPERH_CLUT224
		/* SuperH Linux logo */
		logo = &logo_superh_clut224;
#endif
#ifdef CONFIG_LOGO_M32R_CLUT224
		/* M32R Linux logo */
		logo = &logo_m32r_clut224;
#endif
	}
	return logo;
}

這個函數幀緩沖區硬件設備的顏色深度depth,以及不同的編譯選項來獲取內容的指針。 在kernel/include/linux/linux_logo.h中定義了這些內容結構體:
extern const struct linux_logo logo_linux_mono;
extern const struct linux_logo logo_linux_vga16;
extern const struct linux_logo logo_linux_clut224;
extern const struct linux_logo logo_blackfin_vga16;
extern const struct linux_logo logo_blackfin_clut224;
extern const struct linux_logo logo_dec_clut224;
extern const struct linux_logo logo_mac_clut224;
extern const struct linux_logo logo_parisc_clut224;
extern const struct linux_logo logo_sgi_clut224;
extern const struct linux_logo logo_sun_clut224;
extern const struct linux_logo logo_superh_mono;
extern const struct linux_logo logo_superh_vga16;
extern const struct linux_logo logo_superh_clut224;
extern const struct linux_logo logo_m32r_clut224;
extern const struct linux_logo logo_spe_clut224;

這些結構體變量保存kernel/drivers/video/logo/下的 ***.ppm 以及.pbm的文件內容。 通過logo指針保存了動畫內容之後往後走就要來渲染了,調用到fbmem.c中的:

int fb_show_logo(struct fb_info *info, int rotate)
{
	int y;

	y = fb_show_logo_line(info, rotate, fb_logo.logo, 0,
			      num_online_cpus());
	y = fb_show_extra_logos(info, y, rotate);

	return y;
}

同一個文件往下走調用到:
static void fb_do_show_logo(struct fb_info *info, struct fb_image *image,
			    int rotate, unsigned int num)
{
	unsigned int x;

	if (rotate == FB_ROTATE_UR) {
		for (x = 0;
		     x < num && image->dx + image->width <= info->var.xres;
		     x++) {
			info->fbops->fb_imageblit(info, image);
			image->dx += image->width + 8;
		}
	} else if (rotate == FB_ROTATE_UD) {
		for (x = 0; x < num && image->dx >= 0; x++) {
			info->fbops->fb_imageblit(info, image);
			image->dx -= image->width + 8;
		}
	} else if (rotate == FB_ROTATE_CW) {
		for (x = 0;
		     x < num && image->dy + image->height <= info->var.yres;
		     x++) {
			info->fbops->fb_imageblit(info, image);
			image->dy += image->height + 8;
		}
	} else if (rotate == FB_ROTATE_CCW) {
		for (x = 0; x < num && image->dy >= 0; x++) {
			info->fbops->fb_imageblit(info, image);
			image->dy -= image->height + 8;
		}
	}
}
其中: FB_ROTATE_UR 正常顯示 FB_ROTATE_UD 上下倒置
FB_ROTATE_CW 順時針轉90度
FB_ROTATE_CCW 逆時針轉90度
到這裡就開始調用幀緩沖區硬件設備渲染指定的圖像。


二.init進程啟動動畫:

init進程的啟動又要從android下的/system/core/init/init.c的main函數開始了:
int main(int argc, char **argv)
{
int fd_count = 0;
struct pollfd ufds[4];
char *tmpdev;
char* debuggable;
char tmp[32];
int property_set_fd_init = 0;
int signal_fd_init = 0;
int keychord_fd_init = 0;
bool is_charger = false;


if (!strcmp(basename(argv[0]), "ueventd"))
return ueventd_main(argc, argv);


if (!strcmp(basename(argv[0]), "watchdogd"))
return watchdogd_main(argc, argv);
...
queue_builtin_action(console_init_action, "console_init");
...
}



這裡會根據傳進來的參數argv[0]判斷進程名,因為首先啟動的是init進程,之後會根據init.rc配置ueventd以及watchogd等進程,在這裡,這兩個進程加載的執行文件也是從這個init文件開始會以這個main函數為入口,所以加了判別。

在這裡向init進程的action執行隊列添加了一個console_init_action的action,init進程執行相應操作,具體可查看http://blog.csdn.net/jscese/article/details/18700903,最後依次執行action隊列,執行同文件下的:
static int console_init_action(int nargs, char **args)
{
    int fd;
    char tmp[PROP_VALUE_MAX];

    if (console[0]) {
        snprintf(tmp, sizeof(tmp), "/dev/%s", console);
        console_name = strdup(tmp);
    }

    fd = open(console_name, O_RDWR);
    if (fd >= 0)
        have_console = 1;
    close(fd);

    if( load_565rle_image(INIT_IMAGE_FILE) ) {
        fd = open("/dev/tty0", O_WRONLY);
        if (fd >= 0) {
            const char *msg;
                msg = "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"  // console is 40 cols x 30 lines
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "\n"
            "             A N D R O I D ";
            write(fd, msg, strlen(msg));
            close(fd);
        }
    }
    return 0;
}

初始化控制台,通過dev/console設備來訪問控制台,如果有的話 have_console 全局變量被設置為1. 開始加載顯示init進程的動畫,通過load_565rle_image(INIT_IMAGE_FILE)函數,INIT_IMAGE_FILE 宏定義指定動畫內容位置,定義在init.h中
#define INIT_IMAGE_FILE	"/initlogo.rle"

實現在/system/core/init/logo.c中:
int load_565rle_image(char *fn)
{
    struct FB fb;
    struct stat s;
    unsigned short *data, *bits, *ptr;
    unsigned count, max;
    int fd;

    if (vt_set_mode(1)) 
        return -1;

    fd = open(fn, O_RDONLY);
    if (fd < 0) {
        ERROR("cannot open '%s'\n", fn);
        goto fail_restore_text;
    }

    if (fstat(fd, &s) < 0) {
        goto fail_close_file;
    }

    data = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0);
    if (data == MAP_FAILED)
        goto fail_close_file;

    if (fb_open(&fb))
        goto fail_unmap_data;

    max = fb_width(&fb) * fb_height(&fb);
    ptr = data;
    count = s.st_size;
    bits = fb.bits;
    while (count > 3) {
        unsigned n = ptr[0];
        if (n > max)
            break;
        android_memset16(bits, ptr[1], n << 1);
        bits += n;
        max -= n;
        ptr += 2;
        count -= 4;
    }

    munmap(data, s.st_size);
    fb_update(&fb);
    fb_close(&fb);
    close(fd);
    unlink(fn);
    return 0;

fail_unmap_data:
    munmap(data, s.st_size);    
fail_close_file:
    close(fd);
fail_restore_text:
    vt_set_mode(0);
    return -1;
}

調用:
static int vt_set_mode(int graphics)
{
    int fd, r;
    fd = open("/dev/tty0", O_RDWR | O_SYNC);
    if (fd < 0)
        return -1;
    r = ioctl(fd, KDSETMODE, (void*) (graphics ? KD_GRAPHICS : KD_TEXT));
    close(fd);
    return r;
}


打開控制控制台設備文件/dev/tty0,設置控制台的顯示方式,傳入參數為1,控制台將以圖形方式顯示。
往下就是打開傳入的動畫內容文件,fstat計算大小,通過mmap將initlogo.rle內容映射到init進程地址空間,
再打開/dev/graphics/fb0,通過打開這個設備文件就可以訪問幀緩沖區設備,獲取幀緩沖區設備的固定信息和可變信息,然後映射到init進程地址空間,
獲取屏幕的寬高,計算出幀緩沖區能寫入的大小,就知道在init進程地址空間上的大小,通過一個while循環將initlogo.rle內容寫入到幀緩沖區設備上去。
更新init進程開機畫面到屏幕上面:
static void fb_update(struct FB *fb)
{
    fb->vi.yoffset = 1;
    ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi);
    fb->vi.yoffset = 0;
    ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi);
}
通過幀緩沖區設備渲染。

三.系統服務啟動動畫:



這個動畫是android系統最常用的一個動畫,由bootainimation來負責顯示運行,這在init.rc裡面是作為一個service:

1.init.rc 配置:

service bootanim /system/bin/bootanimation
    class main
    user graphics
    group graphics
    disabled
    oneshot


這個是disabled,init啟動解析的時候是不會被啟動的。

2.SurfaceFinger服務的啟動:

在前文http://blog.csdn.net/jscese/article/details/17115395#t7 中啟動到system server部分啟動了系統所需的一些服務時,在system_init.cpp中啟動視頻服務時會啟動一個SurfaceFlinger的服務,獲取一個SurfaceFinger的實例。 SurfaceFlinger類繼承於BinderService模板類,BinderService類的instantiate()函數就是構造對應類型的服務對象,並注冊到ServiceManager進程中/frameworks/native/include/binder/BinderService.h:
   static void instantiate() { publish(); }
  static status_t publish(bool allowIsolated = false) {
        sp sm(defaultServiceManager());
        return sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated);
    }

調用到/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp中構造:

SurfaceFlinger::SurfaceFlinger()
    :   BnSurfaceComposer(), Thread(false),
        mTransactionFlags(0),
        mTransactionPending(false),
        mAnimTransactionPending(false),
        mLayersRemoved(false),
        mRepaintEverything(0),
        mBootTime(systemTime()),
        mVisibleRegionsDirty(false),
        mHwWorkListDirty(false),
        mDebugRegion(0),
        mDebugDDMS(0),
        mDebugDisableHWC(0),
        mDebugDisableTransformHint(0),
        mDebugInSwapBuffers(0),
        mLastSwapBufferTime(0),
        mDebugInTransaction(0),
        mLastTransactionTime(0),
        mBootFinished(false)
{
    ALOGI("SurfaceFlinger is starting");

    // debugging stuff...
    char value[PROPERTY_VALUE_MAX];

    property_get("debug.sf.showupdates", value, "0");
    mDebugRegion = atoi(value);

    property_get("debug.sf.ddms", value, "0");
    mDebugDDMS = atoi(value);
    if (mDebugDDMS) {
        if (!startDdmConnection()) {
            // start failed, and DDMS debugging not enabled
            mDebugDDMS = 0;
        }
    }
    ALOGI_IF(mDebugRegion, "showupdates enabled");
    ALOGI_IF(mDebugDDMS, "DDMS debugging enabled");
}

這裡的構造只是做了一些初始化,獲取系統開機屬性,設置一些變量。
因為SurfaceFlinger又繼承於RefBase類,並重寫了該類的onFirstRef()函數,我們知道,RefBase類的子類對象在第一次創建時,會自動調用onFirstRef()函數,因此在SurfaceFlinger對象構造完成時,將調用onFirstRef()函數。
void SurfaceFlinger::onFirstRef()
{
    mEventQueue.init(this);

    run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY);

    // Wait for the main thread to be done with its initialization
    mReadyToRunBarrier.wait();
}

這裡就從SurfaceFlinger的父類Thread創建一個新的線程,加入threadLoop函數,調用到readyToRun()函數:
status_t SurfaceFlinger::readyToRun()
{
    ALOGI(  "SurfaceFlinger's main thread ready to run. "
            "Initializing graphics H/W...");


   ...

    //  initialize OpenGL ES
    DisplayDevice::makeCurrent(mEGLDisplay, hw, mEGLContext);
    initializeGL(mEGLDisplay);


    // start the EventThread
    mEventThread = new EventThread(this);
    mEventQueue.setEventThread(mEventThread);
...
    // start boot animation
    startBootAnim();


    return NO_ERROR;
}


3.SurfaceFinger中開啟bootainimation服務:

開始執行bootainimation。
void SurfaceFlinger::startBootAnim() {
    // start boot animation
    property_set("service.bootanim.exit", "0");
    property_set("ctl.start", "bootanim");
}

設置系統屬性ctl.start 的值為bootanim。android的系統屬性啟動以及應用可以參考http://blog.csdn.net/jscese/article/details/18700903。 到這裡就啟動了一個新的服務進程bootanim,/frameworks/base/cmds/bootanimation/bootanimation_main.cpp

int main(int argc, char** argv)
{
#if defined(HAVE_PTHREADS)
    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
#endif

    char value[PROPERTY_VALUE_MAX];
    property_get("debug.sf.nobootanimation", value, "0");
    int noBootAnimation = atoi(value);
    ALOGI_IF(noBootAnimation,  "boot animation disabled");
    if (!noBootAnimation) {
       
        ALOGD("boot animation start");
       

        sp proc(ProcessState::self());
        ProcessState::self()->startThreadPool();

        // create the boot animation object
        sp boot = new BootAnimation();

        IPCThreadState::self()->joinThreadPool();

    }
    return 0;
}

獲取nobootanimation這個屬性的值,判斷如果為0的話 那麼接下來就會啟動一個Binder線程池,並且創建一個BootAnimation對象。這個BootAnimation對象就是用來顯示第三個開機畫面的。由於BootAnimation對象在顯示第三個開機畫面的過程中,需要與SurfaceFlinger服務通信,因此,應用程序BootAnimation就需要啟動一個Binder線程池。
BootAnimation類間接地繼承了RefBase類,並且重寫了RefBase類的成員函數onFirstRef, 因此,當一個BootAnimation對象第一次被智能指針引用的時,這個BootAnimation對象的成員函數onFirstRef就會被調用/frameworks/base/cmds/bootanimation/BootAnimation.cpp:
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);
    }
}
同時構造:
BootAnimation::BootAnimation() : Thread(false)
{
    mSession = new SurfaceComposerClient();
}

mSession是BootAnimation類的一個成員變量,它的類型為SurfaceComposerClient,是用來和SurfaceFlinger執行Binder進程間通信的。

4.bootanimation線程構建:

BootAnimation類繼承了Thread類,因此,當BootAnimation類的成員函數onFirstRef調用了父類Thread的成員函數run之後,系統就會創建一個線程,這個線程在第一次運行之前,會調用BootAnimation類的成員函數readyToRun來執行一些初始化工作,後面再調用BootAnimation類的成員函數threadLoop來顯示第三個開機畫面:


status_t BootAnimation::readyToRun() {
    mAssets.addDefaultAssets();


 ...


    mAndroidAnimation = true;

    // If the device has encryption turned on or is in process
    // of being encrypted we show the encrypted boot animation.
    char decrypt[PROPERTY_VALUE_MAX];
    property_get("vold.decrypt", decrypt, "");

    bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);

    if ((encryptedAnimation &&
            (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
            (mZip.open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE) == NO_ERROR)) ||

            ((access(USER_BOOTANIMATION_FILE, R_OK) == 0) &&
            (mZip.open(USER_BOOTANIMATION_FILE) == NO_ERROR)) ||

            ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
            (mZip.open(SYSTEM_BOOTANIMATION_FILE) == NO_ERROR))) {
        mAndroidAnimation = false;
    }

    return NO_ERROR;
}

在這裡為繪制做准備,包括訪問到幀緩沖區設備,獲取屏幕顯示大小等。 判斷是否為android默認開機動畫。
#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"

上面的文件只要有一個存在就代表不是android默認的 而是用戶自定義的開機動畫。

5.bootanimation執行動畫:

准備工作做完了 就執行:
bool BootAnimation::threadLoop()
{
    bool r;


    if (mAndroidAnimation) {
        r = android();
    } else {
        r = movie();
    }



    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroyContext(mDisplay, mContext);
    eglDestroySurface(mDisplay, mSurface);
    mFlingerSurface.clear();
    mFlingerSurfaceControl.clear();
    eglTerminate(mDisplay);
    IPCThreadState::self()->stopProcess();
    return r;
}

1.android默認動畫:

如果是android默認的開機動畫:
bool BootAnimation::android()
{
    initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
    initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");


    // clear screen
    glShadeModel(GL_FLAT);
    glDisable(GL_DITHER);
    glDisable(GL_SCISSOR_TEST);

...
}

這兩張圖片保存在frameworks/base/core/res/assets/images目錄中.
圖片android-logo-mask.png用作動畫前景,它是一個镂空的“ANDROID”圖像。圖片android-logo-shine.png用作動畫背景,它的中間包含有一個高亮的呈45度角的條紋。在每一次循環中,圖片android-logo-shine.png被劃分成左右兩部分內容來顯示。左右兩個部分的圖像寬度隨著時間的推移而此消彼長,這樣就可以使得圖片android-logo-shine.png中間高亮的條紋好像在移動一樣。另一方面,在每一次循環中,圖片android-logo-shine.png都作為一個整體來渲染,而且它的位置是恆定不變的。由於它是一個镂空的“ANDROID”圖像,因此,我們就可以通過它的镂空來看到它背後的圖片android-logo-shine.png的條紋一閃一閃地劃過。


2.用戶自定義動畫:

如果為用戶自定義的開機動畫:
bool BootAnimation::movie()
{
ZipFileRO& zip(mZip);

size_t numEntries = zip.getNumEntries();
ZipEntryRO desc = zip.findEntryByName("desc.txt");
FileMap* descMap = zip.createEntryFileMap(desc);
ALOGE_IF(!descMap, "descMap is null");
if (!descMap) {
return false;
}

String8 desString((char const*)descMap->getDataPtr(),
descMap->getDataLength());
char const* s = desString.string();

Animation animation;

// Parse the description file
for (;;) {
const char* endl = strstr(s, "\n");
if (!endl) break;
String8 line(s, endl - s);
const char* l = line.string();
int fps, width, height, count, pause;
char path[256];
char pathType;

if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {

// ALOGD("> w=%d, h=%d, fps=%d", width, height, fps);
// ALOGD("jscese> w=%d, h=%d", mWidth, mHeight);


animation.width =width;
animation.height =height;
animation.fps = fps;
}
else if (sscanf(l, " %c %d %d %s", &pathType, &count, &pause, path) == 4) {
//LOGD("> type=%c, count=%d, pause=%d, path=%s", pathType, count, pause, path);
Animation::Part part;
part.playUntilComplete = pathType == 'c';
part.count = count;
part.pause = pause;
part.path = path;
animation.parts.add(part);
}

s = ++endl;
}

...
}

找到解析bootanimation.zip 壓縮包,找到desc.txt 文件,這個文件描述了顯示的相關的特性,包括分辨率以及顯示的幀率:
1920 1080 24  
 p   1   0   part1  
 p   0   10  part2  

第一行的三個數字分別表示開機動畫在屏幕中的顯示寬度、高度以及幀速(fps)。剩余的每一行都用來描述一個動畫片斷,這些行必須要以字符“p”來開頭,後面緊跟著兩個數字以及一個文件目錄路徑名稱。第一個數字表示一個片斷的循環顯示次數,如果它的值等於0,那麼就表示無限循環地顯示該動畫片斷。第二個數字表示每一個片斷在兩次循環顯示之間的時間間隔。這個時間間隔是以一個幀的時間為單位的。文件目錄下面保存的是一系列png文件,這些png文件會被依次顯示在屏幕中。


以上面這個desct.txt文件的內容為例,它描述了一個大小為1920 x 1080的開機動畫,動畫的顯示速度為24幀每秒。這個開機動畫包含有兩個片斷part1和part2。片斷part1只顯示一次,它對應的png圖片保存在目錄part1中。片斷part2無限循環地顯示,其中,每兩次循環顯示的時間間隔為10 x (1 / 24)秒,它對應的png圖片保存在目錄part2中。


往後就是收集信息,通過幀緩沖區設備有次序的渲染這些圖片了。

6.bootanimation停止:

1.activity的空閒通知:

當android開機啟動到了Launcher,第一個被啟動起來的應用程序的主線程空閒的時候,這個activity就會向ActivityManagerService發送一個Activity組件空閒的通知, 每當有一個新的Activity組件啟動起來的時候,ActivityThread類都會向它所描述的應用程序主線程的消息隊列注冊一個類型為Idler的空閒消息處理器。這樣一個應用程序的主線程就可以在空閒的時候,向ActivityManagerService發送一個Activity組件空閒通知,相當於是通知ActivityManagerService,一個新的Activity組件已經准備就緒了。
通過一系列的調用到/frameworks/base/services/java/com/android/server/am/ActivityManagerService.java中的:
  public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
        final long origId = Binder.clearCallingIdentity();
        ActivityRecord r = mMainStack.activityIdleInternal(token, false, config);
...
}

調用到/frameworks/base/services/java/com/android/server/am/ActivityStack.java中的:
final ActivityRecord activityIdleInternal(IBinder token, boolean fromTimeout,
Configuration config) {
if (localLOGV) Slog.v(TAG, "Activity idle: " + token);


ActivityRecord res = null;

...
if (mMainStack) {
if (!mService.mBooted) {
mService.mBooted = true;
enableScreen = true;
}
}
...

if (enableScreen) 
{



mService.enableScreenAfterBoot();
}
...
}


回到ActivityManagerService.java中:
    void enableScreenAfterBoot() {
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
                SystemClock.uptimeMillis());
        mWindowManager.enableScreenAfterBoot();

        synchronized (this) {
            updateEventDispatchingLocked();
        }
    }

2.WindowManagerService通信SurfaceFinger設置property:


調用到了WindowManagerService.java中的:
public void performEnableScreen() 
{
synchronized(mWindowMap) {
if (DEBUG_BOOT) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
...
try {
   IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
   if (surfaceFlinger != null) {
    //Slog.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
    Parcel data = Parcel.obtain();
    data.writeInterfaceToken("android.ui.ISurfaceComposer");
    surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
    data, null, 0);
    data.recycle();
    }
...
}


通過一個類型為IBinder.FIRST_CALL_TRANSACTION的進程間通信請求來通知SurfaceFlinger服務停止顯示開機動畫的.
void SurfaceFlinger::bootFinished()
{
    const nsecs_t now = systemTime();
...
 property_set("service.bootanim.exit", "1");
}

最後還是通過設置android的系統屬性通知到init守護進程,init進程來處理property的改變,在這裡就是停掉bootanimation這個服務進程,bootanimation動畫就停止掉了。




  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved