andoridUI显示

Posted 白嫩豆腐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了andoridUI显示相关的知识,希望对你有一定的参考价值。

前言

android系统大部分程序都是基于activity来实现的,所以我们UI显示都是通过xml文件生成,通过setcontetview来把页面交给phonewidow,最终交给SurfaceFlinger。因为我们相了解一下SurfaceFlinger,直接阅读activity觉得工作量比较大,也没必要,我们可以自己参考BootAnimation写一个简单的程序,作为阅读AndroidUI显示的小测试程序。

正文

我们自己写的程序主要包括一个main程序,代码如下:

int main()
{
    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
        Animation *boot = new Animation();

        waitForSurfaceFlinger();
        
        boot->readyToRun();
        
        boot->threadLoop();
        
    return 0;
}

这里就比简单,调用构造函数,这里有个小track,主要是再内部通过binder获取到SurfaceFlinger的一个代理对象,这里需要等待一下,这里就不再详细介绍,后面会介绍。然后readyToRun,最终通过threadLoop掉入死循环,通过gl渲染UI。
下面我们分析一下两个关键函数,


Animation::Animation(){
    mSession = new SurfaceComposerClient();
}


status_t Animation::readyToRun() {
    mAssets.addDefaultAssets();

    mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
    if (mDisplayToken == nullptr)
        return NAME_NOT_FOUND;

    DisplayConfig displayConfig;
    const status_t error =
            SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &displayConfig);
    if (error != NO_ERROR)
        return error;

    ui::Size resolution = displayConfig.resolution;

    sp<SurfaceControl> control = session()->createSurface(String8("uitest"),
            resolution.getWidth(), resolution.getHeight(), PIXEL_FORMAT_RGB_565);

    sp<Surface> s = control->getSurface();

    // initialize opengl and egl
    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    eglInitialize(display, nullptr, nullptr);
    EGLConfig config = getEglConfig(display);
    EGLSurface surface = eglCreateWindowSurface(display, config, s.get(), nullptr);
    EGLContext context = eglCreateContext(display, config, nullptr, nullptr);
    EGLint w, h;
    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;

    return NO_ERROR;
}

这里代码只包含三部分,

  1. 初始化surface
  2. 初始化gl
  3. 赋值类的全局变量。

关于gl环境,这里不再详细介绍,因为这个东西非常复杂,但是目的相对简单,主要是调用gpu,生成用于显示的buffer。第三步更是基本的复制
初始化surface是相对复杂,
主要包括三步:

1 . 构造函数的获取SurfaceFlinger的代理对象
2. 通过SurfaceFlinger的代理对象session获取SurfaceControl。
3. SurfaceControl是管理SurfaceFlinger管理surface的控制类的代理对象
4. 最终获取surface。
这样的三层设计,主要目的是为了解决一对多,便于管理的目的。
一个surfaceflinger同时管理多个应用,一个应用,可以有多个窗体。基本上每个应用对应一个SurfaceControl,然后每个窗口对应一个surface(phonewidow就是一个surface)我们也可以自己申请一个surface。
下面我们就大概介绍一下如何绘制内容

bool Animation::threadLoop() {
    result = android();

    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroyContext(mDisplay, mContext);
    eglDestroySurface(mDisplay, mSurface);
    mFlingerSurface.clear();
    mFlingerSurfaceControl.clear();
    eglTerminate(mDisplay);
    eglReleaseThread();

    return true;
}

bool Animation::android() {
    initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
    // clear screen
    glClearColor(0,0,0,1);
    glClear(GL_COLOR_BUFFER_BIT);
    eglSwapBuffers(mDisplay, mSurface);

    glEnable(GL_TEXTURE_2D);

//    const nsecs_t startTime = systemTime();
    do {

        const GLint xc = (mWidth  - mAndroid[0].w) / 2;
        const GLint yc = (mHeight - mAndroid[0].h) / 2;

        nsecs_t now = systemTime();

        glDisable(GL_SCISSOR_TEST);
        glClear(GL_COLOR_BUFFER_BIT);

        glEnable(GL_BLEND);
        glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
        glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);

        EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);
        if (res == EGL_FALSE)
            break;

        // 12fps: don't animate too fast to preserve CPU
        const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
        if (sleepTime > 0)
            usleep(sleepTime);

    } while (!exitPending());

    glDeleteTextures(1, &mAndroid[0].name);
    glDeleteTextures(1, &mAndroid[1].name);
    return false;
}

这里的函数threadLoop其实比较简单。其实就是陷入android()函数,android函数其实就是准备图像纹理,以及清空画布,这里不再详细介绍。最终进入一个死循环,没个83333纳秒绘制一次。真正的绘制其实是eglSwapBuffers。这个其实针对吧绘制后的buffer交给SurfaceFlinger。
因为OpenGL过程还是稍微复杂,过程是我们无法了解的,我们直接手动绘制图片。

修改threadLoop,调用函数进入死循环,渲染屏幕。具体代码如下:

bool Animation::threadLoop() {
	result = false;
    if(result){
        result = android();
    }else{
        framebuffer();
        return result;
    }

    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroyContext(mDisplay, mContext);
    eglDestroySurface(mDisplay, mSurface);
    mFlingerSurface.clear();
    mFlingerSurfaceControl.clear();
    eglTerminate(mDisplay);
    eglReleaseThread();

    return true;
}
bool Animation::framebuffer() {

    do {
	nsecs_t now = systemTime();
    	ANativeWindow_Buffer buffer;
    	
    	mFlingerSurface->lock(&buffer, nullptr);
    	uint8_t *dst_data = static_cast<uint8_t *>(buffer.bits);
    	ALOGE("----------%d",buffer.format);	
    	for (int i = 0; i < buffer.width*buffer.height;i++){
    	    dst_data[i*2] = 0xff & (i/buffer.width);
    	    dst_data[i*2 +1] = 0xff & (i/buffer.width);
    	}
    	mFlingerSurface->unlockAndPost();
        const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);
        if (sleepTime > 0)
            usleep(sleepTime);

    } while (!exitPending());

    return false;
}

其实这里就比较简单主要是调用surface的lock获取一个buffer,然后填写内容,最后调用unlockAndPost,交给SurfaceFlinger去渲染。

后记

这里基本上完成了一个最简单的SurfaceFlinger的简单使用,其实绘制最多的是使用opengl或者skila,才能更方便的生成图像,我们下一篇简要介绍一下我们这里生成的buffer如何交给hw层。

以上是关于andoridUI显示的主要内容,如果未能解决你的问题,请参考以下文章

Sphinx、reStructuredText 显示/隐藏代码片段

执行代码时有时不显示对话框片段

如何在 Resharper IntelliSense 中显示所有 Visual Studio 代码片段?

CSS 显示代码片段:使用PRE标记包裹长行

回收站视图未显示在片段中

Xcode 8 Autocomplete Broken - 仅显示有限的用户代码片段 - 知道为啥吗?