Android 4.2 SetContentView 流程分析(三)
来源:互联网 发布:seo网络推广软件 编辑:程序博客网 时间:2024/05/22 14:20
这一路分析下来, 开始进入 JNI layer 了.
[android_view_SurfaceSession.cpp]static jint nativeCreate(JNIEnv* env, jclass clazz) { // new 一个 SurfaceComposerClient对象, 其功用后面会在分析. sp<SurfaceComposerClient> client = new SurfaceComposerClient; client->incStrong(clazz); return reinterpret_cast<jint>(client);}
到此在做个结论,handleResumeActivity 函数利用WindowManagerImpl依照WindowManager.LayoutParams的属性来为DecoreView增加一个 view的纪录.
, 在addView 过程会有以下的流程:
1. 产生一个新的ViewRootImpl, 在ViewRootImpl的对象初始化中建立一个 DecoreView 和 WindowManagerService 之间的 Session.
2. 将先前设置好的View 加入DecoreView. 在加入过程有两个关键动作如下:
a. 执行 performTraversals开始布局并且绘画显示画面.
b. SurfaceComposerClient 组件: new一个 SurfaceComposerClient对象, 其功用跟 surfaceControl 有关.
针对以上第二点的流程的两个关键动作,我们继续分析.
a. 执行 performTraversals
[ViewRootImpl.java]private void performTraversals() { // mView是前面经由setContentView的一个View,也就是DecoreView. final View host = mView; //... //关键函数 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); //.... //... //开始绘图. performDraw(); //...}
performTraversals函数做蛮多杂事的, 我们只需要关心两个动作其中两个关键动作, relayoutWindow和 performDraw.
relayoutWindow 分析
[ViewRootImpl.java]private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility, boolean insetsPending) throws RemoteException { //.... //由此可以看到藉由Session去呼叫WindowManagerService的 // relayoutWindow function. int relayoutResult = sWindowSession.relayout( mWindow, mSeq, params, (int) (mView.getMeasuredWidth() * appScale + 0.5f), (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, insetsPending ? WindowManagerImpl.RELAYOUT_INSETS_PENDING : 0, mWinFrame, mPendingContentInsets, mPendingVisibleInsets, mPendingConfiguration, mSurface); //... return relayoutResult;} [Session.java]public int relayout(IWindow window, int seq, WindowManager.LayoutParamsattrs, int requestedWidth, int requestedHeight, int viewFlags, int flags, Rect outFrame, Rect outContentInsets, Rect outVisibleInsets, Configuration outConfig, SurfaceoutSurface) { int res = mService.relayoutWindow(this, window, seq, attrs, requestedWidth, requestedHeight, viewFlags, flags, outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface); return res;}有此段程序代码,可以看到relayoutWindow函数会藉由Session去呼叫WindowManagerService的relayoutWindowfunction. 其中值得关注的是最后一个参数mSurface.这个mSurface是怎么来的.在定义ViewRootImpl 类别一开始就产生了一个没有带参数的Surface物件
[ViewRootImpl.java]private final Surface mSurface = new Surface(); [Surface.java]public Surface() { checkHeadless(); mCloseGuard.open("release");}
接这就继续分析WindowManagerService的relayoutWindow function做了哪些事?
[WindowManagerService.java]public int relayoutWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, int flags, Rect outFrame, Rect outContentInsets, Rect outVisibleInsets, Configuration outConfig, Surface outSurface) { //断开Binder机制,清除呼叫者的UID, PID,重新设定service的//UID, PID,使其可以Service呼叫自己的功能. long origId = Binder.clearCallingIdentity(); synchronized(mWindowMap) { //经由先前ViewRootImpl在setView时所建立的一个//WindowState Hashtable搭配指定的Client的IBinder去找出相//对应的WindowState. WindowState win = windowForClientLocked(session, client,false); if (win == null) { return 0; } WindowStateAnimator winAnimator = win.mWinAnimator; // do something Surface surface = winAnimator.createSurfaceLocked(); if (surface != null) { //复制一份surface数据到outSurface. outSurface.copyFrom(surface); } else { //归还之前为surface配置的内存. outSurface.release(); } } //恢复Binder机制,清除service的UID, PID,恢复呼叫者的UID,//PID. Binder.restoreCallingIdentity(origId); return (inTouchMode ? WindowManagerImpl.RELAYOUT_RES_IN_TOUCH_MODE : 0) | (toBeDisplayed ? WindowManagerImpl.RELAYOUT_RES_FIRST_TIME : 0) | (surfaceChanged ? WindowManagerImpl.RELAYOUT_RES_SURFACE_CHANGED : 0) | (animating ? WindowManagerImpl.RELAYOUT_RES_ANIMATING : 0);}由此段程序代码带出两个关键, Surface surface = winAnimator.createSurfaceLocked(); 和outSurface.copyFrom(surface); 就依序来分析这两个动作.
Surface surface = winAnimator.createSurfaceLocked();
[WindowStateAnimator.java]Surface createSurfaceLocked() { if (mSurface == null) { // … mSurface = new Surface( mSession.mSurfaceSession, attrs.getTitle().toString(), w, h, format, flags); // … } return mSurface;} [Surface.java]/** create a surface with a name @hide */public Surface(SurfaceSession session, String name, int w, int h, int format, int flags) throws OutOfResourcesException { if (session == null) { throw new IllegalArgumentException("session must not be null"); } if (name == null) { throw new IllegalArgumentException("name must not be null"); } if ((flags & HIDDEN) == 0) { // ... } checkHeadless(); mName = name; nativeCreate(session, name, w, h, format, flags); mCloseGuard.open("release"); }private native void nativeCreate(SurfaceSession session, String name, int w, int h, int format, int flags) throws OutOfResourcesException; [android_view_Surface.cpp]static void nativeCreate(JNIEnv* env, jobject surfaceObj, jobject sessionObj, jstring nameStr, jint w, jint h, jint format, jint flags) { ScopedUtfChars name(env, nameStr); sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj)); sp<SurfaceControl> surface = client->createSurface( String8(name.c_str()), w, h, format, flags); if (surface == NULL) { jniThrowException(env, OutOfResourcesException, NULL); return; } setSurfaceControl(env, surfaceObj, surface);}
由此可知, winAnimator.createSurfaceLocked所做的事就是在native层产生一个SurfaceControl 对象,在Java层产生一个带有surfaceSession参数的Surface参考对象.
outSurface.copyFrom(surface);
[Surface.java]public native void copyFrom(Surface o); [android_view_Surface.cpp]static void Surface_copyFrom( JNIEnv* env, jobject clazz, jobject other){ if (clazz == other) return; if (other == NULL) { doThrowNPE(env); return; } /* * This is used by the WindowManagerService just after constructing * a Surface and is necessary for returning the Surface reference to * the caller. At this point, we should only have a SurfaceControl. */ const sp<SurfaceControl>& surface = getSurfaceControl(env, clazz); const sp<SurfaceControl>& rhs = getSurfaceControl(env, other); //将rhs对象指定给sutface指针对象. if (!SurfaceControl::isSameSurface(surface, rhs)) { // we reassign the surface only if it's a different one // otherwise we would loose our client-side state. setSurfaceControl(env, clazz, rhs); }}
由以上的代码段,WindowManagerService的relayoutWindow可以做个以下总结:
1. 藉由mWindowMap里去得到目前Client (DecoreView)的WindowState对象.
2. 藉由此WindowState物件win来new一个带有SurfaceSession参数的Surface物件.用意是可以借着SurfaceComposerClient来新增一个SurfaceControl.
3. 利用Surface的copyfrom 函数把带有SurfaceSession参数的Surface对象复制一份到ViewRootImpl所产生的一个无参数建构出来的Surface对象.
结论又带出一个新的角色SurfaceComposerClient,其功用后面会分析.
performDraw分析
[ViewRootImpl.java]
private void performDraw() { //检查目前的Screen是否开启且是否要画下一张画面.只要其中一个条 //件不满足,就不继续往下做处理. // fullRedrawNeeded用来控制画面的大小. final boolean fullRedrawNeeded = mFullRedrawNeeded; mFullRedrawNeeded = false; mIsDrawing = true; try { draw(fullRedrawNeeded); } finally { mIsDrawing = false; }} private void draw(boolean fullRedrawNeeded) { // mSurface 是ViewRootImpl所产生的一个无参数建构出来的Surface //对象,经过relayoutWindow处理之后, 就变成一个带有 //surfaceSession参数的Surface对象,在native layer具有surface //control物件. Surface surface = mSurface; //... final Rect dirty = mDirty; // .... //设定画面的大小 if (fullRedrawNeeded) { attachInfo.mIgnoreDirtyState = true; dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f)); } if (!dirty.isEmpty() || mIsAnimating) { //从android 4.1之后在绘制画面多了一个选择就是由HardWare //renderer来处理. if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) { //由Hardware renderer来画. } else if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) { return; } } // ...} private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff, boolean scalingRequired, Rect dirty) { //一旦有Hardware renderer的request而此时Hardware renderer的功 //能又未打开,系统将停止Software renderer处理动作. if (attachInfo.mHardwareRenderer != null && !attachInfo.mHardwareRenderer.isEnabled() && attachInfo.mHardwareRenderer.isRequested()) { mFullRedrawNeeded = true; scheduleTraversals(); return false; } //由Software renderer来画. Canvas canvas; // ... canvas = mSurface.lockCanvas(dirty); // ... //这里的mView就是在一开始 handleResumeActivity函数中的第六个 //步骤, Window Manager (WindowManagerImpl)要处理addView所带 //入的ViewGroup (DecoreView)物件. mView.draw(canvas); // ... surface.unlockCanvasAndPost(canvas); return true;}从以上的程序代码可以看到, Surface lock一块Canvas, 之前配置的View (DecoreView) 在利用这块Canvas来绘制UI组件.避免分析太分散,Surface的 lockCanvas, unlockCanvasAndPost和DecoreView的draw 函数分析放在最后分析.
b.SurfaceComposerClient 组件.
当ViewRootImpl去设置View(DecoreView)时,会先经由Session通知WindowMangerService去addWindow, 在addWindow中会去new SurfaceSession.SurfaceComposerClient就是由new SurfaceSession中产生的.由于SurfaceComposerClient衍生自RefBase类别, 所以都会用sp来取得对象指针.
[android_view_SurfaceSession.cpp]sp<SurfaceComposerClient> client = new SurfaceComposerClient;
通常利用sp来建构对象时,会先呼叫onFirstRef函数,才会呼叫相对应建构子.因此若想要看一下SurfaceComposerClient的初始实作, 需要从其onFirstRef函数下手,分析如下:
[SurfaceComposerClient.cpp]void SurfaceComposerClient::onFirstRef() { // ISurfaceComposer 是 surfaceFlinger的 binder界面,所以 //getComposerService会回传SurfaceFlinger的Binder Proxy: //BpSurfaceComposer. sp<ISurfaceComposer> sm(ComposerService::getComposerService()); if (sm != 0) { sp<ISurfaceComposerClient> conn = sm->createConnection(); if (conn != 0) { mClient = conn; mStatus = NO_ERROR; } }}既然getComposerService回传的是SurfaceFlinger的Binder Proxy,表示由得到的对象,其方法可以在SurfaceFlnger类别去找.所以sm->createConnection(), 其createConnection函数如下所示:
[SurfaceFlinger.cpp]sp<ISurfaceComposerClient> SurfaceFlinger::createConnection(){ sp<ISurfaceComposerClient> bclient; sp<Client> client(new Client(this)); status_t err = client->initCheck(); if (err == NO_ERROR) { bclient = client; } return bclient;}createConnection 函数回传一个ISurfaceComposerClient指针对象, 其实作可以发现这个指针对象是一个Client的指针对象.接下来就来分析Client建构子和其方法initCheck.
[Client.cpp]Client::Client(const sp<SurfaceFlinger>& flinger) : mFlinger(flinger), mNameGenerator(1){}status_t Client::initCheck() const { //没做任何事,推测未来会有实作. return NO_ERROR;}
c. lockCanvas, unlockCanvasAndPost
现在就来分析,Surface的 lockCanvas, unlockCanvasAndPost.
[Surface.java]/** draw into a surface */public Canvas lockCanvas(Rect dirty) throws OutOfResourcesException, IllegalArgumentException { return nativeLockCanvas (dirty);}private native Canvas nativeLockCanvas (Rect dirty); [android_view_Surface.cpp]static jobject nativeLockCanvas (JNIEnv* env, jobject clazz, jobject dirtyRect){ sp<Surface> surface(getSurface(env, surfaceObj)); if (!Surface::isValid(surface)) { doThrowIAE(env); return NULL; } //1. 取得Dirty区域的大小. Region dirtyRegion; if (dirtyRectObj) { Rect dirty; dirty.left = env->GetIntField(dirtyRectObj, gRectClassInfo.left); dirty.top = env->GetIntField(dirtyRectObj, gRectClassInfo.top); dirty.right = env->GetIntField(dirtyRectObj, gRectClassInfo.right); dirty.bottom = env->GetIntField(dirtyRectObj, gRectClassInfo.bottom); if (!dirty.isEmpty()) { dirtyRegion.set(dirty); } } else { dirtyRegion.set(Rect(0x3FFF, 0x3FFF)); } //2. 利用获得的Dirty区域利用Surface对象的lock函数来设置 //SurfaceInfo. Surface::SurfaceInfo info; status_t err = surface->lock(&info, &dirtyRegion); //3. 从Java layer取得SkCanvas object. // mCanvas在Surface建构之前就已经存在了,请参考程序代码 Surface.java jobject canvas = env->GetObjectField(clazz, so.canvas); //... SkCanvas* nativeCanvas = (SkCanvas*)env->GetIntField(canvas, no.native_canvas); //4. 利用得到的SkCanvas来开始设定绘制区域.从Android 4.1开始绘制的 //类型有分SkBitmap内存管理跟 SkRegion内存管理. //利用SurfaceInfo配置一块 2d skia的SkBitmap内存管理 SkBitmap bitmap; ssize_t bpr = info.s * bytesPerPixel(info.format); bitmap.setConfig(convertPixelFormat(info.format), info.w, info.h, bpr); if (info.format == PIXEL_FORMAT_RGBX_8888) { bitmap.setIsOpaque(true); } if (info.w > 0 && info.h > 0) { //设定需要管理surface的buffer bitmap.setPixels(info.bits); } else { // be safe with an empty bitmap. bitmap.setPixels(NULL); } //SkCanvas设定此bitmap内存管理, 以便可以用这个内存管理在 //surface的buffer上作画. nativeCanvas->setBitmapDevice(bitmap); //配置一块 SkRegion内存管理 if (dirtyRegion.isRect()) { // very common case const Rect b(dirtyRegion.getBounds()); clipReg.setRect(b.left, b.top, b.right, b.bottom); } else { size_t count; Rect const* r = dirtyRegion.getArray(&count); while (count) { clipReg.op(r->left, r->top, r->right, r->bottom, SkRegion::kUnion_Op); r++, count--; } } nativeCanvas->clipRegion(clipReg); //5. 储存SkCanvas在内存管理上的配置. int saveCount = nativeCanvas->save(); //6. 利用JNI的函数将一些需要的值来设定Java对象的属性. return canvas;} [Surface.cpp]status_t Surface::lock(SurfaceInfo* other, Region* inOutDirtyRegion) { //... //因为Surface类别衍生自 SurfaceTextureClient类别, 要呼叫父类别的 //函数需要父类别名子加上范围运算符. status_t err = SurfaceTextureClient::lock(&outBuffer, inOutDirtyBounds); //... return err;} [SurfaceTextureClient.cpp]status_t SurfaceTextureClient::lock( ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds){ //0. already locked, retrun if (!mConnectedToCpu) { // int err = SurfaceTextureClient::connect(NATIVE_WINDOW_API_CPU); if (err) { return err; } // we're intending to do software rendering from this point setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); } ANativeWindowBuffer* out; //检查目前在queue中的buffer使用状态.并取出未使用的buffer index. int fenceFd = -1; status_t err = dequeueBuffer(&out, &fenceFd); // 1. 宣告一个GraphicBuffer型态指针变量指向(ANativeWindowBuffer)out //内存. sp<GraphicBuffer> backBuffer(GraphicBuffer::getSelf(out)); //2. 依据backBuffer来设定需要更新的区域边界. const Rect bounds(backBuffer->width, backBuffer->height); Region newDirtyRegion; if (inOutDirtyBounds) { newDirtyRegion.set(static_cast<Rect const&>(*inOutDirtyBounds)); newDirtyRegion.andSelf(bounds); } else { newDirtyRegion.set(bounds); } //3. 检查是否有frontBuffer可以copy,以便显示到画面. // mPostedBuffer跟之后要分析的 unlockCanvasAndPost处理有关. const sp<GraphicBuffer>& frontBuffer(mPostedBuffer); const bool canCopyBack = (frontBuffer != 0 && backBuffer->width == frontBuffer->width && backBuffer->height == frontBuffer->height && backBuffer->format == frontBuffer->format); if (canCopyBack) { // copy the area that is invalid and not repainted this round const Region copyback(mDirtyRegion.subtract(newDirtyRegion)); if (!copyback.isEmpty()) //4. 避免整块memory做更新,因此只要藉由这方法来更新 //该更新的部分. copyBlt(backBuffer, frontBuffer, copyback); } else { // if we can't copy-back anything, modify the user's dirty // region to make sure they redraw the whole buffer newDirtyRegion.set(bounds); mDirtyRegion.clear(); Mutex::Autolock lock(mMutex); for (size_t i=0 ; i<NUM_BUFFER_SLOTS ; i++) { mSlots[i].dirtyRegion.clear(); } } // ... void* vaddr; //5. 依照newDirtyRegion的边界在backBuffer上lock一块内存并传回 //起始地址vaddr给jni中所宣告出来的SkBitmap使用. status_t res = backBuffer->lock( GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, newDirtyRegion.bounds(), &vaddr); //... mLockedBuffer = backBuffer; if (res != 0) { err = INVALID_OPERATION; } else { mLockedBuffer = backBuffer; outBuffer->width = backBuffer->width; outBuffer->height = backBuffer->height; outBuffer->stride = backBuffer->stride; outBuffer->format = backBuffer->format; outBuffer->bits = vaddr; } return err;}在分析lockCanvas中有个dequeueBuffer的动作没有在这里分析,有兴趣再另起一个topic来分析.接下来继续分析unlockCanvasAndPost.
[Surface.java]public void unlockCanvasAndPost(Canvas canvas) { nativeUnlockCanvasAndPost(canvas);}private native void nativeUnlockCanvasAndPost(Canvas canvas); [android_view_Surface.cpp]static void nativeUnlockCanvasAndPost(JNIEnv* env, jobject surfaceObj, jobject canvasObj) { jobject ownCanvasObj = env->GetObjectField(surfaceObj, gSurfaceClassInfo.mCanvas); if (!env->IsSameObject(ownCanvasObj, canvasObj)) { doThrowIAE(env); return; } const sp<Surface>& surface(getSurface(env, clazz)); if (!Surface::isValid(surface)) { return; } SkCanvas* nativeCanvas = reinterpret_cast<SkCanvas*>( env->GetIntField(canvasObj, gCanvasClassInfo.mNativeCanvas)); int saveCount = env->GetIntField(surfaceObj, gSurfaceClassInfo.mCanvasSaveCount); nativeCanvas->restoreToCount(saveCount); nativeCanvas->setBitmapDevice(SkBitmap()); env->SetIntField(surfaceObj, gSurfaceClassInfo.mCanvasSaveCount, 0); status_t err = surface->unlockAndPost(); if (err < 0) { doThrowIAE(env); }} [Surface.cpp]status_t Surface::unlockAndPost() { //因为Surface类别衍生自 SurfaceTextureClient类别, 要呼叫父类别的 //函数需要父类别名子加上范围运算符. return SurfaceTextureClient::unlockAndPost();} [SurfaceTextureClient.cpp]status_t SurfaceTextureClient::unlockAndPost(){ //... //0. 将使用完的Buffer做unlock的动作. status_t err = mLockedBuffer->unlock(); //... //1. 将unlock的buffer index送进queue中. err = queueBuffer(mLockedBuffer.get()); //... //2. 将mLockedBuffer指针对象传给mPostedBuffer指针对象做为下一次 // lockCanvas的依据. mPostedBuffer = mLockedBuffer; mLockedBuffer = 0; }关于View的Draw流程就不在这一次的分析中, 因为Surface中的Canvas和Bitmap对View的Draw流程来说只是个工具. 总结如下:
1. 在App开始要布局layout view都会由setContentView来设置整体画面.
这时的角色有PhoneWindow, WindowManagerImpl, DecoreView
其相互动作如下:
a. getWindow 得到一个 mWindow, 这个 mWindow 就是一个
PhoneWindow 物件.
b. 利用 PhoneWindow 对象设为 WindowManagerImpl的角色.
c. PhoneWindow 物件设置一个 DecoreView 的子 view.
2. 当App从Create状态转到Resume状态时, 这时就会开始绘制画面.
角色: DecoreView, WindowManagerImpl, ViewRootImpl,WindowManagerService,
SurfaceSession, WindowState, Surface, Canvas
i. ViewRootImpl利用Binder机制将WindowManagerService 和 DecoreView建立一个session.
ii. ViewRootImpl经由session请求WindowManagerService来增加一个WindowState,并作记录.
iii. WindowManagerService在作windowstate时, 会建构一个SurfaceComposerClient物件.
iv. 利用SurfaceComposerClient对象产生SurfaceControl对象.
v. 利用Canvas设置两个Buffer, 分别为Front和Back.
vi. 利用Surface对象的lockCanvas, unlockCanvasAndPost函数来处理绘制画面的流程.
- Android 4.2 SetContentView 流程分析(三)
- Android 4.2 SetContentView 流程分析(一)
- Android 4.2 SetContentView 流程分析(二)
- Android 4.2 setContentView 流程研究分析
- 【Android API】setContentView流程分析
- 从setContentView方法分析Android加载布局流程
- 从setContentView方法分析Android加载布局流程
- 从setContentView方法分析Android加载布局流程
- 从setContentView分析Android加载布局的流程
- Android源码分析-深入理解setContentView方法
- Android布局加载之setContentView源码分析
- 走进Android之AppCompatActivity.setContentView源码分析
- android应用程序窗口框架学习(2)-view绘制流程源代码解析-setContentView与LayoutInflater加载解析机制源码分析
- 查看源码分析activity执行setContentView的流程
- Android休眠唤醒驱动流程分析(三)
- Android休眠唤醒驱动流程分析(三)
- Android SDCard UnMounted 流程分析(三)
- Android休眠唤醒驱动流程分析(三)
- MFC文件目录管理的常用函数
- ERROR 2002: Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)解决
- Android 4.2 SetContentView 流程分析(二)
- NSArray
- sp_executesql 调用示例
- Android 4.2 SetContentView 流程分析(三)
- linux没有地址栏的方法
- Codeforces Beta Round #89 (Div. 2)——B
- 如何在VC6.0中设置条件断点
- 电压档调试——功放电路
- meta标签介绍
- hql 语法与详细解释
- 深入认识javascript中的eval函数
- 编译原理 FIRST集和FOLLOW集的求法