Android开机动画总结

2022-08-28 10:35:04 浏览数 (1)

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

开机动画
  • 制作开机动画两个要点
  • 启动开机动画
  • 开机动画运行过程
    • 代码位置
    • 运行简介

  • 开机动画遇到的问题

制作开机动画两个要点

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

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

启动开机动画

  1. 定义服务

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

代码语言:javascript复制
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()
代码语言:javascript复制
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()函数实现

代码语言:javascript复制
void SurfaceFlinger::startBootAnim() {
    // start boot animation
    property_set("service.bootanim.exit", "0");
    property_set("ctl.start", "bootanim");
}

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

代码语言:javascript复制
setprop service.bootanim.exit 0
setprop ctl.start bootanim

开机动画运行过程

代码位置

frameworks/base/cmds/bootanimation

代码语言:javascript复制
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、初始化

代码语言:javascript复制
status_t BootAnimation::readyToRun() {
  • 初始化显示功能
  • 确定bootanimation.zip文件位置,此处客制化较多。部分代码如下:
代码语言:javascript复制
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、播放线程

代码语言:javascript复制
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中定义

代码语言:javascript复制
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是开机动画中定义的结构体,是对开机动画文件解析后数据存储对象。
代码语言:javascript复制
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
  • 录入图片数据
代码语言:javascript复制
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);
 }
  • 播放
代码语言:javascript复制
	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();

退出设置

代码语言:javascript复制
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空间切换到用户空间,存在场景切换,会黑屏。亮屏条件是播放器检测到有帧数据送入,因此前面的帧数据会被遮住(遮住的帧数,因芯片解决方案不同而不同)。 解决方案: 确定具体平台会被隐藏的帧数,开始播放时,重复送入第一帧数据
代码语言:javascript复制
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 编辑,无法播放(和配置可能相关)。

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

0 人点赞