Android深入淺出之Surface[1]
Android深入淺出之Surface
一 目的
本節的目的就是為了講清楚Android中的Surface系統,大家耳熟能詳的SurfaceFlinger到底是個什麼東西,它的工作流程又是怎樣的。當然,鑑於SurfaceFlinger的複雜性,我們依然將採用情景分析的辦法,找到合適的切入點。
一個Activity是怎麼在螢幕上顯示出來的呢?我將首先把這個說清楚。
接著我們把其中的關鍵呼叫抽象在Native層,以這些函式呼叫為切入點來研究SurfaceFlinger。好了,開始我們的征途吧。
二 Activity是如何顯示的
最初的想法就是,Activity獲得一塊視訊記憶體,然後在上面繪圖,最後交給裝置去顯示。這個道理是沒錯,但是
好吧,我們從Activity最初的啟動開始。程式碼在
framework/base/core/java/android/app/ActivityThread.java中,這裡有個函式叫handleLaunchActivity
[---->ActivityThread:: handleLaunchActivity()]
private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) {
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward);
---->呼叫handleResumeActivity
}
handleLaunchActivity中會呼叫handleResumeActivity。
[--->ActivityThread:: handleResumeActivity]
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {
boolean willBeVisible = !a.mStartedActivity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l); //這個很關鍵。
}
上面addView那幾行非常關鍵,它關係到咱們在Activity中setContentView後,整個Window到底都包含了些什麼。我先告訴大家。所有你建立的View之上,還有一個DecorView,這是一個FrameLayout,另外還有一個PhoneWindow。上面這些東西的程式碼在
framework/Policies/Base/Phone/com/android/Internal/policy/impl。這些隱藏的View的建立都是由你在Acitivty的onCreate中呼叫setContentView導致的。
[---->PhoneWindow:: addContentView]
public void addContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {//剛建立的時候mContentParent為空
installDecor();
}
mContentParent.addView(view, params);
final Callback cb = getCallback();
if (cb != null) {
cb.onContentChanged();
}
}
installDecor將建立mDecor和mContentParent。mDecor是DecorView型別,
mContentParent是ViewGroup型別
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
那麼,ViewManager wm = a.getWindowManager()又返回什麼呢?
PhoneWindow從Window中派生,Acitivity建立的時候會呼叫它的setWindowManager。而這個函式由Window類實現。
程式碼在framework/base/core/java/android/view/Window.java中
public void setWindowManager(WindowManager wm,IBinder appToken, String appName) {
mAppToken = appToken;
mAppName = appName;
if (wm == null) {
wm = WindowManagerImpl.getDefault();
}
mWindowManager = new LocalWindowManager(wm);
}
你看見沒,分析JAVA程式碼這個東西真的很複雜。mWindowManager的實現是LocalWindowManager,但由通過Bridge模式把功能交給WindowManagerImpl去實現了。
真的很複雜!
好了,羅裡羅嗦的,我們回到wm.addView(decor, l)。最終會由WindowManagerImpl來完成
addView操作,我們直接看它的實現好了。
程式碼在framework/base/core/java/android/view/WindowManagerImpl.java
[---->addView]
private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
{
ViewRoot root; //ViewRoot,我們的主人公終於登場!
synchronized (this) {
root = new ViewRoot(view.getContext());
root.mAddNesting = 1;
view.setLayoutParams(wparams);
if (mViews == null) {
index = 1;
mViews = new View[1];
mRoots = new ViewRoot[1];
mParams = new WindowManager.LayoutParams[1];
} else {
}
index--;
mViews[index] = view;
mRoots[index] = root;
mParams[index] = wparams;
}
root.setView(view, wparams, panelParentView);
}
ViewRoot是整個顯示系統中最為關鍵的東西,看起來這個東西好像和View有那麼點關係,其實它根本和View等UI關係不大,它不過是一個Handler罷了,唯一有關係的就是它其中有一個變數為Surface型別。我們看看它的定義。ViewRoot程式碼在
framework/base/core/java/android/view/ViewRoot.java中
public final class ViewRoot extends Handler implements ViewParent,
View.AttachInfo.Callbacks
{
private final Surface mSurface = new Surface();
}
它竟然從handler派生,而ViewParent不過定義了一些介面函式罷了。
看到Surface直覺上感到它和SurfaceFlinger有點關係。要不先去看看?
Surface程式碼在framework/base/core/java/android/view/Surface.java中,我們呼叫的是無參建構函式。
public Surface() {
mCanvas = new CompatibleCanvas(); //就是建立一個Canvas!
}
如果你有興趣的話,看看Surface其他建構函式,最終都會呼叫native的實現,而這些native的實現將和SurfaceFlinger建立關係,但我們這裡ViewRoot中的mSurface顯然還沒有到這一步。那它到底是怎麼和SurfaceFlinger搞上的呢?這一切待會就會水落石出的。
另外,為什麼ViewRoot是主人公呢?因為ViewRoot建立了客戶端和SystemServer的關係。我們看看它的建構函式。
public ViewRoot(Context context) {
super();
....
getWindowSession(context.getMainLooper());
}
getWindowsession將建立和WindowManagerService的關係。
ublic static IWindowSession getWindowSession(Looper mainLooper) {
synchronized (mStaticInit) {
if (!mInitialized) {
try {
//sWindowSession是通過Binder機制建立的。終於讓我們看到點希望了
InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
sWindowSession = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"))
.openSession(imm.getClient(), imm.getInputContext());
mInitialized = true;
} catch (RemoteException e) {
}
}
return sWindowSession;
}
}
上面跨Binder的程序呼叫另一端是WindowManagerService,程式碼在
framework/base/services/java/com/android/server/WindowManagerService.java中。我們先不說這個。
回過頭來看看ViewRoot接下來的呼叫。
[-->ViewRoot::setView()],這個函式很複雜,我們看其中關鍵幾句。
public void setView(View view, WindowManager.LayoutParams attrs,
View panelParentView) {
synchronized (this) {
requestLayout();
try {
res = sWindowSession.add(mWindow, mWindowAttributes,
getHostVisibility(), mAttachInfo.mContentInsets);
}
}
requestLayout實現很簡單,就是往handler中傳送了一個訊息。
public void requestLayout() {
checkThread();
mLayoutRequested = true;
scheduleTraversals(); //傳送DO_TRAVERSAL訊息
}
public void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
sendEmptyMessage(DO_TRAVERSAL);
}
}
我們看看跨程序的那個呼叫。sWindowSession.add。它的最終實現在WindowManagerService中。
[--->WindowSession::add()]
public int add(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, Rect outContentInsets) {
return addWindow(this, window, attrs, viewVisibility, outContentInsets);
}
WindowSession是個內部類,會呼叫外部類的addWindow
這個函式巨複雜無比,但是我們的核心目標是找到建立顯示相關的部分。所以,最後精簡的話就簡單了。
[--->WindowManagerService:: addWindow]
public int addWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int viewVisibility,
Rect outContentInsets) {
//建立一個WindowState,這個又是什麼玩意兒呢?
win = new WindowState(session, client, token,
attachedWindow, attrs, viewVisibility);
win.attach();
return res;
}
WindowState類中有一個和Surface相關的成員變數,叫SurfaceSession。它會在
attach函式中被建立。SurfaceSession嘛,就和SurfaceFlinger有關係了。我們待會看。
好,我們知道ViewRoot建立及呼叫add後,我們客戶端的View系統就和WindowManagerService建立了牢不可破的關係。
另外,我們知道ViewRoot是一個handler,而且剛才我們呼叫了requestLayout,所以接下來訊息迴圈下一個將呼叫的就是ViewRoot的handleMessage。
public void handleMessage(Message msg) {
switch (msg.what) {
case DO_TRAVERSAL:
performTraversals();
performTraversals更加複雜無比,經過我仔細挑選,目標鎖定為下面幾個函式。當然,後面我們還會回到performTraversals,不過我們現在更感興趣的是Surface是如何建立的。
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
boolean initialized = false;
boolean contentInsetsChanged = false;
boolean visibleInsetsChanged;
try {
//ViewRoot也有一個Surface成員變數,叫mSurface,這個就是代表SurfaceFlinger的客戶端
//ViewRoot在這個Surface上作畫,最後將由SurfaceFlinger來合成顯示。剛才說了mSurface還沒有什麼內容。
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
[---->ViewRoot:: relayoutWindow()]
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
//relayOut是跨程序呼叫,mSurface做為引數傳進去了,看來離真相越來越近了呀!
int relayoutResult = sWindowSession.relayout(
mWindow, params,
(int) (mView.mMeasuredWidth * appScale + 0.5f),
(int) (mView.mMeasuredHeight * appScale + 0.5f),
viewVisibility, insetsPending, mWinFrame,
mPendingContentInsets, mPendingVisibleInsets,
mPendingConfiguration, mSurface); mSurface做為引數傳進去了。
}
我們趕緊轉到WindowManagerService去看看吧。、
public int relayoutWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, boolean insetsPending,
Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,
Configuration outConfig, Surface outSurface){
.....
try {
//看到這裡,我內心一陣狂喜,有戲,太有戲了!
//其中win是我們最初建立的WindowState!
Surface surface = win.createSurfaceLocked();
if (surface != null) {
//先建立一個本地surface,然後把傳入的引數outSurface copyFrom一下
outSurface.copyFrom(surface);
win.mReportDestroySurface = false;
win.mSurfacePendingDestroy = false;
} else {
outSurface.release();
}
}
}
[--->WindowState::createSurfaceLocked]
Surface createSurfaceLocked() {
try {
mSurface = new Surface(
mSession.mSurfaceSession, mSession.mPid,
mAttrs.getTitle().toString(),
0, w, h, mAttrs.format, flags);
}
Surface.openTransaction();
這裡使用了Surface的另外一個建構函式。
public Surface(SurfaceSession s,
int pid, String name, int display, int w, int h, int format, int flags)
throws OutOfResourcesException {
mCanvas = new CompatibleCanvas();
init(s,pid,name,display,w,h,forma