• Android 7 修改启动动画和开机声音


    背景

    在修改开机音量的时候,发现找不到对应的声音功能调用。

    因此了解了一下安卓的开机声音是如何实现的。

    安卓4~安卓7 都可以这么做。

    参考:

    前言

    关于安卓的开机效果(动画、logo、声音),有下面几个阶段:

    • 老的方案1:Linux 系统启动,出现Linux小企鹅画面(reboot)(Android 1.5及以上版本已经取消加载图片);
    • 沿用至今的方案2:Lndroid平台图形系统启动,出现含闪动的ANDROID字样的动画图片(start)并加载Android系统。

    我们这里说的是沿用至今的方案2。

    源码分析

    android开机动画叫源码位于frameworks/base/cmds/bootanimation/BootAnimation.cpp中,会将/data/local/bootanimation.zip/system/media/bootanimation.zip里面的图片(支持png、jpeg、bmp)以动画的形式播放出来:

    在源码树中搜索bootanimation.zip即可找到可供修改的开机动画。

    static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
    static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip";
    static const char SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[] = "/system/media/bootanimation-encrypted.zip";
    
    status_t BootAnimation::readyToRun() {
        mAssets.addDefaultAssets();
    
        sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
                ISurfaceComposer::eDisplayIdMain));
        DisplayInfo dinfo;
        status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
        if (status)
            return -1;
    
        // create the native surface andrew.hu modify
        sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
                dinfo.orientation == 1 ? dinfo.h : dinfo.w,
                dinfo.orientation == 1 ? dinfo.w : dinfo.h, PIXEL_FORMAT_RGB_565);
    
        SurfaceComposerClient::openGlobalTransaction();
        control->setLayer(0x40000000);
        SurfaceComposerClient::closeGlobalTransaction();
    
        sp<Surface> s = control->getSurface();
    
        // initialize opengl and egl
        const EGLint attribs[] = {
                EGL_RED_SIZE,   8,
                EGL_GREEN_SIZE, 8,
                EGL_BLUE_SIZE,  8,
                EGL_DEPTH_SIZE, 0,
                EGL_NONE
        };
        EGLint w, h;
        EGLint numConfigs;
        EGLConfig config;
        EGLSurface surface;
        EGLContext context;
    
        EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    
        eglInitialize(display, 0, 0);
        eglChooseConfig(display, attribs, &config, 1, &numConfigs);
        surface = eglCreateWindowSurface(display, config, s.get(), NULL);
        context = eglCreateContext(display, config, NULL, NULL);
        eglQuerySurface(display, surface, EGL_WIDTH, &w);
        eglQuerySurface(display, surface, EGL_HEIGHT, &h);
    
        if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
            return NO_INIT;
    
        mDisplay = display;
        mContext = context;
        mSurface = surface;
        mWidth = w;
        mHeight = h;
        mFlingerSurfaceControl = control;
        mFlingerSurface = s;
    
        // If the device has encryption turned on or is in process
        // of being encrypted we show the encrypted boot animation.
        char decrypt[PROPERTY_VALUE_MAX];
        property_get("vold.decrypt", decrypt, "");
    
        bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);
    	// 读取 对应 储存了开机动画的包
        if (encryptedAnimation && (access(getAnimationFileName(IMG_ENC), R_OK) == 0)) {
            mZipFileName = getAnimationFileName(IMG_ENC);
        }
        else if (access(getAnimationFileName(IMG_OEM), R_OK) == 0) {
            mZipFileName = getAnimationFileName(IMG_OEM);
        }
        else if (access(getAnimationFileName(IMG_SYS), R_OK) == 0) {
            mZipFileName = getAnimationFileName(IMG_SYS);
        }
        return NO_ERROR;
    }
    

    循环:

    bool BootAnimation::threadLoop()
    {
        bool r;
        // We have no bootanimation file, so we use the stock android logo
        // animation.
        if (mZipFileName.isEmpty()) {
            r = android(); //   执行android字体闪动的图片 会加载"images/android-logo-mask.png"和"images/android-logo-shine.png"
        } else {
            r = movie();   //   执行bootanimation.zip中提供的动画图片 
        }
    
        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;
    }
    

    所以如果你想修改动画,那么把你做好的动画拷贝到编译好对应的目录下即可,然后编译刷入整合进system镜像包就可以看到效果了

    如果你想修改android闪动的那两张图片的话,最简单的方法是直接替换图片,如果你懂openGL的话也可以自己做酷炫的动画

    另外如果需要修改开机logo动画,可以这么做(参考BootAnimation.cpp中的android函数):

    1、将图片文件xx.png放置到 frameworks/base/core/res/assets/images中。

    2、再在函数中完成对应的调用:

    • 声明对应的类型:Texture mTexture
    • 加载initTexture(&mTexture, mAssets, "images/xx.png");
    • 使用openGL来绘制界面(略)

    添加开机声音

    既然我们已经知道了关于开机效果显示的大致流程。

    那么添加开机声音也很简单,我们可以使用MediaPlayer这个类来完成我们的需求。

    声明与实现

    首先在BootAnimation.h添加方法的声明和头文件的引用

    #include <media/AudioSystem.h>
    #include <media/mediaplayer.h>
    

    添加: void bootMusic();的声明以及对应的实现

    // frameworks/base/cmds/bootanimation/BootAnimation.h
    class BootAnimation : public Thread, public IBinder::DeathRecipient
    {
        //...
    private:
        void bootMusic();
    }
    
    
    // frameworks/base/cmds/bootanimation/BootAnimation.cpp
    void BootAnimation::bootMusic()
    {
        int index;
        const char *fileName = "/system/media/boot.wav";
        MediaPlayer* mp = new MediaPlayer();
        audio_devices_t device = AudioSystem::getDevicesForStream(AUDIO_STREAM_ENFORCED_AUDIBLE);
        if (mp->setDataSource(NULL, fileName, NULL) == NO_ERROR)
        {
            mp->setAudioStreamType(AUDIO_STREAM_ENFORCED_AUDIBLE/*AudioSystem: :ENFORCED_AUDIBLE*/);
            mp->prepare();
        }
        LOGE ("bootMusic
    ");
        AudioSystem::initStreamVolume(AUDIO_STREAM_ENFORCED_AUDIBLE, 0,7);
        AudioSystem::setStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE, 7, device);
        AudioSystem::getStreamVolumeIndex(AUDIO_STREAM_ENFORCED_AUDIBLE/*AudioSystem::ENFORCED_AUDIBLE*/, &index, device);
        LOGE ("index %d",index);
        if (index != 0)
        {
            LOGD("playing %s", fileName);
            mp->setVolume(0.4f, 0.4f);
            mp->seekTo(0);
            mp->start();
        }
    }
    

    只要fileName对应的声音文件存在而且有权限访问到,那么无所谓放在哪里。

    此后,在合适的地方(android ()或者movie ())调用即可,例如:

    bool BootAnimation::movie()
    {
        Animation* animation = loadAnimation(mZipFileName);
        // ...
        glEnable(GL_TEXTURE_2D);
        glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    	// ...
        
        bootMusic(); // 播放声音 (只要在播放动画之前播放即可)
        
        playAnimation(*animation); // 播放动画
    
        // ...
    
        return false;
    }
    

    Android.mk

    光有头文件还不够,播放声音还需要引入对应的库:

    # frameworks/base/cmds/bootanimation/Android.mk
    
    # ...
    
    LOCAL_SHARED_LIBRARIES := 
        libcutils 
        liblog 
        libandroidfw 
        libutils 
        libbinder 
        libui 
        libskia 
        libEGL 
        libGLESv1_CM 
        libgui 
        libOpenSLES 
        libtinyalsa 
        libregionalization 
        libmedia # 注意,libmedia是新添加的;
        
    # ...
    

    大功告成,这样就成功的添加了开机音乐。

    修改铃声

    也许有人会问,那android系统自带的那些音乐和铃声在什么地方呢?

    系统默认了很多的声音,如果有需要,可以修改frameworks/base/data/sounds下面文档及文档夹中的声音文档。

    至于编译完成后放到什么分区那是由AllAudio.mk决定的。

    AllAudio.mk的作用:是将这些音乐文档全部打包到系统system/media/audio下面各个模块的文档,然后在系统开机的时候,扫描这些文档,将其加入到数据库中,之后在设置中更换声音时,则直接从数据库中查询这些音乐文档,然后供用户选择。

    如果需要添加某个铃声,可以这么做:

    1、在AllAudio.mk添加一列类似这样的内容:$(LOCAL_PATH)/aaa.ogg:system/media/audio/bbb/xxx.ogg

    2、再将aaa.ogg拷贝到frameworks/base/data/sounds

    修改也是类似的原理。

    当然,也可以通过修改mk文件中指定的音乐文档名来实现静音等目的。

    例如,ro.config.notification_sound(通知默认的音乐文档文档名)在build/target/product/full_base.mk 中定义,如果我们不想有声音那么我们可以将默认值改为不存在的文档,则不会播放通知声音了。当然我们也可以在客户定义的mk中使用PRODUCT_PROPERTY_OVERRIDES 去复写此属性,将其指定为不存在文档或者为空,这样就不会有通知声音响了。

    修改开机动画

    这里介绍bootanimation.zip如何制作。

    开机动画包内容

    先看看bootanimation.zip的内容:解压.zip文件后,会有n个存放图片的文件夹(partn)+1个desc.txt文件。

    开机动画压缩包不能包含bootanimation文件夹,必现是如下格式

    ├── desc.txt
    ├── part0
    │   ├── 00013.png
    │   ├── 00014.png
    │   ├── 00052.png
    └── part1
        ├── 00013.png
        ├── 00015.png
        ├── 00019.png
        └── 00069.png
    

    上图中的文件夹名字可随便命名(对应desc.txt即可),里面存放的就是开机要显示的图片。

    desc.txt解析

    打开以后可以看到是类似这样的内容。

    240 320 1
    p 2 0 part0
    p 0 0 part1
    
    

    第一行代表帧率以及动画大小:

    240 320 1
    每秒1帧

    此后的每一行代表每一段动画具体的效果:

    p 2 0 part0
    代表一段动画 代表文件夹播放的次数,0代表永远循环 表示这部分播完后下部分开始的间隔 代表从哪个文件夹中寻找动画

    一般来说,将最后的那段动画设置成循环播放,就可以在进入系统界面之前一直有画面,不会出现黑屏的情况(所有动画都做完了,但是还没进入到系统界面就会出现黑屏)。

    最后一行是空行,代表文件结束;很多人在改动时候会把这一行删掉,如果制作以后失败了,那么可以将这一行加上去试试。

    图片替换

    图片需要统一格式为png,且各图片的大小需要相同。

    图片的命名可以随意,但是最后的数字要有顺序。

    zip制作

    开机动画压缩格式必须为存储方式。

    在Windows中可以在压缩时,指定压缩级别为不压缩/存档。由于Windows下压缩软件良莠不齐,因此建议在Linux中 制作。

    Linux下可以使用下列命令:

    cd bootanimation
    zip -0 -r ../bootanimation.zip ./*
    
    如果说我的文章对你有用,只不过是我站在巨人的肩膀上再继续努力罢了。
    若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
    博客地址:https://www.cnblogs.com/schips/
  • 相关阅读:
    包 (package)
    Object类
    异常
    接口
    抽象类
    多态(经典案例)
    三大特性:(经典代码)
    对象创建的过程(重点理解)
    final关键字
    cocos2dx工程中接入支付宝sdk
  • 原文地址:https://www.cnblogs.com/schips/p/change_bootanimation_and_add_boot_music_support_in_android.html
Copyright © 2020-2023  润新知