这是一篇写给自己用于备忘的文字。所以内容上比较跳跃,不建议作为参考。使用代码版本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的绘制,我们以后再看。