Android开机动画总结

制作开机动画两个要点

  1. 压缩时选择“存储”模式
  2. 资源文件命名序号,需要和最大序号位数相同,位数不够,前面补零。如00、01、02、。。。、10、11。
  3. 系统开机动画支持功能
// 注意:不同的android版本,配置方法可能不同,该配置是Android6.0的方法
配置debug.sf.nobootanimation 为0

若要关闭开机动画功能,在device目录下的mk文件中配置,确保系统开机默认值为1;若要支持动画,不用配置,默认为0

启动开机动画

  1. 定义服务

开机动画在init.rc中定义为native service,如

service shutdownanim /system/bin/bootanimation /system/media/shutdownanimation.zip
    class core                                                                                                                                                                            
    user graphics                                                                                                                                                                         
    group graphics audio media                                                                                                                                                            
    disabled                                                                                                                                                                              
    oneshot                                     
  1. 开机init进程注册服务
    此时服务只是被注册,并没有实际运行
  2. 系统启动开机动画
    在frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp中,surfaceflinger初始化时执行startBootAnim()
void SurfaceFlinger::init() {
    ALOGI(  "SurfaceFlinger's main thread ready to run. "
            "Initializing graphics H/W...");

    { // Autolock scope 
        Mutex::Autolock _l(mStateLock);
        
        // initialize EGL for the default display
        mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        eglInitialize(mEGLDisplay, NULL, NULL);
        ......
    // initialize our drawing state
    mDrawingState = mCurrentState;
    
    // set initial conditions (e.g. unblank default device)
    initializeDisplays();
    
    mRenderEngine->primeCache();
    
    // start boot animation
    startBootAnim();
    
    ALOGV("Done initializing");
}   

startBootAnim()函数实现

void SurfaceFlinger::startBootAnim() {
    // start boot animation
    property_set("service.bootanim.exit", "0");
    property_set("ctl.start", "bootanim");
}

从实现看出,在系统起来后,若要运行./bootanimtion,需要先把service.bootanim.exit的值设置为0,然后通过ctl.start启动。命令如下:

setprop service.bootanim.exit 0
setprop ctl.start bootanim

开机动画运行过程

代码位置

frameworks/base/cmds/bootanimation

bootanimation/
├── Android.mk               编译脚本
├── audioplay.cpp            音频播放
├── AudioPlayer.cpp          播放器
├── AudioPlayer.h
├── audioplay.h
├── BootAnimation.cpp        开机动画播放主流程,重点关注
├── BootAnimation.h
├── bootanimation_main.cpp   init启动过程注册服务文件
├── bootanim.rc              Android新版本,服务启动方式
└── FORMAT.md                说明文档,markdown格式
运行简介

主要介绍开机动画解析播放流程,不做细节说明,文件是BootAnimation.cpp
1、初始化

status_t BootAnimation::readyToRun() {
  • 初始化显示功能
  • 确定bootanimation.zip文件位置,此处客制化较多。部分代码如下:
ZipFileRO* zipFile = NULL;

    if ((encryptedAnimation &&
            (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
            ((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) ||

            ((access(OEM_BOOTANIMATION_FILE, R_OK) == 0) &&
            ((zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) ||

            ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
            ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) {
        mZip = zipFile;
    }

2、播放线程

bool BootAnimation::threadLoop()
{
    bool r;
    // We have no bootanimation file, so we use the stock android logo
    // animation.
    playMusic();
    if (mZip == NULL) {
        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;
}

如果没有找到开机动画文件,默认播放Android默认字样,执行android();
3、movie实现
保存开机动画文件数据的结构体,BootAnimation.h中定义

struct Animation {
        struct Frame {
            String8 name;
            FileMap* map;
            mutable GLuint tid;
            bool operator < (const Frame& rhs) const {
                return name < rhs.name;
            }
        };
        struct Part {
            int count;
            int pause;
            String8 path;
            SortedVector<Frame> frames;
            bool playUntilComplete;
            float backgroundColor[3];
            FileMap* audioFile;
        };
        int fps;
        int width;
        int height;
        Vector<Part> parts;
    };
  • 解析desc.txt文件
    获取播放配置,初始化Animation变量。注意,Animation是开机动画中定义的结构体,是对开机动画文件解析后数据存储对象。
if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
            // ALOGD("> w=%d, h=%d, fps=%d", width, height, fps);
            // 获取播放要求,和设备分辨率相关;每秒播放帧数
            animation.width = width;
            animation.height = height;
            animation.fps = fps;
        }
        else if (sscanf(l, " %c %d %d %s #%6s", &pathType, &count, &pause, path, color) >= 4) {
            // ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s", pathType, count, pause, path, color);
            // 动画中可以有多个播放文件夹,一个文件夹一个播放规则,结构体中保存多个part,记录不同规则
            Animation::Part part;
            part.playUntilComplete = pathType == 'c';
            part.count = count;      // 播放次数,0 循环
            part.pause = pause;    // 没播放完一周期,暂停时间,循环播放有效
            part.path = path;         // 该值是该part的文件夹名
            part.audioFile = NULL;  // 播放音频的文件
            if (!parseColor(color, part.backgroundColor)) {   // 检查颜色配置,没有设置,默认是00000
                ALOGE("> invalid color '#%s'", color);
                part.backgroundColor[0] = 0.0f;
                part.backgroundColor[1] = 0.0f;
                part.backgroundColor[2] = 0.0f;
            }
            animation.parts.add(part);   // 保存一个播放规则的part
  • 录入图片数据
Animation::Part& part(animation.parts.editItemAt(j));    // 获取一个part
if (leaf == "audio.wav") {
     // a part may have at most one audio file
     part.audioFile = map;
 } else {
 	 // 一个part中保存一个frame容器,播放文件在容器中按照文件名自动排序,因此必须留意文件名命令
     Animation::Frame frame;   
     frame.name = leaf; 
     frame.map = map;
     // add的同时和已经保存的文件比较,插入正确位置,保证后面有序播放
     part.frames.add(frame);
 }
  • 播放
	const Animation::Frame& frame(part.frames[j]);
	nsecs_t lastFrame = systemTime();
	
	if (r > 0) {
		  // 第二轮播放,不需要初始化
	      glBindTexture(GL_TEXTURE_2D, frame.tid);
    } else {
    	  // 第一轮播放时初始化
	      if (part.count != 1) {
	          glGenTextures(1, &frame.tid);
	          glBindTexture(GL_TEXTURE_2D, frame.tid);
	          glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	          glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	      }
	      initTexture(frame);
	  }

	  if (!clearReg.isEmpty()) {
	      Region::const_iterator head(clearReg.begin());
	      Region::const_iterator tail(clearReg.end());
	      glEnable(GL_SCISSOR_TEST);
	      while (head != tail) {
	          const Rect& r2(*head++);
	          glScissor(r2.left, mHeight - r2.bottom,
	                  r2.width(), r2.height());
	          glClear(GL_COLOR_BUFFER_BIT);
	      }
	      glDisable(GL_SCISSOR_TEST);
	  }
	  // specify the y center as ceiling((mHeight - animation.height) / 2)
	  // which is equivalent to mHeight - (yc + animation.height)
	  glDrawTexiOES(xc, mHeight - (yc + animation.height),
	                0, animation.width, animation.height);
	  eglSwapBuffers(mDisplay, mSurface);
	
	  nsecs_t now = systemTime();
	  nsecs_t delay = frameDuration - (now - lastFrame);
	  //ALOGD("%lld, %lld", ns2ms(now - lastFrame), ns2ms(delay));
	  lastFrame = now;
	
	  if (delay > 0) {
	      struct timespec spec;
	      spec.tv_sec  = (now + delay) / 1000000000;
	      spec.tv_nsec = (now + delay) % 1000000000;
	      int err;
	      do {
	          err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
	      } while (err<0 && errno == EINTR);
	  }
	  // 检查是否退出,在ActivityManagerService中通知退出
	  checkExit();

退出设置

base/services/core/java/com/android/server/am/ActivityManagerService.java:6992:                    SystemProperties.set("service.bootanim.exit", "1");

开机动画遇到的问题

  1. 动画播放文件乱序
    原因: 文件命名错误,仅是简单的1、2、3、。。。,播放文件超过9,就会出错
    解决方案: 参考“制作开机动画两个要点”
  2. 开机动画前面几帧被遮住,没有看到被播放
    原因: kernel logo播放完后,从kernel空间切换到用户空间,存在场景切换,会黑屏。亮屏条件是播放器检测到有帧数据送入,因此前面的帧数据会被遮住(遮住的帧数,因芯片解决方案不同而不同)。
    解决方案: 确定具体平台会被隐藏的帧数,开始播放时,重复送入第一帧数据
Animation::Frame tmp_frame;
if(wait_count < WAIT_FRAME_COUNT && i == 0 && (j == 0 || j == 1)) {
	j = 0;
}
// ALOGD("---------- i = %d j = %d wait_count = %d",i,j,wait_count);
// add end

const Animation::Frame& frame(part.frames[j]);
nsecs_t lastFrame = systemTime();
if (r > 0 || (wait_count > 0 && wait_count < WAIT_FRAME_COUNT)) {
// ALOGD("-------r------ wait_count: %d",wait_count);
    glBindTexture(GL_TEXTURE_2D, frame.tid);
} else {
    // ALOGD("------------- wait_count: %d",wait_count);
    if (part.count != 1) {
        glGenTextures(1, &frame.tid);
        glBindTexture(GL_TEXTURE_2D, frame.tid);
        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    }
    initTexture(frame);
}

// add by icetech
// resolv bootanim can't display the first three frame 
if(wait_count < WAIT_FRAME_COUNT) wait_count++;
// add end

3、相同的文件,压缩方式相同,一个可以播放一个不可以播放
desc.txt要使用文本文档编辑;使用notepad++编辑,无法播放(和配置可能相关)。

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值