android 开机画面是由应用程序bootanimation来负责显示的,主要类BootAnimation.cpp直接继承于android中定义的一个Thread, 接地继承了RefBase类,并且重写了RefBase类的成员函数onFirstRef,因此,当一个BootAnimation对象第一次被引用的时,这个BootAnimation对象的成员函数onFirstRef就会被调用。     当应用程序启动时,是先从main开始,具体代码如下:         sp boot = new BootAnimation();         //add video boot         memset(value,0,sizeof(value));         property_get("service.bootvideo", value, "0");         gUseBootVideo = (atoi(value) == 1 ? true : false);
        if(gUseBootVideo){            // boot->setBootVolume();            /* modify,bootanimation path */            /*before modify code(property_set("service.bootvideo", "2");)*/            if (access(USER_BOOTVIDEO_FILE, R_OK) == 0) {                property_set("service.bootvideo", "2");            }else{                gUseBootVideo = false;            }            /*modify end */         } 如果access(USER_BOOTVIDEO_FILE, R_OK) == 0),即bootvideo文件存在则会执行property_set("service.bootvideo", "2"),则启动系统内的视频播放应用程序LibPlayer播放开机视频,同时gUseBootVideo =true代表bootanimation播放的是开机视频。否则播放开机动画。    所以当开机视频文件bootvideo存在时,应用程序bootanimation会做2件事情:        一:执行到sp boot = new BootAnimation();时会调用BootAnimation对象的成员函数onFirstRe,在onFirstRe里会执行线程的run方法,然后会调用动画的android或movie方法来显示动画(既然是为了播放开机视频,开机动画就没必要播放,后续在播放动画的方法中必须跳过GLES的绘图逻辑以免出现动画和视频同时出现的BUG)。       二:执行 到property_set("service.bootvideo", "2");时,则启动系统内的视频播放应用程序LibPlayer播放开机视频。

      之前说到BootAnimation对象的成员函数onFirstRe执行后会经过一系列的调用最终执行到播放开机动画,具体流程如下: 1.在onFirstRe中调用 run("BootAnimation", PRIORITY_DISPLAY);创建线程 2.这个线程在第一次运行之前,会调用BootAnimation类的成员函数readyToRun来执行一些初始化工作,这里我们需要注意mAndroidAnimation这个bool,它是来决定后面的动画走的是原生android还是我们自定义的movie 3.调用BootAnimation类的成员函数htreadLoop来显示第开机动画,这里我们走的是movie。  
  1. bool BootAnimation::threadLoop()  
  2. {  
  3.     bool r;  
  4.     if (mAndroidAnimation) {  
  5.         r = android();  
  6.     } else {  
  7.         r = movie();  
  8.     }  
  9.    .............
  10.   return r;
  11. }  
4.调用movie来解压bootanimation.zip,并通过解析 desc.txt 显示里面一张张的图片。 desc.txt  是 用来描述用户自定义的开机动画是如何显示的,格式如下:      
  1. 600 480 24  
  2. p   1   0   part1  
  3. p   0   10  part2 

具体的书写格式j及含义我就不多说了自行谷歌,但是需要大家注意的一点就是无论是什么样的 desc.txt  它的最后一行应该基本都是
  1. p   0   xx  xxxx 
第二个参数0代表的就是part文件夹中的图片是无限循环显示的,我们看到的情况就是屏幕上显示了一张静态的图片。为什么要无限循环显示?     答案就是 bootanimation在等一个消息,它在等launcher是否已经准备好了,如果launcher准备好了,那么它就停止循环,让launcher显示出来,否则就一直循环播放,直到launcher准备好。而launcher准备好的消息最终是由 SurfaceFlinger::bootFinished()  中的一个prop属性来发出的,而动画在循环播放中会不停的通过BootAnimation的checkExit来读取这个属性值,如果为真则调用其父类 Thread的函数requestExit退出线程,并且break退出for循环,最后返回false结束播放。      -----------------------------------------------------分割线--------针对开机动画卡在logo-----------------------------------------------------------------------     movie中我们只关心动画的能否正常显示(至于其他的绘图呀什么的太难了,表示不懂),以及动画能否正常退出。好了,假如是执行的是原生的android源码并且 bootanimation.zip是正确的,那么一切都好说,显示肯定是没有问题的。但是,万能的ROM厂商不答应啊,开机动画太单调了,还是换个广告植入下吧~于是动不动就推送一个bootanimation.zip给你,所以你每次开机就会看到不同的开机动画。于是问题就来了,广告发的太多万一哪天不小心推送了一个错误的bootanimation.zip给用户,则很有可能导致卡在开机动画那里了,从而会导致launcher起不来。所以客户提出的需求就是:不管bootanimation.zip对不对,你得让launcher起来,动画播不播就别管了。     下面分析下错误的bootanimation.zip的几种情况:     ①没有 desc.txt 文件,源码中已经进行了判断,直接返回     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;     }      ② desc.txt 文件中定义的图片包名和实际的不匹配(如 定义的是part1 ,但是实际的是part),而movie的for循环语句分析完成desc.txt文件的内容后,就得到了开机动画的显示大小、速度以及片断信息,这些信息都保存在Animation对象animation中,而后面的绘图就是根据这些信息来完成的。而错误的bootanimation.zip会导致 const   size_t  pcount = animation.parts.size() 及const size_t fcount = part.frames.size(); 均为0,而源码中并没有对这种情况进行判断所以会造成movie中的流程不正确导致屏幕一直卡在logo。     movie中最后有一个三层的for循环,主要工作就是根据之前拿到的animation来进行绘图工作:     当 pcount =0时,会直接跳过for循环,直接返回false     当 fcount =0时,无法进入第三个for循环,会在第二个for那里进行无限循环!
  由于我手上的代码并不是原生的android源码,而是项目code,所以关于画面显示的原理可能不太一样。我们的开机视频及开机动画是在Video层播放的,而launcher的显示是在OSD层,所以在视频和动画播放结束后需要将display_mode从Video层切换到OSD层,否则就算launcher正常启动了我们还是会看到屏幕一直卡在开机logo。     所以之前说的那2种出错的情况不能直接返回,而是需要按照正常流程那样在 SurfaceFlinger::bootFinished()  调用后进行一个setDisPlayMode操作,再返回。这样就可以避免屏幕一直卡在开机logo。     针对这些情况方法都是一样的:先调用用setDisPlayMode然后通过checkExit循环判断是否已接收到 bootFinished ,若接收到直接返回退出播放。例如:     int switch_logo_flag = 0;       while(!descMap&&!gUseBootVideo){        if(switch_logo_flag == 0){           property_set(RUNNING_PROP_NAME,"running");           switch_logo_flag=1;        }        checkExit();        if(exitPending()){           ALOGD("======tzr=====descMap is null ,return \n");           return false;        }       }

------------------------------------------------------------开机视频播放过程中出现黑屏------------------------------------------------------------------------       如果视频长度超过10s, 在播放开机视频的10后就会出现屏幕一直黑屏,直到launcher显示出来。      根据log及之前的开机动画分析可知,开机视频的流程与开机动画相比就是增加了用libPlayer播放指定文件夹中的视频文件,而当开机的准备工作完成后会执行 SurfaceFlinger::bootFinished()   会设置service.bootanim.exit=1来通知应用程序bootanimation可以停止播放动画了,然后setDisplayMode以便让launcher显示出来。但是如果播放的是长度较长的视频,在接到 SurfaceFlinger 的通知后不能停止播放,必须在播放结束后才能执行setDisplayMode。       而播放10s后的黑屏问题就是因为恰好执行了执行 SurfaceFlinger::bootFinished()   然后bootanimation立即调用了setDisplayMode。既然问题的原因已经查明,修改的方法很简单,就是当接收到service.bootanim.exit=1时,不要立即setDisplayMode,而是要等到接收到开机视频播放完成的消息后再去设置。 接收和设置的代码全部在函数checkExit中:  void BootAnimation::checkExit() {     // Allow surface flinger to gracefully request shutdown     char value[PROPERTY_VALUE_MAX];     property_get(EXIT_PROP_NAME, value, "0");     int exitnow = atoi(value);     if(gUseBootVideo){ // 下面这个while循环就是为了判断是否已经执行了SurfaceFlinger::bootFinished(),如果是则退出循环执行下面的代码,否则就一直循环等待         do{             usleep(10*1000);             memset(value,0,sizeof(value));             property_get(EXIT_PROP_NAME, value, "0");             exitnow = atoi(value);           }while(!exitnow);     }
    if (exitnow) {
        if(gUseBootVideo){ -               property_set("service.bootvideo.exit", "1"); / /这里是修改前的代码,通过这个属性设置会执行set_display_mode.sh             do{                 memset(value,0,sizeof(value));                 property_get("init.svc.bootvideo", value, "NULL"); / /这个属性init.svc.bootvideo=stopped代表视频播放结束了               }while(!strcmp(value,"running")); +               property_set("service.bootvideo.exit", "1");         }
        requestExit();     } }


这篇文章是之前很早写的基于android4.4版本,现在项目都已经基于android8.1了,代码架构也更改非常大,但是一些基础的地方还是相同的,还是发出来作为参考

更多相关文章

  1. Android 动画系列之自定义补间动画
  2. 【Android UI设计与开发】之动画(Animation)详解(一)
  3. Android 属性动画(Property Animation)介绍
  4. Android手势识别ViewFlipper触摸动画
  5. 基于RTP和Android的视频传输的研究实现方法
  6. Android CountDownTimer带有动画的倒计时
  7. Android取消RecyclerView、ListView、ScrollView、HorizontalScr
  8. android 原生的 MediaExtractor,MediaCodec 和 opengl 对视频进
  9. 图文详解Android属性动画

随机推荐

  1. Android(安卓)background tint颜色渲染
  2. android测试之getevent/sendevent
  3. Android源码下载——用git clone实现单个
  4. Android菜单简析02(ContextMenu)
  5. 详解高速神器python脚步打包android apk,
  6. Android软件广告屏蔽方法及代码
  7. C语言实现基于控制台的电子时钟
  8. Python3利用Qt5实现简易的五子棋游戏
  9. 5个数组函数实例演示
  10. CRM开源系统源码|客户管理系统PHP源码