深入浅出 详解Android Surface系统

移动开发 Android
本文详细介绍了Android中的Surface系统,采用情景分析的办法,详解了何为SurfaceFlinger,以及SurfaceFlinger的工作流程,以Activity函数调用为切入点来研究SurfaceFlinger。

一 目的

本篇文章的目的就是为了讲清楚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  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

 

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); //这个很关键。  
 
                } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.

 

上面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();  
 
}  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

 

installDecor将创建mDecor和mContentParent。mDecor是DecorView类型,ContentParent是ViewGroup类型

private void installDecor() {  
 
if (mDecor == null) {  
 
mDecor = generateDecor();  
 
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);  
 
mDecor.setIsRootNamespace(true);  
 
}  
 
if (mContentParent == null) {  
 
mContentParent = generateLayout(mDecor); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

 

那么,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);  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

 

你看见没,分析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);  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.

 

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();  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

 

它竟然从handler派生,而ViewParent不过定义了一些接口函数罢了。

看到Surface直觉上感到它和SurfaceFlinger有点关系。要不先去看看?

Surface代码在framework/base/core/java/android/view/Surface.java中,我们调用的是无参构造函数。

public Surface() {  
 
mCanvas = new CompatibleCanvas(); //就是创建一个Canvas!  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

 

如果你有兴趣的话,看看Surface其他构造函数,最终都会调用native的实现,而这些native的实现将和SurfaceFlinger建立关系,但我们这里ViewRoot中的mSurface显然还没有到这一步。那它到底是怎么和SurfaceFlinger搞上的呢?这一切待会就会水落石出的。

另外,为什么ViewRoot是主人公呢?因为ViewRoot建立了客户端和SystemServer的关系。我们看看它的构造函数。

public ViewRoot(Context context) {  
 
super();  
 
....  
 
getWindowSession(context.getMainLooper());  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

 

getWindowsession将建立和WindowManagerService的关系。

public 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;  
 
        }  
 
    } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.

 

上面跨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);  
 
                }   
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

 

requestLayout实现很简单,就是往handler中发送了一个消息。

public void requestLayout() {  
 
        checkThread();  
 
        mLayoutRequested = true;  
 
        scheduleTraversals(); //发送DO_TRAVERSAL消息  
 
}   
 
public void scheduleTraversals() {  
 
        if (!mTraversalScheduled) {  
 
            mTraversalScheduled = true;  
 
            sendEmptyMessage(DO_TRAVERSAL);  
 
        }  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

 

我们看看跨进程的那个调用。sWindowSession.add。它的最终实现在WindowManagerService中。

[--->WindowSession::add()]

public int add(IWindow window, WindowManager.LayoutParams attrs,  
 
                int viewVisibility, Rect outContentInsets) {  
 
            return addWindow(this, window, attrs, viewVisibility, outContentInsets);  
 
        } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

 

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;  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

 

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(); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

 

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); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

 

[---->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做为参数传进去了。  
 
       } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

 

我们赶紧转到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();  
 
                    }  
 
                }  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.

 

[--->WindowState::createSurfaceLocked]

Surface createSurfaceLocked() {  
 
            
 
                try {  
 
                    mSurface = new Surface(  
 
                            mSession.mSurfaceSession, mSession.mPid,  
 
                            mAttrs.getTitle().toString(),  
 
                            0, w, h, mAttrs.format, flags);  
 
                  }   
 
                Surface.openTransaction(); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

 

这里使用了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;  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

 

到这里,不进入JNI是不可能说清楚了。不过我们要先回顾下之前的关键步骤。

◆ add中,new了一个SurfaceSession

◆创建new了一个Surface

◆调用copyFrom,把本地Surface信息传到outSurface中#p#

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();  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

 

它的init函数对应为:

[--->SurfaceSession_init]

static void SurfaceSession_init(JNIEnv* env, jobject clazz)  
 
{  
 
//SurfaceSession对应为SurfaceComposerClient  
 
sp client = new SurfaceComposerClient;  
 
client->incStrong(clazz);  
 
//Google常用做法,在JAVA对象中保存C++对象的指针。  
 
env->SetIntField(clazz, sso.client, (int)client.get());  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

 

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 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);  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.

 

 

static void setSurfaceControl(JNIEnv* env, jobject clazz,  
 
const sp& 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());  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

 

[--->Surface_copyFrom]

 

static void Surface_copyFrom(  
 
JNIEnv* env, jobject clazz, jobject other)  
 
{  
 
const sp& surface = getSurfaceControl(env, clazz);  
 
const sp& rhs = getSurfaceControl(env, other);  
 
if (!SurfaceControl::isSameSurface(surface, rhs)) {  
 
setSurfaceControl(env, clazz, rhs);  
 
//把本地那个surface的surfaceControl对象转移到outSurface上  
 
}  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

 

这里仅仅是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); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

 

它的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); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

 

刚才说了,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;  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.

 

真奇怪啊,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;  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.

 

太诡异了!竟然有这么多花花肠子。我相信如果没有aidl的帮助,我无论如何也不会知道这其中的奥妙。

那好,我们的流程明白了。

◆客户端虽然传了一个surface,但其实没传递给服务端

◆服务端调用writeToParcel,把信息写到Parcel中,然后数据传回客户端

◆客户端调用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& control(getSurfaceControl(env, clazz));  
 
//还好,只是把数据序列化到Parcel中  
 
SurfaceControl::writeSurfaceToParcel(control, parcel);  
 
if (flags & PARCELABLE_WRITE_RETURN_VALUE) {  
 
setSurfaceControl(env, clazz, 0);  
 
}  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

 

那看看客户端的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& control(getSurface(env, clazz));  
 
//不过我们看到希望了,根据服务端那边Parcel信息来构造一个新的surface  
 
sp rhs = new Surface(*parcel);  
 
if (!Surface::isSameSurface(control, rhs)) {  
 
setSurface(env, clazz, rhs); //把这个新surface赋给客户端。终于我们有了surface!  
 
}  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

 

到此,我们终于七拐八绕的得到了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什么呀?  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.

 

[--->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();  
 
}  
 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.

 

看起来,这个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这块代码基本重写一遍,希望能够简单精炼点。

【编辑推荐】

  1. Android开发:自定义GridView/ListView数据源
  2. Android自定义标题栏:显示网页加载进度
  3. Android应用开发教程:两个运行的Activity之间的通信
  4. Android学习笔记:Activity跳转

 

责任编辑:佚名 来源: CSDN
相关推荐

2011-07-04 10:39:57

Web

2021-03-16 08:54:35

AQSAbstractQueJava

2013-11-14 15:53:53

AndroidAudioAudioFlinge

2022-09-26 09:01:15

语言数据JavaScript

2012-05-21 10:06:26

FrameworkCocoa

2019-01-07 15:29:07

HadoopYarn架构调度器

2017-07-02 18:04:53

块加密算法AES算法

2021-07-20 15:20:02

FlatBuffers阿里云Java

2012-02-07 15:16:01

Android核心组件Service

2012-02-07 15:29:17

Android核心组件Service

2012-02-07 14:45:52

Android核心组件Service

2012-02-07 14:37:01

Android核心组件Service

2022-01-13 09:38:25

Android架构设计

2012-02-07 15:09:03

Android核心组件Service

2016-10-14 14:32:58

JavascriptDOMWeb

2016-10-14 13:53:05

JavascriptDOMWeb

2023-12-04 13:22:00

JavaScript异步编程

2021-07-19 11:54:15

MySQL优先队列

2009-11-17 17:31:58

Oracle COMM

2010-07-16 09:11:40

JavaScript内存泄漏
点赞
收藏

51CTO技术栈公众号