本篇文章根據前面的分析來實際操作畫一個窗口,使用純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
獲取buffer - 向buffer中填充圖形數據
- 調用
queueBuffer
將buffer送到SurfaceFlinger
合成
我們的代碼就按上面步驟來寫,DrawWindowTest.cpp
源碼如下:
#include <binder/ProcessState.h>
#include <binder/IPCThreadState.h>
#include <system/window.h>
#include <ui/GraphicBuffer.h>
#include <ui/Fence.h>
#include <utils/Log.h>
#include <utils/RefBase.h>
#include <gui/SurfaceControl.h>
#include <gui/SurfaceComposerClient.h>
#include <binder/IBinder.h>
#include <ui/DisplayInfo.h>
#include <gui/Surface.h>
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圖形架構。