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)


相关推荐

  • 2020年5月份编程语言排行榜「建议收藏」

    2020年5月份编程语言排行榜「建议收藏」前言本文章中语言排名数据来自TIOBE排行榜和PYPL排行榜。这段时间一直在忙,都忘记更新这个排行榜了,今天重操旧业,给大家看一下5月份的编程语言排行榜TIOBE排行榜5月份数据2020年5月TIOBE指数以下是官方说明五月标题:编程语言C又回到了第一位Java和C在4月份已经非常接近了,但是这个月C再次超越了Java。上一次C排名第一是在2015年。我们只能猜测为什么C又是第一名。其中一个原因可能是冠状病毒。这听起来可能很傻,但有些编程语言确实从这种情况中受益。数据科学领域的例子有P

  • 无人驾驶安全报告分析

    摘要随着经济的快速发展,各国汽车保有量急剧增加,促使城市路况更加严峻繁杂,城市交通正面临着前所未有的巨大压力。加之疲劳驾驶、酒后驾驶等人为因素,使世界各国的交通事故率逐年上升,甚至多于世界大战死亡人数。随着汽车技术、信息通信技术与智能控制技术的高效融合,集自动控制、人工智能、体系结构视觉设计等众多技术于一体的无人驾驶汽车应运而生。通过在车辆内安装智能操纵控制系统与感应设备来获取信息用以控制车…

  • 进程处于挂起状态表示_挂起进程转换图

    进程处于挂起状态表示_挂起进程转换图文章目录引言挂起状态是什么?挂起状态和阻塞状态有什么区别?如何主动挂起程序总结引言以前对于这个概念始终比较模糊,遂在解决后记录博客,希望帮助到有同样问题的朋友。挂起状态是什么?我们一般认为进程有五个状态,即新建态,就绪态,阻塞态,运行态,终止态。而在这些状态之外还存在着一个状态,我们称之为挂起状态,它既可以是我们客户主动使得进程挂起,也可以是操作系统因为某些原因使得进程挂起。总而言之引入挂起状态的原因有以下几种:用户的请求:可能是在程序运行期间发现了可疑的问题,需要暂停进程。父进程的请求:

    2022年10月29日
  • Android开发 屏幕适配之像素密度适配

    Android开发 屏幕适配之像素密度适配由于市场上采用Android系统的设备种类繁多,迫使Andriod开发人员不得不做烦人的适配工作。适配工作包括对安装不同Android版本的设备进行适配,对不同屏幕的设备进行适配等。而屏幕适配又包括:屏幕尺寸(small,normal,large,xlarge,这些在Android3.2以上版本开始不建议使用,转而使用最小屏幕宽度如sw600dp,最小宽度,最小高度等)屏幕

  • ps修图教程新手入门:如何用Photoshop处理证件照「建议收藏」

    ps修图教程新手入门:如何用Photoshop处理证件照「建议收藏」今天小编给大家讲解如何用Photoshop处理证件照,证件照是大家生活中经常要用到的,相信很多同学碰到过需要给背景照换颜色的时候,却不知道如何更换背景颜色。我们平时照的证件照,一般都是红底,这时我们遇到要蓝底的时候怎么办呢?下面讲解ps修图教程新手入门如何用Photoshop处理证件照。下面,以一寸照片为例,讲解如何用Photoshop制作证件照。1、电脑操作2、ps软件:AdobePhotoshop2017(演示)一、ps改变尺寸1、打开证件照原件(图片小编从网上下载了一张,并打码

  • anaconda prompt系统找不到指定的路径_tensorflow怎么用

    anaconda prompt系统找不到指定的路径_tensorflow怎么用我们默认打开jupyternotebook一般是在C盘,那么久而久之你很多的代码都新建在C盘,以后想要清理的时候难免会比较困难,所以在这里推荐大家通过命令在D盘指定目录下打开jupyternotebook,那么你新建的所有文件都是在D盘的指定目录下啦!…

发表回复

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

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