Android 开机动画流程介绍

前言

简单介绍了安卓开机动画流程

流程

【开机动画启动流程】:

// Bootanim.rc (frameworks\base\cmds\bootanimation)
    service bootanim /system/bin/bootanimation
        class core
        user graphics
        group graphics audio
        disabled
        oneshot

在 init 程序中注册了这样一个服务,那它在哪启动的?
答案见 老罗UI相关/Android系统的开机动画显示 
    在是 SurfaceFlinger 初始化时启动了,通过如下命令:
    SurfaceFlinger::readyToRun()
    {
        
        property_set("ctl.start",bootanim);
        
    }
    
    上面的以 ctl 开头的是一个控制型属性,会让 init.cpp 启动 bootanim 服务。 

// Z:\work\E262L_WW_0911_eng\frameworks\base\cmds\bootanimation\Android.mk  
    LOCAL_SRC_FILES:= \
                    bootanimation_main.cpp \
                    AudioPlayer.cpp \
                    BootAnimation.cpp
    LOCAL_MODULE:= bootanimation    
    
    
    ////////////////////////////////////////////////////////////////////////////
    //Bootanimation_main.cpp (frameworks\base\cmds\bootanimation)
    int main(int argc, char** argv)
    {
        // 首先检查系统属性“debug.sf.nobootnimaition”的值是否不等于0。
        //      如果不等于的话,那么接下来就会启动一个Binder线程池,并且创建一个BootAnimation对象
        // 这个BootAnimation对象就是用来显示第三个开机画面的。
        //      由于BootAnimation对象在显示第三个开机画面的过程中,需要与SurfaceFlinger服务通信
        //      因此,应用程序bootanimation就需要启动一个Binder线程池
        property_get("debug.sf.nobootanimation", value, "0");
        int noBootAnimation = atoi(value);
        if (!noBootAnimation) {
            sp<ProcessState> proc(ProcessState::self());
            ProcessState::self()->startThreadPool();
           
            // BootAnimation类间接地继承了RefBase类,并且重写了RefBase类的成员函数onFirstRef,
            // 因此,当一个BootAnimation对象第一次被智能指针引用的时,这个BootAnimation对象的成员函数onFirstRef就会被调用
            sp<BootAnimation> boot = new BootAnimation(setBoot,sePaly,setRotated);
                    /////////////////////////////////////////////////////////////////////////////////
                    // BootAnimation.cpp (frameworks\base\cmds\bootanimation)
                    BootAnimation::onFirstRef() 
                            // 由于BootAnimation类引用了SurfaceFlinger服务,因此,当SurfaceFlinger服务意外死亡时,BootAnimation类就需要得到通知
                            // 通过调用成员变量mSession的成员函数linkToComposerDeath来注册SurfaceFlinger服务的死亡接收通知来实现的
                            status_t err = mSession->linkToComposerDeath(this);
                            
                            // BootAnimation类继承了Thread类,因此,当BootAnimation类的成员函数onFirstRef调用了父类Thread的成员函数run之后
                            // 系统就会创建一个线程,这个线程在第一次运行之前,会调用BootAnimation类的成员函数readyToRun来执行一些初始化工作
                            // 后面再调用BootAnimation类的成员函数htreadLoop来显示第三个开机画面
                            run("BootAnimation", PRIORITY_DISPLAY);
                                    //////////////////////////////////////////////////////////////
                                    // 首先会进行一些初始化操作:
                                    //      获取相关动画资源 
                                    //      申请一些 Surface 资源进行显示
                                    BootAnimation::readyToRun()
                                            // 获得开关机动画 .zip 文件,相关资源路径为:
                                            //      char mResourcePath_gb[PATH_COUNT_USP][PROPERTY_VALUE_MAX] =
                                            //                            { 
                                            //                                "/custom/media/",             //  0  
                                            //                                "/system/media/"               //  1  
                                            //                            };
                                            //  或者:
                                            //      static const char* mResourcePath[MNC_COUNT][PATH_COUNT] =
                                            //          {{"/system/media/bootanimation1.zip", "/custom/media/bootanimation1.zip", "/data/local/bootanimation1.zip"},    //  0  
                                            //           {"/system/media/bootanimation2.zip", "/custom/media/bootanimation2.zip", "/data/local/bootanimation2.zip"}     //  1  
                                            //          };
                                            if (bBootOrShutDown) {
                                                initBootanimationZip();
                                            } else {
                                                initShutanimationZip();
                                            }
                                            
                                            // 获得开关机动车里面的 desc.txt 文件
                                            ZipEntryRO desc = mZip->findEntryByName("desc.txt");
                                            mZip->getEntryInfo(desc, &method, NULL, NULL, NULL, NULL, NULL);
                                            
                                            // 进行 Surface 资源获得
                                            // 进行 EGL 相关操作
                                            
                                            
                                            
                                    //////////////////////////////////////////////////////////////
                                    // 再进行动画显示
                                    BootAnimation::threadLoop()
                                            // 首先进行开机声音播放
                                            if (resourcePath != NULL) {
                                                bPlayMP3 = true;
                                                ALOGD("sound file path: %s", resourcePath);
                                                mediaplayer = new MediaPlayer();
                                                mediastatus = mediaplayer->setDataSource(NULL, resourcePath, NULL);

                                                sp<BootVideoListener> listener = new BootVideoListener(this);
                                                mediaplayer->setListener(listener);

                                                if (mediastatus == NO_ERROR) {
                                                    ALOGD("mediaplayer is initialized");
                                                    Parcel* attributes = new Parcel();
                                                    attributes->writeInt32(AUDIO_USAGE_MEDIA);            //usage
                                                    attributes->writeInt32(AUDIO_CONTENT_TYPE_MUSIC);     //audio_content_type_t
                                                    attributes->writeInt32(AUDIO_SOURCE_DEFAULT);         //audio_source_t
                                                    attributes->writeInt32(0);                            //audio_flags_mask_t
                                                    attributes->writeInt32(1);                            //kAudioAttributesMarshallTagFlattenTags of mediaplayerservice.cpp
                                                    attributes->writeString16(String16("BootAnimationAudioTrack")); // tags
                                                    mediaplayer->setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, *attributes);
                                                    mediaplayer->setAudioStreamType(AUDIO_STREAM_MUSIC);
                                                    mediastatus = mediaplayer->prepare();
                                                }
                                                if (mediastatus == NO_ERROR) {
                                                    ALOGD("media player is prepared");
                                                    mediastatus = mediaplayer->start();
                                                }

                                            }else{
                                                bPlayMP3 = false;
                                            }
                                            
                                            // 如果没有客制化开机动画,就使用 android() 显示默认开机动画 
                                            if ((mZip == NULL)&&(mZipFileName.isEmpty())) {
                                                r = android();
                                                        // 就是一个安卓字然后亮边滚动
                                                        // 原理如下:
                                                        //         Android 系统默认的开机动画是由两张图片 android-logo-mask.png 和 android-logo-shine.png中。
                                                        //     这两张图片保存在 frameworks/base/core/res/assets/images 目录中,它们最终会被编译在 framework-res 
                                                        //     模块(frameworks/base/core/res)中,即编译在framework-res.apk文件中。编译在 framework-res 模块中的
                                                        //     资源文件可以通过 AssetManager 类来访问。
                                                        //         
                                                        //         BootAnimation 类的成员函数 android 首先调用另外一个成员函数 initTexture 来将根据图片 android-logo-mask.png
                                                        //     和 android-logo-shine.png 的内容来分别创建两个纹理对象,这两个纹理对象就分别保存在 BootAnimation 类的成员变量 
                                                        //     mAndroid 所描述的一个数组中。通过混合渲染这两个纹理对象,我们就可以得到一个开机动画,这是通过中间的 while 循环语句来实现的。
                                                        // 
                                                        //         图片 android-logo-mask.png 用作动画前景,它是一个镂空的“ANDROID”图像。图片android-logo-shine.png用作动画背
                                                        //     景,它的中间包含有一个高亮的呈 45 度角的条纹。在每一次循环中,图片 android-logo-shine.png 被划分成左右两部分内容
                                                        //     来显示。左右两个部分的图像宽度随着时间的推移而此消彼长,这样就可以使得图片android-logo-shine.png中间高亮的条纹好
                                                        //     像在移动一样。另一方面,在每一次循环中,图片android-logo-shine.png都作为一个整体来渲染,而且它的位置是恒定不变的。
                                                        //     由于它是一个镂空的“ANDROID”图像,因此,我们就可以通过它的镂空来看到它背后的图片android-logo-shine.png的条纹一闪一闪地划过。
                                                        // 
                                                        //     这个while循环语句会一直被执行,直到应用程序/system/bin/bootanimation被结束为止
                                                        
                                                        
                                            } else {
                                            // 否则就使用 movie() 来显示客制化的开机动画
                                                if (!bETC1Movie) {
                                                    ALOGD("threadLoop() movie()");
                                                    r = movie();
                                                        // 显示用户自定义的动画:
                                                        相关原理如下:
                                                                从前面 BootAnimation 类的成员函数 readyToRun 的实现可以知道,如果目标设备上存在压缩文件 /data/local/bootanimation.zip,
                                                            那么BootAnimation类的成员变量mZip就会指向它,否则的话,就会指向目标设备上的压缩文件/system/media/bootanimation.zip。无论
                                                            BootAnimation类的成员变量mZip指向的是哪一个压缩文件,这个压缩文件都必须包含有一个名称为“desc.txt”的文件,用来描述用户自
                                                            定义的开机动画是如何显示的。
	
                                                            文件desc.txt的内容格式如下面的例子所示:
                                                                600 480 24  
                                                                p   1   0   part1  
                                                                p   0   10  part2  
                                                                
                                                                第一行的三个数字分别表示开机动画在屏幕中的显示宽度、高度以及帧速(fps)。剩余的每一行都用来描述一个动画片断,
                                                                这些行必须要以字符“p”来开头,后面紧跟着两个数字以及一个文件目录路径名称。第一个数字表示一个片断的循环显示次数,
                                                                如果它的值等于0,那么就表示无限循环地显示该动画片断。第二个数字表示每一个片断在两次循环显示之间的时间间隔。这个时
                                                                间间隔是以一个帧的时间为单位的。文件目录下面保存的是一系列png文件,这些png文件会被依次显示在屏幕中。
       
                                                            以上面这个desct.txt文件的内容为例:                                                            
                                                                它描述了一个大小为600 x 480的开机动画,动画的显示速度为24帧每秒。
                                                                这个开机动画包含有两个片断 part1 和 part2。
                                                                    片断part1只显示一次,它对应的png图片保存在目录part1中。
                                                                    片断part2无限循环地显示,其中,每两次循环显示的时间间隔为10 x (1 / 24)秒,它对应的png图片保存在目录part2中。

                                                            上面的for循环语句分析完成desc.txt文件的内容后,就得到了开机动画的显示大小、速度以及片断信息。这些信息都保存在Animation
                                                            对象animation中,其中,每一个动画片断都使用一个Animation::Part对象来描述,并且保存在Animation对象animation的成员变量parts所
                                                            描述的一个片断列表中。
                                                            
                                                            
                                                                接下来,BootAnimation类的成员函数movie再断续将每一个片断所对应的png图片读取出来,每一个png图片都表示一个动画帧,使用一个
                                                            Animation::Frame对象来描述,并且保存在对应的Animation::Part对象的成员变量frames所描述的一个帧列表中。
                                                                获得了开机动画的所有信息之后,接下来BootAnimation类的成员函数movie就准备开始显示开机动画了
                                                                前面的一系列gl函数首先用来清理屏幕,接下来的一系列gl函数用来设置OpenGL的纹理显示方式。
                                                                变量 xc 和 yc 的值用来描述开机动画的显示位置,即需要在屏幕中间显示开机动画,另外一个变量frameDuration的值
                                                                    用来描述每一帧的显示时间,它是以纳秒为单位的。

                                                                Region对象clearReg用来描述屏幕中除了开机动画之外的其它区域,它是用整个屏幕区域减去开机动画所点据的区域来得到的。

                                                                准备好开机动画的显示参数之后,最后就可以执行显示开机动画的操作了
                                                            
                                                            
                                                            
                                                } else {
                                                    ALOGD("threadLoop() ETC1movie()");
                                                    r = ETC1movie();
                                                }
                                            }
                                    
                                    
                    
            IPCThreadState::self()->joinThreadPool();
            
        }
        
    }

【开机动画的停止流程】:

        从前面Android系统默认Home应用程序(Launcher)的启动过程源代码分析一文可以知道,当System进程将系统中的关键服务启动起来之后,就会
    将应用程序启动器(Launcher)启动起来。从Android应用程序启动过程源代码分析一文又可以知道,Android应用程序的启动过程实际上就是它的根
    Activity组件的启动过程。对于应用程序Launcher来说,它的根Activity组件即为Launcher组件。

        一个Activity组件在启动起来之后,就会被记录起来,等到它所运行在的主线程空闲的时候,这个主线程就会向ActivityManagerService发送一
    个Activity组件空闲的通知。由于应用程序Launcher是系统中第一个被启动的应用程序,即它的根Activity组件是系统中第一个被启动的Activity组件,
    因此,当ActivityManagerService接收到它的空闲通知的时候,就可以知道系统是刚刚启动起来的。在这种情况下,ActivityManagerService就会停
    止显示开机动画,以便可以在屏幕中显示应用程序 Lancher 的界面。
        
    相关流程如下:具体参见<<老罗 UI相关/Android系统的开机动画显示>>
        ActivityManagerService 
            =>WindowManagerService
                =>SurfaceFlinger
                    frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp
                        property_set("ctl.stop", "bootanim");  

猜你喜欢

转载自blog.csdn.net/wangjun7121/article/details/88141376