Android深刻淺出之 Surface
一 目的
本節的目的就是爲了講清楚 Android 中的 Surface 系統,你們耳熟能詳的 SurfaceFlinger 究竟是
個什麼東西,它的工做流程又是怎樣的。固然,鑑於 SurfaceFlinger 的複雜性,咱們依然將採用
情景分析的辦法,找到合適的切入點。
一個 Activity 是怎麼在屏幕上顯示出來的呢?我將首先把這個說清楚。
接着咱們把其中的關鍵調用抽象在 Native 層,以這些函數調用爲切入點來研究 SurfaceFlinger 。
好了,開始咱們的征途吧。
二 Activity 是如何顯示的
最初的想法就是, Activity 得到一塊顯存,而後在上面繪圖,最後交給設備去顯示。這個道理是
沒錯,可是 Android 的 SurfaceFlinger 是在 System Server 進程中建立的, 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,format,flags); ----> 調用了 native 的 init 函數。
mName = name;
}
到這裏,不進入 JNI 是不可能說清楚了。不過咱們要先回顧下以前的關鍵步驟。
l add 中, new 了一個 SurfaceSession
l 建立 new 了一個 Surface
l 調用 copyFrom ,把本地 Surface 信息傳到 outSurface 中
JNI 層
上面兩個類的 JNI 實現都在 framework/base/core/jni/android_view_Surface.cpp 中。
[---->SurfaceSession:: SurfaceSession()]
public class SurfaceSession {
/** Create a new connection with the surface flinger. */
public SurfaceSession() {
init();
}
它的 init 函數對應爲:
[--->SurfaceSession_init]
static void SurfaceSession_init(JNIEnv* env, jobject clazz)
{
//SurfaceSession 對應爲 SurfaceComposerClient
sp<SurfaceComposerClient> client = new SurfaceComposerClient;
client->incStrong(clazz);
//Google 經常使用作法,在 JAVA 對象中保存 C++ 對象的指針。
env->SetIntField(clazz, sso.client, (int)client.get());
}
Surface 的 init 對應爲:
[--->Surface_init]
static void Surface_init(
JNIEnv* env, jobject clazz,
jobject session,
jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)
{
SurfaceComposerClient* client =
(SurfaceComposerClient*)env->GetIntField(session, sso.client);
sp<SurfaceControl> surface;
if (jname == NULL) {
//client 是 SurfaceComposerClient ,返回的 surface 是一個 SurfaceControl
// 真得很複雜!
surface = client->createSurface(pid, dpy, w, h, format, flags);
} else {
const jchar* str = env->GetStringCritical(jname, 0);
const String8 name(str, env->GetStringLength(jname));
env->ReleaseStringCritical(jname, str);
surface = client->createSurface(pid, name, dpy, w, h, format, flags);
}
// 把 surfaceControl 信息設置到 Surface 對象中
setSurfaceControl(env, clazz, surface);
}
static void setSurfaceControl(JNIEnv* env, jobject clazz,
const sp<SurfaceControl>& surface)
{
SurfaceControl* const p =
(SurfaceControl*)env->GetIntField(clazz, so.surfaceControl);
if (surface.get()) {
surface->incStrong(clazz);
}
if (p) {
p->decStrong(clazz);
}
env->SetIntField(clazz, so.surfaceControl, (int)surface.get());
}
[--->Surface_copyFrom]
static void Surface_copyFrom(
JNIEnv* env, jobject clazz, jobject other)
{
const sp<SurfaceControl>& surface = getSurfaceControl(env, clazz);
const sp<SurfaceControl>& rhs = getSurfaceControl(env, other);
if (!SurfaceControl::isSameSurface(surface, rhs)) {
setSurfaceControl(env, clazz, rhs);
// 把本地那個 surface 的 surfaceControl 對象轉移到 outSurface 上
}
}
這裏僅僅是 surfaceControl 的轉移,可是並無看到 Surface 相關的信息。
那麼 Surface 在哪裏建立的呢?爲了解釋這個問題,我使用了終極武器, aidl 。
1 終極武器 AIDL
aidl 能夠把 XXX.aidl 文件轉換成對應的 java 文件。咱們剛纔調用的是 WindowSession 的
relayOut 函數。以下:
sWindowSession.relayout(
mWindow, params,
(int) (mView.mMeasuredWidth * appScale + 0.5f),
(int) (mView.mMeasuredHeight * appScale + 0.5f),
viewVisibility, insetsPending, mWinFrame,
mPendingContentInsets, mPendingVisibleInsets,
mPendingConfiguration, mSurface);
它的 aidl 文件在 framework/base/core/java/android/view/IWindowSession.aidl 中
interface IWindowSession {
int add(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, out Rect outContentInsets);
void remove(IWindow window);
// 注意喔,這個 outSurface 前面的是 out ,表示輸出參數,這個相似於 C++ 的引用。
int relayout(IWindow window, in WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility,
boolean insetsPending, out Rect outFrame, out Rect outContentInsets,
out Rect outVisibleInsets, out Configuration outConfig,
out Surface outSurface);
剛纔說了, JNI 及其 JAVA 調用只是 copyFrom 了 SurfaceControl 對象到 outSurface 中,可是沒
看到哪裏建立 Surface 。這其中的奧祕就在 aidl 文件編譯後生成的 java 文件中。
你在命令行下能夠輸入:
aidl -Id:/android-2.2-froyo-20100625-source/source/frameworks/base/core/java/ -Id:/android-2.2-froyo-20100625-source/source/frameworks/base/Graphics/java d:/android-2.2-froyo-20100625-source/source/frameworks/base/core/java/android/view/IWindowSession.aidl test.java
以生成 test.java 文件。 -I 參數指定 include 目錄,例如 aidl 有些參數是在別的 java 文件中指定
的,那麼這個 -I 就須要把這些目錄包含進來。
先看看 ViewRoot 這個客戶端生成的代碼是什麼。
public int relayout(
android.view.IWindow window,
android.view.WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight,
int viewVisibility, boolean insetsPending,
android.graphics.Rect outFrame,
android.graphics.Rect outContentInsets,
android.graphics.Rect outVisibleInsets,
android.content.res.Configuration outConfig,
android.view.Surface outSurface) ---->outSurface 是第 11 個參數
throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((window!=null))?(window.asBinder()):(null)));
if ((attrs!=null)) {
_data.writeInt(1);
attrs.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
_data.writeInt(requestedWidth);
_data.writeInt(requestedHeight);
_data.writeInt(viewVisibility);
_data.writeInt(((insetsPending)?(1):(0)));
// 奇怪, outSurface 的信息沒有寫到 _data 中。那 .....
mRemote.transact(Stub.TRANSACTION_relayout, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
if ((0!=_reply.readInt())) {
outFrame.readFromParcel(_reply);
}
....
if ((0!=_reply.readInt())) {
outSurface.readFromParcel(_reply); // 從 Parcel 中讀取信息來填充 outSurface
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
真奇怪啊, Binder 客戶端這頭居然沒有把 outSurface 的信息發過去。咱們趕忙看看服務端。
服務端這邊處理是在 onTranscat 函數中。
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel
reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case TRANSACTION_relayout:
{
data.enforceInterface(DESCRIPTOR);
android.view.IWindow _arg0;
android.view.Surface _arg10;
// 剛纔說了, Surface 信息並無傳過來,那麼咱們在 relayOut 中看到的 outSurface 是怎
麼
// 出來的呢?看下面這句,原來在服務端這邊居然 new 了一個新的 Surface !!!
_arg10 = new android.view.Surface();
int _result = this.relayout(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8,
_arg9, _arg10);
reply.writeNoException();
reply.writeInt(_result);
//_arg10 是 copyFrom 了,那怎麼傳到客戶端呢?
if ((_arg10!=null)) {
reply.writeInt(1);// 調用 Surface 的 writeToParcel ,把信息加入 reply
_arg10.writeToParcel(reply,
android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
return true;
}
太詭異了!居然有這麼多花花腸子。我相信若是沒有 aidl 的幫助,我不管如何也不會知道這其中
的奧妙。
那好,咱們的流程明白了。
l 客戶端雖然傳了一個 surface ,但其實沒傳遞給服務端
l 服務端調用 writeToParcel ,把信息寫到 Parcel 中,而後數據傳回客戶端
l 客戶端調用 Surface 的 readFromParcel ,得到 surface 信息。
那就去看看 writeToParcel 吧。
[---->Surface_writeToParcel]
static void Surface_writeToParcel(
JNIEnv* env, jobject clazz, jobject argParcel, jint flags)
{
Parcel* parcel = (Parcel*)env->GetIntField(
argParcel, no.native_parcel);
const sp<SurfaceControl>& control(getSurfaceControl(env, clazz));
// 還好,只是把數據序列化到 Parcel 中
SurfaceControl::writeSurfaceToParcel(control, parcel);
if (flags & PARCELABLE_WRITE_RETURN_VALUE) {
setSurfaceControl(env, clazz, 0);
}
}
那看看客戶端的 Surface_readFromParcel 吧。
[----->Surface_readFromParcel]
static void Surface_readFromParcel(
JNIEnv* env, jobject clazz, jobject argParcel)
{
Parcel* parcel = (Parcel*)env->GetIntField( argParcel, no.native_parcel);
// 客戶端這邊尚未 surface 呢
const sp<Surface>& control(getSurface(env, clazz));
// 不過咱們看到但願了,根據服務端那邊 Parcel 信息來構造一個新的 surface
sp<Surface> rhs = new Surface(*parcel);
if (!Surface::isSameSurface(control, rhs)) {
setSurface(env, clazz, rhs); // 把這個新 surface 賦給客戶端。終於咱們有了 surface !
}
}
到此,咱們終於七拐八繞的獲得了 surface ,這其中經歷太多曲折了。下一節,咱們將精簡這其
中複雜的操做,統一歸到 Native 層,以這樣爲切入點來了解 Surface 的工做流程和原理。
好,反正你知道 ViewRoot 調用了 relayout 後, Surface 就真正從 WindowManagerService 那得
到了。繼續回到 ViewRoot ,其中還有一個重要地方是咱們知道卻不瞭解的。
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 {
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
// relayoutWindow 完後,咱們獲得了一個無比寶貴的 Surface
// 那咱們畫界面的地方在哪裏?就在這個函數中,離 relayoutWindow 不遠處。
....
boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();
if (!cancelDraw && !newSurface) {
mFullRedrawNeeded = false;
draw(fullRedrawNeeded); //draw?draw 什麼呀?
}
[--->ViewRoot::draw()]
private void draw(boolean fullRedrawNeeded) {
Surface surface = mSurface; // 嘿嘿,不擔憂了, surface 資源都齊全了
if (surface == null || !surface.isValid()) {
return;
}
if (mAttachInfo.mViewScrollChanged) {
mAttachInfo.mViewScrollChanged = false;
mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
}
int yoff;
final boolean scrolling = mScroller != null && mScroller.computeScrollOffset();
if (scrolling) {
yoff = mScroller.getCurrY();
} else {
yoff = mScrollY;
}
if (mCurScrollY != yoff) {
mCurScrollY = yoff;
fullRedrawNeeded = true;
}
float appScale = mAttachInfo.mApplicationScale;
boolean scalingRequired = mAttachInfo.mScalingRequired;
Rect dirty = mDirty;
if (mUseGL) { // 咱們不用 OPENGL
...
}
Canvas canvas;
try {
int left = dirty.left;
int top = dirty.top;
int right = dirty.right;
int bottom = dirty.bottom;
// 從 Surface 中鎖定一塊區域,這塊區域是咱們認爲的須要重繪的區域
canvas = surface.lockCanvas(dirty);
// TODO: Do this in native
canvas.setDensity(mDensity);
}
try {
if (!dirty.isEmpty() || mIsAnimating) {
long startTime = 0L;
try {
canvas.translate(0, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired
? DisplayMetrics.DENSITY_DEVICE : 0);
//mView 就是以前的 decoreView,
mView.draw(canvas);
}
} finally {
// 咱們的圖畫完了,告訴 surface 釋放這塊區域
surface.unlockCanvasAndPost(canvas);
}
if (scrolling) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
}
看起來,這個 surface 的用法很簡單嘛:
l lockSurface ,獲得一個畫布 Canvas
l 調用 View 的 draw ,讓他們在這個 Canvas 上盡情繪圖才。另外,這個 View 會調用全部它的子 View 來畫圖,最終會進入到 View
的 onDraw 函數中,在這裏咱們能夠作定製化的界面美化工做。固然,若是你想定製化整個系統畫圖的話,徹底能夠把
performTranvsal 看懂,而後再修改。
l unlockCanvasAndPost ,告訴 Surface 釋放這塊畫布
固然,這幾個重要函數調用幹了具體的活。這些重要函數,咱們最終會精簡到 Native 層的。
2 總結
到這裏,你應該知道了一個 Activity 中,調用 setContentView 後它如何從系統中獲取一塊
Surface ,以及它是如何使用這個 Surface 的了。不得不說,關於 UI 這塊, Android 絕對是夠復
雜的。難怪 2.3 把 UI 這塊代碼基本重寫一遍,但願可以簡單精煉點。 java