Android开机动画

Android开机动画

android12-release
推荐 Android 12 开机动画代码与流程详解


1、BootLoader开机图片

一般使用rle格式图片;例如放在splash分区

shell 复制代码
adb reboot bootloader
fastboot flash splash splash.img
fastboot reboot

2、Kernel开机图片

记录 kernel/drivers/video/msm/msm_fb.c 也是读出根目录下的xx.rle,并显示为开机画面

3、系统启动时(BootAnimation)动画

使用BootAnimation程序显示开机画面,如需修改开机画面,不用修改代码,只需按格式要求做bootanimation.zip包,放在系统的/system/media目录中,或/oem/media/product/media等目录。

代码路径:frameworks/base/cmds/bootanimation

3.1 bootanimation.zip位置

frameworks/base/cmds/bootanimation/BootAnimation.cpp,例如bootanimation.zip

3.2 bootanimation启动

frameworks/base/cmds/bootanimation/Android.bp

frameworks/base/cmds/bootanimation/bootanim.rc

frameworks/base/cmds/bootanimation/bootanimation_main.cpp

frameworks/base/cmds/bootanimation/BootAnimation.cpp

frameworks/base/cmds/bootanimation/BootAnimationUtil.cpp

SurfaceFlinger进程名:bootanim

bin文件:/system/bin/bootanimation

对应启动入口:/frameworks/base/cmds/bootanimation/bootanimation_main.cpp
bootAnimationDisabled()检测系统属性(debug.sf.nobootanimation、ro.boot.quiescent、ro.bootanim.quiescent.enabled),noBootAnimation 是否启动开机动画。注意init进程在启动bootanimation服务是disable的,不会主动将应用程序bootanimation启动起来。启动bootanimation是从surfaceFlinger这边来启动的

frameworks/base/cmds/bootanimation/bootanimation_main.cpp

cpp 复制代码
int main()
{
    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);

    bool noBootAnimation = bootAnimationDisabled();
    ALOGI_IF(noBootAnimation,  "boot animation disabled");
    if (!noBootAnimation) {

        sp<ProcessState> proc(ProcessState::self());
        ProcessState::self()->startThreadPool();

        // create the boot animation object (may take up to 200ms for 2MB zip)
        sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());

        waitForSurfaceFlinger();

        boot->run("BootAnimation", PRIORITY_DISPLAY);

        ALOGV("Boot animation set up. Joining pool.");

        IPCThreadState::self()->joinThreadPool();
    }
    return 0;
}

frameworks/base/cmds/bootanimation/BootAnimationUtil.cpp

cpp 复制代码
bool bootAnimationDisabled() {
    char value[PROPERTY_VALUE_MAX];
    property_get("debug.sf.nobootanimation", value, "0");
    if (atoi(value) > 0) {
        return true;
    }

    property_get("ro.boot.quiescent", value, "0");
    if (atoi(value) > 0) {
        // Only show the bootanimation for quiescent boots if this system property is set to enabled
        if (!property_get_bool("ro.bootanim.quiescent.enabled", false)) {
            return true;
        }
    }

    return false;
}

3.3 SurfaceFlinger启动bootanimation

SurfaceFlinger启动-Android12 初始化时候 mStartPropertySetThread->Start() 在线程中设置property_set("ctl.start", "bootanim")启动开机动画

/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

frameworks/native/services/surfaceflinger/StartPropertySetThread.cpp

cpp 复制代码
#include <cutils/properties.h>
#include "StartPropertySetThread.h"

namespace android {

StartPropertySetThread::StartPropertySetThread(bool timestampPropertyValue):
        Thread(false), mTimestampPropertyValue(timestampPropertyValue) {}

status_t StartPropertySetThread::Start() {
    return run("SurfaceFlinger::StartPropertySetThread", PRIORITY_NORMAL);
}

bool StartPropertySetThread::threadLoop() {
    // Set property service.sf.present_timestamp, consumer need check its readiness
    property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0");
    // Clear BootAnimation exit flag
    property_set("service.bootanim.exit", "0");
    property_set("service.bootanim.progress", "0");
    // Start BootAnimation if not started
    property_set("ctl.start", "bootanim");
    // Exit immediately
    return false;
}

} // namespace android

3.4 播放开机动画playAnimation

  • result = android()android原生动画;result = movie()自动动画
  • playAnimation(*mAnimation)播放动画;releaseAnimation(mAnimation)释放动画资源
  • 播放动画过程:initTexture(frame.map, &w, &h)drawClock(animation.clockFont, part.clockPosX, part.clockPosY)drawProgress(lastDisplayedProgress, animation.progressFont, posX, posY)checkExit()
  • checkExit()检测属性"service.bootanim.exit"退出动画
cpp 复制代码
bool BootAnimation::threadLoop() {
    bool result;
    // We have no bootanimation file, so we use the stock android logo
    // animation.
    if (mZipFileName.isEmpty()) {
        result = android();
    } else {
        result = movie();
    }

    mCallbacks->shutdown();
    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroyContext(mDisplay, mContext);
    eglDestroySurface(mDisplay, mSurface);
    mFlingerSurface.clear();
    mFlingerSurfaceControl.clear();
    eglTerminate(mDisplay);
    eglReleaseThread();
    IPCThreadState::self()->stopProcess();
    return result;
}
cpp 复制代码
bool BootAnimation::movie() {
    if (mAnimation == nullptr) {
        mAnimation = loadAnimation(mZipFileName);
    }

    if (mAnimation == nullptr)
        return false;

    // mCallbacks->init() may get called recursively,
    // this loop is needed to get the same results
    for (const Animation::Part& part : mAnimation->parts) {
        if (part.animation != nullptr) {
            mCallbacks->init(part.animation->parts);
        }
    }
    mCallbacks->init(mAnimation->parts);

    bool anyPartHasClock = false;
    for (size_t i=0; i < mAnimation->parts.size(); i++) {
        if(validClock(mAnimation->parts[i])) {
            anyPartHasClock = true;
            break;
        }
    }
    if (!anyPartHasClock) {
        mClockEnabled = false;
    }

    // Check if npot textures are supported
    mUseNpotTextures = false;
    String8 gl_extensions;
    const char* exts = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
    if (!exts) {
        glGetError();
    } else {
        gl_extensions.setTo(exts);
        if ((gl_extensions.find("GL_ARB_texture_non_power_of_two") != -1) ||
            (gl_extensions.find("GL_OES_texture_npot") != -1)) {
            mUseNpotTextures = true;
        }
    }

    // Blend required to draw time on top of animation frames.
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glShadeModel(GL_FLAT);
    glDisable(GL_DITHER);
    glDisable(GL_SCISSOR_TEST);
    glDisable(GL_BLEND);

    glBindTexture(GL_TEXTURE_2D, 0);
    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);

    bool clockFontInitialized = false;
    if (mClockEnabled) {
        clockFontInitialized =
            (initFont(&mAnimation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR);
        mClockEnabled = clockFontInitialized;
    }

    initFont(&mAnimation->progressFont, PROGRESS_FONT_ASSET);

    if (mClockEnabled && !updateIsTimeAccurate()) {
        mTimeCheckThread = new TimeCheckThread(this);
        mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL);
    }

    playAnimation(*mAnimation);

    if (mTimeCheckThread != nullptr) {
        mTimeCheckThread->requestExit();
        mTimeCheckThread = nullptr;
    }

    if (clockFontInitialized) {
        glDeleteTextures(1, &mAnimation->clockFont.texture.name);
    }

    releaseAnimation(mAnimation);
    mAnimation = nullptr;

    return false;
}
cpp 复制代码
bool BootAnimation::playAnimation(const Animation& animation) {
    const size_t pcount = animation.parts.size();
    nsecs_t frameDuration = s2ns(1) / animation.fps;

    SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
            elapsedRealtime());

    int fadedFramesCount = 0;
    int lastDisplayedProgress = 0;
    for (size_t i=0 ; i<pcount ; i++) {
        const Animation::Part& part(animation.parts[i]);
        const size_t fcount = part.frames.size();
        glBindTexture(GL_TEXTURE_2D, 0);

        // Handle animation package
        if (part.animation != nullptr) {
            playAnimation(*part.animation);
            if (exitPending())
                break;
            continue; //to next part
        }

        // process the part not only while the count allows but also if already fading
        for (int r=0 ; !part.count || r<part.count || fadedFramesCount > 0 ; r++) {
            if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break;

            mCallbacks->playPart(i, part, r);

            glClearColor(
                    part.backgroundColor[0],
                    part.backgroundColor[1],
                    part.backgroundColor[2],
                    1.0f);

            // For the last animation, if we have progress indicator from
            // the system, display it.
            int currentProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0);
            bool displayProgress = animation.progressEnabled &&
                (i == (pcount -1)) && currentProgress != 0;

            for (size_t j=0 ; j<fcount ; j++) {
                if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break;

                processDisplayEvents();

                const int animationX = (mWidth - animation.width) / 2;
                const int animationY = (mHeight - animation.height) / 2;

                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);
                    }
                    int w, h;
                    initTexture(frame.map, &w, &h);
                }

                const int xc = animationX + frame.trimX;
                const int yc = animationY + frame.trimY;
                Region clearReg(Rect(mWidth, mHeight));
                clearReg.subtractSelf(Rect(xc, yc, xc+frame.trimWidth, yc+frame.trimHeight));
                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 - frame.trimHeight) / 2)
                // which is equivalent to mHeight - (yc + frame.trimHeight)
                const int frameDrawY = mHeight - (yc + frame.trimHeight);
                glDrawTexiOES(xc, frameDrawY, 0, frame.trimWidth, frame.trimHeight);

                // if the part hasn't been stopped yet then continue fading if necessary
                if (exitPending() && part.hasFadingPhase()) {
                    fadeFrame(xc, frameDrawY, frame.trimWidth, frame.trimHeight, part,
                              ++fadedFramesCount);
                    if (fadedFramesCount >= part.framesToFadeCount) {
                        fadedFramesCount = MAX_FADED_FRAMES_COUNT; // no more fading
                    }
                }

                if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
                    drawClock(animation.clockFont, part.clockPosX, part.clockPosY);
                }

                if (displayProgress) {
                    int newProgress = android::base::GetIntProperty(PROGRESS_PROP_NAME, 0);
                    // In case the new progress jumped suddenly, still show an
                    // increment of 1.
                    if (lastDisplayedProgress != 100) {
                      // Artificially sleep 1/10th a second to slow down the animation.
                      usleep(100000);
                      if (lastDisplayedProgress < newProgress) {
                        lastDisplayedProgress++;
                      }
                    }
                    // Put the progress percentage right below the animation.
                    int posY = animation.height / 3;
                    int posX = TEXT_CENTER_VALUE;
                    drawProgress(lastDisplayedProgress, animation.progressFont, posX, posY);
                }

                handleViewport(frameDuration);

                eglSwapBuffers(mDisplay, mSurface);

                nsecs_t now = systemTime();
                nsecs_t delay = frameDuration - (now - lastFrame);
                //SLOGD("%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, nullptr);
                    } while (err<0 && errno == EINTR);
                }

                checkExit();
            }

            usleep(part.pause * ns2us(frameDuration));

            if (exitPending() && !part.count && mCurrentInset >= mTargetInset &&
                !part.hasFadingPhase()) {
                if (lastDisplayedProgress != 0 && lastDisplayedProgress != 100) {
                    android::base::SetProperty(PROGRESS_PROP_NAME, "100");
                    continue;
                }
                break; // exit the infinite non-fading part when it has been played at least once
            }
        }
    }

    // Free textures created for looping parts now that the animation is done.
    for (const Animation::Part& part : animation.parts) {
        if (part.count != 1) {
            const size_t fcount = part.frames.size();
            for (size_t j = 0; j < fcount; j++) {
                const Animation::Frame& frame(part.frames[j]);
                glDeleteTextures(1, &frame.tid);
            }
        }
    }

    return true;
}

3.6 开机动画退出检测

  • checkExit()检测属性"service.bootanim.exit"退出动画;在playAnimation方法和android方法中都有一个checkExit方法来负责检查是否退出动画
  • WMS中performEnableScreen()设置SystemProperties.set("service.bootanim.exit", "1")
cpp 复制代码
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 (exitnow) {
        requestExit();
    }
}
java 复制代码
private void performEnableScreen() {
    synchronized (mGlobalLock) {
        ProtoLog.i(WM_DEBUG_BOOT, "performEnableScreen: mDisplayEnabled=%b"
                        + " mForceDisplayEnabled=%b" + " mShowingBootMessages=%b"
                        + " mSystemBooted=%b mOnlyCore=%b. %s", mDisplayEnabled,
                mForceDisplayEnabled, mShowingBootMessages, mSystemBooted, mOnlyCore,
                new RuntimeException("here").fillInStackTrace());
        if (mDisplayEnabled) {
            return;
        }
        if (!mSystemBooted && !mShowingBootMessages) {
            return;
        }

        if (!mShowingBootMessages && !mPolicy.canDismissBootAnimation()) {
            return;
        }

        // Don't enable the screen until all existing windows have been drawn.
        if (!mForceDisplayEnabled) {
            if (mBootWaitForWindowsStartTime < 0) {
                // First time we will start waiting for all windows to be drawn.
                mBootWaitForWindowsStartTime = SystemClock.elapsedRealtime();
            }
            for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
                if (mRoot.getChildAt(i).shouldWaitForSystemDecorWindowsOnBoot()) {
                    return;
                }
            }
            long waitTime = SystemClock.elapsedRealtime() - mBootWaitForWindowsStartTime;
            mBootWaitForWindowsStartTime = -1;
            if (waitTime > 10) {
                ProtoLog.i(WM_DEBUG_BOOT,
                        "performEnableScreen: Waited %dms for all windows to be drawn",
                        waitTime);
            }
        }

        if (!mBootAnimationStopped) {
            Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
            // stop boot animation
            // formerly we would just kill the process, but we now ask it to exit so it
            // can choose where to stop the animation.
            SystemProperties.set("service.bootanim.exit", "1");
            mBootAnimationStopped = true;
        }

        if (!mForceDisplayEnabled && !checkBootAnimationCompleteLocked()) {
            ProtoLog.i(WM_DEBUG_BOOT, "performEnableScreen: Waiting for anim complete");
            return;
        }

        try {
            IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
            if (surfaceFlinger != null) {
                ProtoLog.i(WM_ERROR, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
                Parcel data = Parcel.obtain();
                data.writeInterfaceToken("android.ui.ISurfaceComposer");
                surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
                        data, null, 0);
                data.recycle();
            }
        } catch (RemoteException ex) {
            ProtoLog.e(WM_ERROR, "Boot completed: SurfaceFlinger is dead!");
        }

        EventLogTags.writeWmBootAnimationDone(SystemClock.uptimeMillis());
        Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
        mDisplayEnabled = true;
        ProtoLog.i(WM_DEBUG_SCREEN_ON, "******************** ENABLING SCREEN!");

        // Enable input dispatch.
        mInputManagerCallback.setEventDispatchingLw(mEventDispatchingEnabled);
    }

    try {
        mActivityManager.bootAnimationComplete();
    } catch (RemoteException e) {
    }

    mPolicy.enableScreenAfterBoot();

    // Make sure the last requested orientation has been applied.
    updateRotationUnchecked(false, false);
}

3.7 简易时序图

4、bootanimation.zip文件

bootanimation.zip

bootanimation.zip\desc.txt:

xml 复制代码
1080 2400 5
p 0 5 part0

bootanimation.zip\part0:

相关推荐
Estar.Lee1 小时前
查手机号归属地免费API接口教程
android·网络·后端·网络协议·tcp/ip·oneapi
温辉_xh1 小时前
uiautomator案例
android
工业甲酰苯胺2 小时前
MySQL 主从复制之多线程复制
android·mysql·adb
少说多做3433 小时前
Android 不同情况下使用 runOnUiThread
android·java
Estar.Lee4 小时前
时间操作[计算时间差]免费API接口教程
android·网络·后端·网络协议·tcp/ip
找藉口是失败者的习惯5 小时前
从传统到未来:Android XML布局 与 Jetpack Compose的全面对比
android·xml
Jinkey6 小时前
FlutterBasic - GetBuilder、Obx、GetX<Controller>、GetxController 有啥区别
android·flutter·ios
大白要努力!7 小时前
Android opencv使用Core.hconcat 进行图像拼接
android·opencv
天空中的野鸟8 小时前
Android音频采集
android·音视频
小白也想学C9 小时前
Android 功耗分析(底层篇)
android·功耗