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;
}
这里代码只包含三部分,
- 初始化surface
- 初始化gl
- 赋值类的全局变量。
关于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 显示/隐藏代码片段