Android开机动画总结

Android开机动画总结开机动画制作开机动画两个要点启动开机动画开机动画运行过程代码位置运行简介开机动画遇到的问题制作开机动画两个要点压缩时选择“存储”模式资源文件命名序号,需要和最大序号位数相同,位数不够,前面补零。如00、01、02、。。。、10、11。系统开机动画支持功能配置debug.sf.nobootanimation为0若要关闭开机动画功能,在device目录下的mk文件中配置,确保系统开…

大家好,又见面了,我是你们的朋友全栈君。

制作开机动画两个要点

  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++编辑,无法播放(和配置可能相关)。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/146502.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号