这是一篇写给自己用于备忘的文字。所以内容上比较跳跃,不建议作为参考。使用代码版本Android4.4.
首先Activity的加载不归我们管辖,所以View的展示可以说是从Activity的setContentView()开始的,这个方法最终会走到PhoneWindow(继承与Window)类中的setContentView()方法。
public void setContentView(View view, ViewGroup.LayoutParams params) { if (mContentParent == null) { installDecor(); } else { mContentParent.removeAllViews(); } mContentParent.addView(view, params); .... }
这里涉及了一个重要的对象,mContentParent对象,这是个ViewGroup类型对象。也就是我们activity的内容部分(用于放置我们加进去的View)的根View,这是由installDecor()方法创建的。这个方法首先需要创建整个Activity的根DecorView对象,这是个FrameLayout的子类。是整个的Activity的框架(包含标题栏与ActionBar)。然后在通过generateLayout()方法创建mContentParent对象,与其说创建不如说获取,因为这个对象时从DecorView中实例化xml布局文件中根据ID得到的。
protected ViewGroup generateLayout(DecorView decor) { ....... View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); ....... return contentParent; }
最后,回到PhoneWindow中,mContentParent对象使用addView()方法,加载我们要加入的view。这样我们要展示的View就完全准备好了。但是这里并没有开始绘制,只是单纯的准备工作完成。
之后,由于Activity自己加载过程中onCreate()方法执行之后即上述加载完View结束后,随着Activity的加载,ActivityManagerService(AMS)开始调用ActivtyThread的handleResumeActivity()的方法,并把mDecorView设置为可见(setVisibility(View.VISIBLE))。
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) { ....... ActivityClientRecord r = performResumeActivity(token, clearHide); ....... final Activity a = r.activity; //获取activity对象 ....... decor.setVisibility(View.INVISIBLE); //显示decor ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { a.mWindowAdded = true; wm.addView(decor, l); //加载了我们的View } ....... }
这里最重要的代码就是wm.addView(decor, l);但是这里要先说一下wm,看上述代码可知只是一个ViewManager类型,实际上是一个WindowManager(实现了ViewManager接口)。他通过activity的getWindowManager()获得,获得的WindowManager对象最初来自于Window类的getWindowManager()方法,由setWindowManager()创建。
public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) { ....... if (wm == null) { wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); } mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); }
这里可以看到,首先我们通过mContext对象获取系统的WindowManagerService。然后通过createLocalWindowManager方法复制了一份。所以说对于WindowManagerService对象,每一个Activity程序都会有一个自己的mWindowManager。下面就是通过这个WindowManager将我们的View加载并显示出来。回到addView()方法,这个方法的实现在WindowManagerImpl里面,发现他只是一个代理,这个方法最终交给了WindowManagerGlobal类的一个单例来执行。
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); //单例对象 ............... public void addView(View view, ViewGroup.LayoutParams params) { mGlobal.addView(view, params, mDisplay, mParentWindow); }
单例的addView()方法如下:
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ......... ViewRootImpl root; ......... root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); ......... root.setView(view, wparams, panelParentView); ......... }
这里我们首次看到了ViewRoot,这是非常重要的类。在ViewRoot的构造函数中可见,
public ViewRootImpl(Context context, Display display) { ....... mWindowSession = WindowManagerGlobal.getWindowSession(); ...... }
这里取得了一个WindowSession的对象,这是一个aidl的接口,用于跨进程取得Session对象。流程如下:
public static IWindowSession getWindowSession() { ..... sWindowSession = windowManager.openSession( imm.getClient(), imm.getInputContext()); ..... return sWindowSession; }
这里调用了类WindowManagerService里实现的openSession方法。
public IWindowSession openSession(IInputMethodClient client, IInputContext inputContext) { ...... Session session = new Session(this, client, inputContext); return session; }
用此可见,Session是在WMS中直接创建的,如名字一样,这是一个WMS与ViewRoot的一个会话,由此ViewRoot的功能就非常清楚:是View与WMS通信的桥梁,在ViewRoot中使用WMS。我们了解了ViewRoot的功能,让我们看看他是如何执行的。我们回到WindowManagerImpl的addView()。ViewRoot使用setView方法将我们需要显示View放入ViewRoot内进行操作。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { .......... requestLayout(); .......... res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mInputChannel); ......... }
首先是requestLayout()方法,经过scheduleTraversals()方法的传递,使用Choreographer的postCallback()方法异步执行了一个Runnable。这里的Choreographer就是一个消息处理器,里面包含一个handler来处理几种信号,这个与主题关系不大暂时不做介绍。这个Runnable中调用了一个doTraversal()方法,最终指向performTraversals()方法。这个方法相信有些人非常了解,因为我们所用View的树结构就是在这里被绘制展开的,包含一系列的measure,Layout等测量大小计算位置等方法。但是现在先不介绍他。因为怎么画还不急着去研究,我们先要去拿到我们的画布。虽然绘制代码逻辑在前,但是别忘了他是个消息队列形式执行的方法,在我们的activity没启动完毕时,这个绘制过程是不会被触发的。 这时我们回到setView来看Session的addToDisplay方法。
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, InputChannel outInputChannel) { return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outContentInsets, outInputChannel); }
这里代理调用了mService的addWindow()方法,其中传入的参数window是ViewRoot内的一个内部类通过binder机制放入到WMS中用于WMS反向调用ViewRoot,即与Session相反。这样ViewRoot与WMS建立了双向链接。这时,让我们进入WMS看看addWindow()方法。
public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, InputChannel outInputChannel) { .............. win = new WindowState(this, session, client, token, attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent); ............. win.attach(); mWindowMap.put(client.asBinder(), win); ............. }
这里实例化了一个WindowState对象,里面保存了了一个SurfaceSession对象。这个SurfaceSession的实例化是通过win的attach()方法,最终调用到Session中的windowAddedLocked()方法。
void windowAddedLocked() { ........ mSurfaceSession = new SurfaceSession(); ........ mService.mSessions.add(this); }
最终SurfaceSession被放入了Service的HashSet<Session>集合里面,对于Surface的实例化这里简单说一下,他调用了自己的native方法,与SurfaceFlinger建立了一个链接,SurfaceFlinger是用来绘制surface的一个服务,SurfaceSession就是WMS与SurfaceFlinger的通信桥梁。至此Activity的启动消息被执行完成,但是我们还是没有获取到画布,所以我们继续进入到performTraversals()方法。先无视掉View绘制的代码,我们直接看我们需要的代码 :relayoutWindow()方法(在performTraversals()直接调用)。
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
.........
int relayoutResult = mWindowSession.relayout(,......, mSurface);
.........
}
看着有点乱,就是讲mSurface传入了Session的方法中。mSurface是ViewRoot自己建立的一个Surface对象,但是是个空构造函数没什么意义。这里传入Session就是要利用WMS充实这个Surface对象,这个Surface就是我们要绘制的屏幕。在Session中,他直接把Surface对象传入了mService即WMS对象中。
public int relayout(IWindow window, ..., Surface outSurface) { ...... int res = mService.relayoutWindow(this, window, ..., outSurface); .... }
这里的outSurface就是从客户端ViewRoot中一步步传入WMS内的Surface对象了。下面来开WMS对Surface对象的处理过程
public int relayoutWindow(Session session, IWindow client, ......, Surface outSurface) { WindowState win = windowForClientLocked(session, client, false); ....... WindowStateAnimator winAnimator = win.mWinAnimator; ........ SurfaceControl surfaceControl = winAnimator.createSurfaceLocked(); if (surfaceControl != null) { outSurface.copyFrom(surfaceControl); } }
win就是通过我们上文中被放入map中的WindowState对象。这里首先通过win获取到WindowStateAnimator对象,再通过这个对象通过createSurfaceLocked()方法创建一个SurfaceControl对象(在早期的版本要简单些,直接就通过win创建出surface了)。
SurfaceControl createSurfaceLocked() {
........
mSurfaceControl = new SurfaceControl(
mSession.mSurfaceSession,
attrs.getTitle().toString(),
w, h, format, flags);
........
}
在SurfaceControl的构造函数中传入了SurfaceSession对象。然后回到上面,通过surface的copyFrom方法,从SurfaceControl对象中取得Surface。这里获取的方式仍然是native方法。到此,其实我们已经在java层面看到了surface的创建于传递的过程。其实surface还远没有结束,现在关于它我们只了解了三个部分,ViewRoot的创建(只是简单实例化一个对象,没有意义),SurfaceFlinger创建真正的Surface,WMS获得它。至于Surface怎样传回ViewRoot,还有其他对它的一系列处理,其中大量的代码是在native函数中进行的,这里先不研究native的C++函数。我们优先搞定上层处理。所以我们回到ViewRoot中继续看View的创建于绘制流程。
performTraversals() { ..... performDraw(); //开始绘制了 ..... }
再继续看performDraw()
private void performDraw() { ...... draw(fullRedrawNeeded); ...... }
继续。
private void draw(boolean fullRedrawNeeded) { Surface surface = mSurface; //我们千辛万苦获得的surface ........ if (!drawSoftware(surface, attachInfo, yoff, scalingRequired, dirty)) { return; } ....... }
这里的drawSoftware()方法就是绘制了
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff, boolean scalingRequired, Rect dirty) { ....... canvas = mSurface.lockCanvas(dirty); //在指定区域拿到canvas画笔 ....... mView.draw(canvas); //开始绘制我们的View ....... surface.unlockCanvasAndPost(canvas); //回收surface ....... }
终于,我们看到了mView.draw(canvas);利用画笔开始调用View的draw方法。具体的View的绘制,我们以后再看。