美文网首页
[016]BootAnimation引发的思考

[016]BootAnimation引发的思考

作者: 王小二的技术栈 | 来源:发表于2019-10-31 16:47 被阅读0次

前言

BootAnimation就是安卓系统的开机动画,估计网上面对BootAnimation的源码解读已经一大堆了,但是我想借BootAnimation分析以及和应用的对比来让读者好好理解一个应用的本质。

bootanimation在哪里

bootanimation的源码在frameworks/base/cmd/bootanimation目录下,是c++写的,最后编译成一个可执行程序bootanimation是在手机system/bin目录下。

对照应用:

我们编译出来的是一个APK的压缩包,一般是dex加一些资源,放在我们手机system目录或者data目录。

bootanimation的启动

bootanimation会在android开机启动的时候执行init.rc然后执行以下指令,然后就会找到bootanimation的可执行程序并运行。

service bootanim /system/bin/bootanimation
    class core animation
    user graphics
    group graphics audio
    disabled
    oneshot
    writepid /dev/stune/top-app/tasks

然后就会运行bootanimation_main.cpp中main方法,这是应该大家在刚开始学c语言或者java语言的hello world的时候都清楚,暂时先不用代码细节,我们后面慢慢分析。

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();

        waitForSurfaceFlinger();

        // create the boot animation object
        sp<BootAnimation> boot = new BootAnimation(new AudioAnimationCallbacks());
        ALOGV("Boot animation set up. Joining pool.");

        IPCThreadState::self()->joinThreadPool();
    }
    ALOGV("Boot animation exit");
    return 0;
}
对照应用:

其实一个应用的启动过程和上述有点类似,说白了就是Zygote进程加载应用的dex文件,然后执行ActivityThread.java的中main方法,有点长,有兴趣的可以仔细看看

    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

bootanimation的main方法

虽然main方法的代码不多,但是值得思考的问题有很多,以下是我对这边所有代码注解

int main()
{
    //设置进程的优先级
    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
    //判断是否disable BootAnimation
    bool noBootAnimation = bootAnimationDisabled();
    ALOGI_IF(noBootAnimation,  "boot animation disabled");
    if (!noBootAnimation) {
        //初始化Binder服务
        sp<ProcessState> proc(ProcessState::self());
        //启动Binder线程池
        ProcessState::self()->startThreadPool();
        //等待SurfaceFlinger服务结束
        waitForSurfaceFlinger();
        //创建开机动画的对象,其实BootAnimation这个对象会另外运行一个线程
        // create the boot animation object
        sp<BootAnimation> boot = new BootAnimation(new AudioAnimationCallbacks());
        ALOGV("Boot animation set up. Joining pool.");
        //将当前线程加入Binder线程池避免退出。
        IPCThreadState::self()->joinThreadPool();
    }
    ALOGV("Boot animation exit");
    return 0;
}
细节1:

sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
这两行代码,会初始化bootanimation的Binder服务,为什么要Binder服务,因为开机动画需要SurfaceFlinger的支持,SurfaceFlinger就是一个Binder服务。

对照应用:

应用除了需要SurfaceFlinger,还需要AMS,PMS,WMS等大量的Binder服务,所以也需要初始化Binder服务,但是我们发现ActivityThread.java的main方法中并没有Binder服务的初始化,其实应用的Binder服务的初始化在onZygoteInit的时候已经完成了。

    virtual void onZygoteInit()
    {
        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        proc->startThreadPool();
    }
细节2:

大家会发现bootanimation的最后一步是IPCThreadState::self()->joinThreadPool(),这个的意思是将当前的线程也就是main方法所在的线程加入Binder的线程池,并block住。为什么要这样子做,这样子是保证当前进程不退出。

对照应用:

其实在ActivityThread.java的main方法中也有类似的操作就是Looper.loop(),也是为了避免主线程也就是UI线程退出。

小结:

其实开机动画也好,我们自己写的应用也好,本质上主线程就是一个永远没有返回或者结束的main方法。

bootanimation的界面绘制

先看如下代码,简单总结一下就是通过Binder调用从SurfaceFlinger获取了一块Surface,并将Surface和OpenGL绘制引擎进行绑定,因为开机动画是通过OpenGL绘制的。

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;
    //创建SurfaceControl
    // create the native surface
    sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
            dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
    //设置Layer
    SurfaceComposerClient::Transaction t;
    t.setLayer(control, 0x40000000)
        .apply();
    //获得对应的Surface
    sp<Surface> s = control->getSurface();

    //初始化opengl egl
    // 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);
    //0,0就是代表NULL,正常情况下这个两个是返回,最大,最小的版本
    eglInitialize(display, 0, 0);
    //选择我们自己需要的配置
    eglChooseConfig(display, attribs, &config, 1, &numConfigs);
    //创建一个Window,注意这个时间创建一个传入了一个EGLNativeWindowType,建立起OPENGL和Surface之间的关系
    surface = eglCreateWindowSurface(display, config, s.get(), NULL);
    //创建统一的上下文
    context = eglCreateContext(display, config, NULL, NULL);
    //获取surface的宽度
    eglQuerySurface(display, surface, EGL_WIDTH, &w);
    //获取surface的高度
    eglQuerySurface(display, surface, EGL_HEIGHT, &h);
   //将这个上下文和当前创建的屏幕和Surface绑定
    if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
        return NO_INIT;
    ......省略部分代码
    return NO_ERROR;
}
对照应用:

一般应用都是基于Activity开发的,其实Activity就是帮你隐藏了太多像SurfaceFlinger申请Surface,并在Surface使用Canvas进行绘制。

申请Surface
/frameworks/base/core/java/android/view/ViewRootImpl.java

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {

        ...省略部分代码
       //mSurface需要通过relayout方法,才能变成可用的Surface
        int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
                mPendingMergedConfiguration, mSurface);

        return relayoutResult;
    }

通过Surface获得Canvas对象然后传递给view进行ondraw方法的绘制

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty, Rect surfaceInsets) {

        // Draw with software renderer.
        final Canvas canvas;
        ....省略代码
        // 通过前面申请的Surface锁定以后,获得Canvas对象
        canvas = mSurface.lockCanvas(dirty);
        ....省略代码
       // 将Canvas对象传递给View进行draw的方法的回调,也就是当前的activity开始界面绘制
        mView.draw(canvas);
        ....省略代码
        return true;
    }

总结

对BootAnimation和应用进行比较,我更多的希望读者可以透过现象看本质,其实开机动画和应用非常类似。只不过应用的启动需要Zygote进程,AMS,PMS,WMS的支持,分装了好多对Binder初始化,Surface的申请,绘制,而开机动画启动比较快,这个时候Zygote进程,AMS,PMS,WMS都还没有准备好,只能依靠SurfaceFlinger和Binder的最原始的接口来显示UI。

进一步思考

BootAnimation不支持触摸事件,应用支持触摸事件,我们能否让BootAnimation也支持触摸事件,应用的触摸事件的本质是什么 ?

相关文章

  • [016]BootAnimation引发的思考

    前言 BootAnimation就是安卓系统的开机动画,估计网上面对BootAnimation的源码解读已经一大堆...

  • Android Framework利用OpenGL修改开机动画

    目录 效果展示 实现步骤 1.修改BootAnimation 这里我们需要修改BootAnimation.h和Bo...

  • Android O 8.0 自定义开机动画/开机铃声

    1. 制作bootanimation.zip 参考解压的一个bootanimation.zip, 里面有两个文件夹...

  • 引发的思考

  • 引发的思考

    一、 每天进店的各色人群中,我根据他们的行为举止大概可以归纳他们的品性怎么样: 有的人结账的时候,十分的客气,他们...

  • 造纸行业

    纸巾引发的思考

  • 引发思考

    今天参加了远道而来的学习培训班,学习用NLP的技术解决,企业文化与管理脱节的问题。 这是一个历时三个小时的培训课堂...

  • 引发思考

    刚刚步入职场,慢慢的发现一切都和以前的学生生涯有所不同。 当学生的时候可以默默地做自己喜欢的事,不用担心该是自己的...

  • 引发思考

    知乎链接-1,每天什么都不想做怎么办 知乎链接-2,人迷茫的时候该干什么

  • 引发思考

    阅读新记录 雨泽今天晚上主动给妈妈讲故事,偶尔会有不认识的字,让妈妈告诉他, 今天在学校自己阅读《大卫,不可以》,...

网友评论

      本文标题:[016]BootAnimation引发的思考

      本文链接:https://www.haomeiwen.com/subject/udctbctx.html