本篇文章根据前面的分析来实际操作画一个窗口,使用纯native API来实现,这里的native API并非ndk,而是native层函数,需要在AOSP编译环境下进行。
首先在/frameworks/native/libs/gui/
目录下创建drawWindowTest
测试目录,drawWindowTest
目录中创建Android.bp
和DrawWindowTest.cpp
:
Android.bp:
cc_binary {
name: "drawWindow",
srcs: ["DrawWindowTest.cpp"],
shared_libs: [
"liblog",
"libbinder",
"libgui",
"libui",
"libutils",
],
}
接着来看看DrawWindowTest.cpp
怎么写,前面几篇文章的分析我们大致了解了画一个窗口的流程:
SurfaceFlinger
进程建立连接Surface
对应SurfaceFlinger
进程Layer
Surface
,还需要将Surface
连接到BufferQueue
Surface
的各种数据,如宽高,格式,Z-order,位置,缩放等dequeueBuffer
获取bufferqueueBuffer
将buffer送到SurfaceFlinger
合成我们的代码就按上面步骤来写,DrawWindowTest.cpp
源码如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace android;
//状态值
status_t err;
sp<SurfaceComposerClient> mSurfaceComposerClient;
sp<SurfaceControl> mSurfaceControl;
//屏幕宽高
int mWidth,mHeight;
//GraphicBuffer的父类ANativeWindowBuffer
ANativeWindowBuffer *mNativeBuffer = nullptr;
//连接SurfaceFlinger
void connectSurfaceFlinger();
//获取Surface
sp<ANativeWindow> getSurface();
//连接BufferQueue
void connectBufferQueue(ANativeWindow *surface);
//设置Transaction
void setBufferTransaction();
//设置其他信息
void setBuffer(ANativeWindow *surface);
int main() {
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
//连接SurfaceFlinger
connectSurfaceFlinger();
//获取surface
ANativeWindow *surface = getSurface().get();
//surface连接bufferqueue
connectBufferQueue(surface);
//设置Transaction
setBufferTransaction();
//设置其他信息
setBuffer(surface);
int fenceFD= -1;
//申请buffer
err = surface->dequeueBuffer(surface, &mNativeBuffer, &fenceFD);
if (err != NO_ERROR) {
ALOGE("dequeueBuffer err....");
}
//通过Fence确认申请的buffer是否完全被上一个使用者使用完
sp<Fence> fence(new Fence(fenceFD));
//等待收到releaseFence
int waitResult = fence->waitForever("dequeueBuffer_EmptyNative");
if (waitResult != OK) {
ALOGE("Fence wait err....");
}
//ANativeWindowBuffer转GraphicBuffer
sp<GraphicBuffer> buff(GraphicBuffer::from(mNativeBuffer));
//buffer数据
uint8_t *data = NULL;
//通过lock先锁住buffer
err = buff->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&data));
if (err != NO_ERROR) {
ALOGE("lock buffer err....");
}
//填充数据,这里直接赋值为0,会画一个黑色窗口
*data = 0;
err = buff->unlock();
if (err != NO_ERROR) {
ALOGE("unlock buffer err....");
}
//将填充好的buffer送到SurfaceFlinger进行合成显示
err = surface->queueBuffer(surface, buff->getNativeBuffer(), -1);
if (err != NO_ERROR) {
ALOGE("queueBuffer buffer err....");
return err;
}
mNativeBuffer = NULL;
IPCThreadState::self()->joinThreadPool();
return EXIT_SUCCESS;
}
//1.创建SurfaceComposerClient,这是SurfaceFlinger的Client端
void connectSurfaceFlinger(){
mSurfaceComposerClient = new SurfaceComposerClient;
err = mSurfaceComposerClient->initCheck();
if (err != NO_ERROR) {
ALOGE("SurfaceComposerClient initCheck err....");
return;
}
}
//2.创建Surface,ANativeWindow是创建Surface父类
sp<ANativeWindow> getSurface(){
sp<IBinder> display = SurfaceComposerClient::getInternalDisplayToken();
android::DisplayInfo mainDisplayInfo;
//获取手机的屏幕信息
err = SurfaceComposerClient::getDisplayInfo(display, &mainDisplayInfo);
if (err != NO_ERROR) {
ALOGE("getDisplayInfo err....");
}
//屏幕宽
mWidth = mainDisplayInfo.w;
//屏幕高
mHeight = mainDisplayInfo.h;
//创建surface对应surfaceflinger进程layer
mSurfaceControl = mSurfaceComposerClient->createSurface(
String8("drawWindow"), mWidth/2, mHeight/2,
PIXEL_FORMAT_RGBX_8888, ISurfaceComposerClient::eOpaque);
if (mSurfaceControl == NULL || !mSurfaceControl->isValid()) {
ALOGE("mSurfaceControl err....");
}
//获取surface
sp<ANativeWindow> anw = mSurfaceControl->getSurface();
return anw;
}
//3.Surface连接BufferQueue
void connectBufferQueue(ANativeWindow *surface){
err = native_window_api_connect(surface, NATIVE_WINDOW_API_CPU);
if (err != NO_ERROR) {
ALOGE("connect bufferqueue err....");
}
}
//4.设置buffer数据
void setBufferTransaction(){
/*
setLayer():设置窗口的Z-order
setPosition():设置窗口显示的位置
show():设置窗口显示出来
apply():将设置的窗口信息应用到SurfaceFlinger,真正生效
*/
SurfaceComposerClient::Transaction{}
.setLayer(mSurfaceControl, 0x7FFFFFFF)
.setPosition(mSurfaceControl,mWidth/4,mHeight/4)
.show(mSurfaceControl)
.apply();
}
//5.设置buffer
void setBuffer(ANativeWindow *surface){
//设置usage
err = native_window_set_usage(surface, GRALLOC_USAGE_SW_WRITE_OFTEN);
if (err != NO_ERROR) {
ALOGE("native_window_set_usage err....");
}
//设置transform
err = native_window_set_buffers_transform(surface, NATIVE_WINDOW_TRANSFORM_ROT_90);
if (err != NO_ERROR) {
ALOGE("native_window_set_buffers_transform err....");
}
//设置scaling_mode
err = native_window_set_scaling_mode(
surface, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
if (err != NO_ERROR) {
ALOGE("native_window_set_scaling_mode err....");
}
}
上面的代码基本上是按照画窗口的流程来实现的,看下上面setBuffer
函数,里面调用了一系列native_window_set_XXX
函数来个Surface
设置各种信息,其实最终都是调用到Surface
中setXXX
函数,native_window_set_XXX
是给外部调用提供操作Surface
的接口,这些函数定义在frameworks/native/libs/nativewindow/include/system/window.h中,举个例子看看native_window_set_usage
函数
static inline int native_window_set_usage(struct ANativeWindow* window, uint64_t usage) {
return window->perform(window, NATIVE_WINDOW_SET_USAGE64, usage);
}
会调用ANativeWindow
的perform
函数:
int (*perform)(struct ANativeWindow* window,
int operation, ... );
这个函数指针是在Surface
构造函数中赋值的:
ANativeWindow::perform = hook_perform;
int Surface::hook_perform(ANativeWindow* window, int operation, ...) {
va_list args;
va_start(args, operation);
Surface* c = getSelf(window);
int result = c->perform(operation, args);
va_end(args);
return result;
}
hook_perform
函数又调用Surface
的perform
函数:
int Surface::perform(int operation, va_list args)
{
int res = NO_ERROR;
switch (operation) {
case NATIVE_WINDOW_CONNECT:
// deprecated. must return NO_ERROR.
break;
case NATIVE_WINDOW_DISCONNECT:
// deprecated. must return NO_ERROR.
break;
case NATIVE_WINDOW_SET_USAGE:
res = dispatchSetUsage(args);
break;
case NATIVE_WINDOW_SET_CROP:
res = dispatchSetCrop(args);
break;
case NATIVE_WINDOW_SET_BUFFER_COUNT:
res = dispatchSetBufferCount(args);
.......
}
这里面有很多函数,根据传递过来的参数调用不同的dispatchXXX
,比如native_window_set_usage
传递的就是NATIVE_WINDOW_SET_USAGE64
,从而调用dispatchSetUsage64
:
case NATIVE_WINDOW_SET_USAGE64:
res = dispatchSetUsage64(args);
break;
dispatchSetUsage64
又调用setUsage
最终将数据设置给了Surface
int Surface::dispatchSetUsage64(va_list args) {
uint64_t usage = va_arg(args, uint64_t);
return setUsage(usage);
}
画一个窗口有很多信息必须设置,在之前分析的dequeueBuffer
和queueBuffer
中都有相关判断,比如宽高不能小于0,必须连接BufferQueue
等。
编译测试程序:mmm frameworks/native/libs/gui/drawWindowTest/
push进手机:
adb push out/target/product/SEOUL_ATT/system/bin/drawWindow /system/bin
运行这个测试程序:adb shell /system/bin/drawWindow
效果如下:
通过这一个测试程序结合前几篇理论的文章可以更好的熟悉Android图形架构。