详解Android Surface系统

Android 进阶——图形系统的另一种“画布” Surface 详解 Surface 是Android系统中真正的画布,Activity上的所有UI都是在Surface 上完成绘制的,每一个Surface 对象都在SurfaceFlinger中有对应的图层(Layer),SurfaceFlinger 负责把这些Layer按需混合处理后输出到Frame Buffer中,再由Display设备(屏幕或显示器)把Frame Buffer里的数据呈现到屏幕上。 阅读详情

一 目的

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

  1. private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) {  
  2.  
  3.       Activity a = performLaunchActivity(r, customIntent);  
  4.  
  5.    
  6.  
  7.         if (a != null) {  
  8.  
  9.             r.createdConfig = new Configuration(mConfiguration);  
  10.  
  11.             Bundle oldState = r.state;  
  12.  
  13.             handleResumeActivity(r.token, false, r.isForward);  
  14.  
  15. ---->调用handleResumeActivity  
  16.  
  17. } 

handleLaunchActivity中会调用handleResumeActivity。

[--->ActivityThread:: handleResumeActivity]

  1. final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) {  
  2.  
  3.          boolean willBeVisible = !a.mStartedActivity;  
  4.  
  5.              
  6.  
  7. if (r.window == null && !a.mFinished && willBeVisible) {  
  8.  
  9.                 r.window = r.activity.getWindow();  
  10.  
  11.                 View decor = r.window.getDecorView();  
  12.  
  13.                 decor.setVisibility(View.INVISIBLE);  
  14.  
  15.                 ViewManager wm = a.getWindowManager();  
  16.  
  17.                 WindowManager.LayoutParams l = r.window.getAttributes();  
  18.  
  19.                 a.mDecor = decor;  
  20.  
  21.                 l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;  
  22.  
  23.                 if (a.mVisibleFromClient) {  
  24.  
  25.                     a.mWindowAdded = true;  
  26.  
  27.                     wm.addView(decor, l); //这个很关键。  
  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]

  1. public void addContentView(View view, ViewGroup.LayoutParams params) {  
  2.  
  3. if (mContentParent == null) { //刚创建的时候mContentParent为空  
  4.  
  5. installDecor();  
  6.  
  7. }  
  8.  
  9. mContentParent.addView(view, params);  
  10.  
  11. final Callback cb = getCallback();  
  12.  
  13. if (cb != null) {  
  14.  
  15. cb.onContentChanged();  
  16.  
  17. }  
  18.  
  19. } 

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

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

那么,ViewManager wm = a.getWindowManager()又返回什么呢?

PhoneWindow从Window中派生,Acitivity创建的时候会调用它的setWindowManager。而这个函数由Window类实现。

代码在framework/base/core/java/android/view/Window.java中:

  1. public void setWindowManager(WindowManager wm,IBinder appToken, String appName) {  
  2.  
  3. mAppToken = appToken;  
  4.  
  5. mAppName = appName;  
  6.  
  7. if (wm == null) {  
  8.  
  9. wm = WindowManagerImpl.getDefault();  
  10.  
  11. }  
  12.  
  13. mWindowManager = new LocalWindowManager(wm);  
  14.  
  15. } 

你看见没,分析JAVA代码这个东西真的很复杂。mWindowManager的实现是LocalWindowManager,但由通过Bridge模式把功能交给WindowManagerImpl去实现了。

真的很复杂!

好了,我们回到wm.addView(decor, l)。最终会由WindowManagerImpl来完成addView操作,我们直接看它的实现好了。

代码在framework/base/core/java/android/view/WindowManagerImpl.java:

[---->addView]

  1. private void addView(View view, ViewGroup.LayoutParams params, boolean nest)  
  2.  
  3. {  
  4.  
  5. ViewRoot root; //ViewRoot,我们的主人公终于登场!  
  6.  
  7. synchronized (this) {  
  8.  
  9. root = new ViewRoot(view.getContext());  
  10.  
  11. root.mAddNesting = 1;  
  12.  
  13. view.setLayoutParams(wparams);  
  14.  
  15. if (mViews == null) {  
  16.  
  17. index = 1;  
  18.  
  19. mViews = new View[1];  
  20.  
  21. mRoots = new ViewRoot[1];  
  22.  
  23. mParams = new WindowManager.LayoutParams[1];  
  24.  
  25. } else {  
  26.  
  27. }  
  28.  
  29. index--;  
  30.  
  31. mViews[index] = view;  
  32.  
  33. mRoots[index] = root;  
  34.  
  35. mParams[index] = wparams;  
  36.  
  37. }  
  38.  
  39. root.setView(view, wparams, panelParentView);  
  40.  
  41. } 

ViewRoot是整个显示系统中最为关键的东西,看起来这个东西好像和View有那么点关系,其实它根本和View等UI关系不大,它不过是一个Handler罢了,唯一有关系的就是它其中有一个变量为Surface类型。我们看看它的定义。ViewRoot代码在framework/base/core/java/android/view/ViewRoot.java中:

  1. public final class ViewRoot extends Handler implements ViewParent,  
  2.  
  3. View.AttachInfo.Callbacks  
  4.  
  5. {  
  6.  
  7. private final Surface mSurface = new Surface();  
  8.  
  9. } 

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

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

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

  1. public Surface() {  
  2.  
  3. mCanvas = new CompatibleCanvas(); //就是创建一个Canvas!  
  4.  
  5. } 

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

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

  1. public ViewRoot(Context context) {  
  2.  
  3. super();  
  4.  
  5. ....  
  6.  
  7. getWindowSession(context.getMainLooper());  
  8.  
  9. } 

getWindowsession将建立和WindowManagerService的关系。

  1. public static IWindowSession getWindowSession(Looper mainLooper) {  
  2.  
  3.         synchronized (mStaticInit) {  
  4.  
  5.             if (!mInitialized) {  
  6.  
  7.                 try {  
  8.  
  9.                 //sWindowSession是通过Binder机制创建的。终于让我们看到点希望了  
  10.  
  11.                     InputMethodManager imm = InputMethodManager.getInstance(mainLooper);  
  12.  
  13.                     sWindowSession = IWindowManager.Stub.asInterface(  
  14.  
  15.                             ServiceManager.getService("window"))  
  16.  
  17.                             .openSession(imm.getClient(), imm.getInputContext());  
  18.  
  19.                     mInitialized = true;  
  20.  
  21.                 } catch (RemoteException e) {  
  22.  
  23.                 }  
  24.  
  25.             }  
  26.  
  27.             return sWindowSession;  
  28.  
  29.         }  
  30.  
  31.     } 

上面跨Binder的进程调用另一端是WindowManagerService,代码在framework/base/services/java/com/android/server/WindowManagerService.java中。我们先不说这个。

回过头来看看ViewRoot接下来的调用。

[-->ViewRoot::setView()],这个函数很复杂,我们看其中关键几句。

  1. public void setView(View view, WindowManager.LayoutParams attrs,  
  2.  
  3.             View panelParentView) {  
  4.  
  5.         synchronized (this) {  
  6.  
  7.             requestLayout();   
  8.  
  9.                 try {  
  10.  
  11.                     res = sWindowSession.add(mWindow, mWindowAttributes,  
  12.  
  13.                             getHostVisibility(), mAttachInfo.mContentInsets);  
  14.  
  15.                 }   
  16.  
  17. } 

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

  1. public void requestLayout() {  
  2.  
  3.         checkThread();  
  4.  
  5.         mLayoutRequested = true;  
  6.  
  7.         scheduleTraversals(); //发送DO_TRAVERSAL消息  
  8.  
  9. }   
  10.  
  11. public void scheduleTraversals() {  
  12.  
  13.         if (!mTraversalScheduled) {  
  14.  
  15.             mTraversalScheduled = true;  
  16.  
  17.             sendEmptyMessage(DO_TRAVERSAL);  
  18.  
  19.         }  
  20.  
  21. } 

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

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

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

WindowSession是个内部类,会调用外部类的addWindow。

这个函数巨复杂无比,但是我们的核心目标是找到创建显示相关的部分。所以,最后精简的话就简单了。

[--->WindowManagerService:: addWindow]

  1. public int addWindow(Session session, IWindow client,  
  2.  
  3.             WindowManager.LayoutParams attrs, int viewVisibility,  
  4.  
  5.             Rect outContentInsets) {  
  6.  
  7.         //创建一个WindowState,这个又是什么玩意儿呢?  
  8.  
  9.               win = new WindowState(session, client, token,  
  10.  
  11.                     attachedWindow, attrs, viewVisibility);  
  12.  
  13.            win.attach();  
  14.  
  15.            return res;  
  16.  
  17. } 

WindowState类中有一个和Surface相关的成员变量,叫SurfaceSession。它会在attach函数中被创建。SurfaceSession嘛,就和SurfaceFlinger有关系了。我们待会看。

好,我们知道ViewRoot创建及调用add后,我们客户端的View系统就和WindowManagerService建立了牢不可破的关系。

另外,我们知道ViewRoot是一个handler,而且刚才我们调用了requestLayout,所以接下来消息循环下一个将调用的就是ViewRoot的handleMessage。

  1. public void handleMessage(Message msg) {  
  2.  
  3.         switch (msg.what) {  
  4.  
  5.        case DO_TRAVERSAL:  
  6.  
  7.             performTraversals(); 

performTraversals更加复杂无比,经过我仔细挑选,目标锁定为下面几个函数。当然,后面我们还会回到performTraversals,不过我们现在更感兴趣的是Surface是如何创建的。

  1. private void performTraversals() {  
  2.  
  3.         // cache mView since it is used so much below...  
  4.  
  5.         final View host = mView;  
  6.  
  7.    
  8.  
  9.          boolean initialized = false;  
  10.  
  11.             boolean contentInsetsChanged = false;  
  12.  
  13.             boolean visibleInsetsChanged;  
  14.  
  15.             try {  
  16.  
  17. //ViewRoot也有一个Surface成员变量,叫mSurface,这个就是代表SurfaceFlinger的客户端  
  18.  
  19. //ViewRoot在这个Surface上作画,最后将由SurfaceFlinger来合成显示。刚才说了mSurface还没有什么内容。  
  20.  
  21.           relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); 

[---->ViewRoot:: relayoutWindow()]

  1. private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,  
  2.  
  3.             boolean insetsPending) throws RemoteException {  
  4.  
  5.          
  6.  
  7. //relayOut是跨进程调用,mSurface做为参数传进去了,看来离真相越来越近了呀!  
  8.  
  9.         int relayoutResult = sWindowSession.relayout(  
  10.  
  11.                 mWindow, params,  
  12.  
  13.                 (int) (mView.mMeasuredWidth * appScale + 0.5f),  
  14.  
  15.                 (int) (mView.mMeasuredHeight * appScale + 0.5f),  
  16.  
  17.                 viewVisibility, insetsPending, mWinFrame,  
  18.  
  19.                 mPendingContentInsets, mPendingVisibleInsets,  
  20.  
  21.                 mPendingConfiguration, mSurface); mSurface做为参数传进去了。  
  22.  
  23.        } 

我们赶紧转到WindowManagerService去看看吧。

  1. public int relayoutWindow(Session session, IWindow client,  
  2.  
  3.             WindowManager.LayoutParams attrs, int requestedWidth,  
  4.  
  5.             int requestedHeight, int viewVisibility, boolean insetsPending,  
  6.  
  7.             Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,  
  8.  
  9.             Configuration outConfig, Surface outSurface){  
  10.  
  11.                .....  
  12.  
  13.          try {  
  14.  
  15.            //看到这里,我内心一阵狂喜,有戏,太有戏了!  
  16.  
  17.          //其中win是我们最初创建的WindowState!  
  18.  
  19.                     Surface surface = win.createSurfaceLocked();  
  20.  
  21.                     if (surface != null) {  
  22.  
  23.                   //先创建一个本地surface,然后把传入的参数outSurface copyFrom一下  
  24.  
  25.                         outSurface.copyFrom(surface);  
  26.  
  27.                         win.mReportDestroySurface = false;  
  28.  
  29.                         win.mSurfacePendingDestroy = false;  
  30.  
  31.                        } else {  
  32.  
  33.                        outSurface.release();  
  34.  
  35.                     }  
  36.  
  37.                 }  
  38.  
  39. } 

[--->WindowState::createSurfaceLocked]

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

这里使用了Surface的另外一个构造函数。

  1.     public Surface(SurfaceSession s,  
  2.  
  3.             int pid, String name, int display, int w, int h, int format, int flags)  
  4.  
  5.         throws OutOfResourcesException {  
  6.  
  7.         mCanvas = new CompatibleCanvas();  
  8.  
  9.         init(s,pid,name,display,w,h,format,flags); ---->调用了native的init函数。  
  10.  
  11.         mName = name;  
  12.  
  13. } 

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

◆ add中,new了一个SurfaceSession

◆创建new了一个Surface

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

JNI层

上面两个类的JNI实现都在framework/base/core/jni/android_view_Surface.cpp中。

[---->SurfaceSession:: SurfaceSession()]

  1. public class SurfaceSession {  
  2.  
  3. /** Create a new connection with the surface flinger. */ 
  4.  
  5. public SurfaceSession() {  
  6.  
  7. init();  
  8.  
  9. } 

它的init函数对应为:

[--->SurfaceSession_init]

  1. static void SurfaceSession_init(JNIEnv* env, jobject clazz)  
  2.  
  3. {  
  4.  
  5. //SurfaceSession对应为SurfaceComposerClient  
  6.  
  7. sp client = new SurfaceComposerClient;  
  8.  
  9. client->incStrong(clazz);  
  10.  
  11. //Google常用做法,在JAVA对象中保存C++对象的指针。  
  12.  
  13. env->SetIntField(clazz, sso.client, (int)client.get());  
  14.  
  15. } 

Surface的init对应为:

[--->Surface_init]

  1. static void Surface_init(  
  2.  
  3. JNIEnv* env, jobject clazz,  
  4.  
  5. jobject session,  
  6.  
  7. jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)  
  8.  
  9. {  
  10.  
  11. SurfaceComposerClient* client =  
  12.  
  13. (SurfaceComposerClient*)env->GetIntField(session, sso.client);  
  14.  
  15. sp surface;  
  16.  
  17. if (jname == NULL) {  
  18.  
  19. //client是SurfaceComposerClient,返回的surface是一个SurfaceControl  
  20.  
  21. //真得很复杂!  
  22.  
  23. surface = client->createSurface(pid, dpy, w, h, format, flags);  
  24.  
  25. } else {  
  26.  
  27. const jchar* str = env->GetStringCritical(jname, 0);  
  28.  
  29. const String8 name(str, env->GetStringLength(jname));  
  30.  
  31. env->ReleaseStringCritical(jname, str);  
  32.  
  33. surface = client->createSurface(pid, name, dpy, w, h, format, flags);  
  34.  
  35. }  
  36.  
  37. //把surfaceControl信息设置到Surface对象中  
  38.  
  39. setSurfaceControl(env, clazz, surface);  
  40.  
  41. } 

  1. static void setSurfaceControl(JNIEnv* env, jobject clazz,  
  2.  
  3. const sp& surface)  
  4.  
  5. {  
  6.  
  7. SurfaceControl* const p =  
  8.  
  9. (SurfaceControl*)env->GetIntField(clazz, so.surfaceControl);  
  10.  
  11. if (surface.get()) {  
  12.  
  13. surface->incStrong(clazz);  
  14.  
  15. }  
  16.  
  17. if (p) {  
  18.  
  19. p->decStrong(clazz);  
  20.  
  21. }  
  22.  
  23. env->SetIntField(clazz, so.surfaceControl, (int)surface.get());  
  24.  
  25. } 

[--->Surface_copyFrom]

  1. static void Surface_copyFrom(  
  2.  
  3. JNIEnv* env, jobject clazz, jobject other)  
  4.  
  5. {  
  6.  
  7. const sp& surface = getSurfaceControl(env, clazz);  
  8.  
  9. const sp& rhs = getSurfaceControl(env, other);  
  10.  
  11. if (!SurfaceControl::isSameSurface(surface, rhs)) {  
  12.  
  13. setSurfaceControl(env, clazz, rhs);  
  14.  
  15. //把本地那个surface的surfaceControl对象转移到outSurface上  
  16.  
  17. }  
  18.  
  19. } 

这里仅仅是surfaceControl的转移,但是并没有看到Surface相关的信息。

那么Surface在哪里创建的呢?为了解释这个问题,我使用了终极武器,aidl。

1 终极武器AIDL

aidl可以把XXX.aidl文件转换成对应的java文件。我们刚才调用的是WindowSession的

relayOut函数。如下:

  1. sWindowSession.relayout(  
  2.  
  3. mWindow, params,  
  4.  
  5. (int) (mView.mMeasuredWidth * appScale + 0.5f),  
  6.  
  7. (int) (mView.mMeasuredHeight * appScale + 0.5f),  
  8.  
  9. viewVisibility, insetsPending, mWinFrame,  
  10.  
  11. mPendingContentInsets, mPendingVisibleInsets,  
  12.  
  13. mPendingConfiguration, mSurface); 

它的aidl文件在framework/base/core/java/android/view/IWindowSession.aidl中

  1. interface IWindowSession {  
  2.  
  3. int add(IWindow window, in WindowManager.LayoutParams attrs,  
  4.  
  5. in int viewVisibility, out Rect outContentInsets);  
  6.  
  7. void remove(IWindow window);  
  8.  
  9. //注意喔,这个outSurface前面的是out,表示输出参数,这个类似于C++的引用。  
  10.  
  11. int relayout(IWindow window, in WindowManager.LayoutParams attrs,  
  12.  
  13. int requestedWidth, int requestedHeight, int viewVisibility,  
  14.  
  15. boolean insetsPending, out Rect outFrame, out Rect outContentInsets,  
  16.  
  17. out Rect outVisibleInsets, out Configuration outConfig,  
  18.  
  19. 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这个客户端生成的代码是什么。

  1. public int relayout(  
  2.  
  3. android.view.IWindow window,  
  4.  
  5. android.view.WindowManager.LayoutParams attrs,  
  6.  
  7. int requestedWidth, int requestedHeight,  
  8.  
  9. int viewVisibility, boolean insetsPending,  
  10.  
  11. android.graphics.Rect outFrame,  
  12.  
  13. android.graphics.Rect outContentInsets,  
  14.  
  15. android.graphics.Rect outVisibleInsets,  
  16.  
  17. android.content.res.Configuration outConfig,  
  18.  
  19. android.view.Surface outSurface) ---->outSurface是第11个参数  
  20.  
  21. throws android.os.RemoteException  
  22.  
  23. {  
  24.  
  25. android.os.Parcel _data = android.os.Parcel.obtain();  
  26.  
  27. android.os.Parcel _reply = android.os.Parcel.obtain();  
  28.  
  29. int _result;  
  30.  
  31. try {  
  32.  
  33. _data.writeInterfaceToken(DESCRIPTOR);  
  34.  
  35. _data.writeStrongBinder((((window!=null))?(window.asBinder()):(null)));  
  36.  
  37. if ((attrs!=null)) {  
  38.  
  39. _data.writeInt(1);  
  40.  
  41. attrs.writeToParcel(_data, 0);  
  42.  
  43. }  
  44.  
  45. else {  
  46.  
  47. _data.writeInt(0);  
  48.  
  49. }  
  50.  
  51. _data.writeInt(requestedWidth);  
  52.  
  53. _data.writeInt(requestedHeight);  
  54.  
  55. _data.writeInt(viewVisibility);  
  56.  
  57. _data.writeInt(((insetsPending)?(1):(0)));  
  58.  
  59. //奇怪,outSurface的信息没有写到_data中。那.....  
  60.  
  61. mRemote.transact(Stub.TRANSACTION_relayout, _data, _reply, 0);  
  62.  
  63. _reply.readException();  
  64.  
  65. _result = _reply.readInt();  
  66.  
  67. if ((0!=_reply.readInt())) {  
  68.  
  69. outFrame.readFromParcel(_reply);  
  70.  
  71. }  
  72.  
  73. ....  
  74.  
  75. if ((0!=_reply.readInt())) {  
  76.  
  77. outSurface.readFromParcel(_reply); //从Parcel中读取信息来填充outSurface  
  78.  
  79. }  
  80.  
  81. }  
  82.  
  83. finally {  
  84.  
  85. _reply.recycle();  
  86.  
  87. _data.recycle();  
  88.  
  89. }  
  90.  
  91. return _result;  
  92.  
  93. } 

真奇怪啊,Binder客户端这头竟然没有把outSurface的信息发过去。我们赶紧看看服务端。

服务端这边处理是在onTranscat函数中。

  1. @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException  
  2.  
  3. {  
  4.  
  5. switch (code)  
  6.  
  7. {  
  8.  
  9. case TRANSACTION_relayout:  
  10.  
  11. {  
  12.  
  13. data.enforceInterface(DESCRIPTOR);  
  14.  
  15. android.view.IWindow _arg0;  
  16.  
  17. android.view.Surface _arg10;  
  18.  
  19. //刚才说了,Surface信息并没有传过来,那么我们在relayOut中看到的outSurface是怎么  
  20.  
  21. //出来的呢?看下面这句,原来在服务端这边竟然new了一个新的Surface!!!  
  22.  
  23. _arg10 = new android.view.Surface();  
  24.  
  25. int _result = this.relayout(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5, _arg6, _arg7, _arg8, _arg9, _arg10);  
  26.  
  27. reply.writeNoException();  
  28.  
  29. reply.writeInt(_result);  
  30.  
  31. //_arg10是copyFrom了,那怎么传到客户端呢?  
  32.  
  33. if ((_arg10!=null)) {  
  34.  
  35. reply.writeInt(1);//调用Surface的writeToParcel,把信息加入reply  
  36.  
  37. _arg10.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);  
  38.  
  39. }  
  40.  
  41. return true;  
  42.  
  43. } 

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

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

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

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

◆客户端调用Surface的readFromParcel,获得surface信息。

那就去看看writeToParcel吧。

[---->Surface_writeToParcel]

  1. static void Surface_writeToParcel(  
  2.  
  3. JNIEnv* env, jobject clazz, jobject argParcel, jint flags)  
  4.  
  5. {  
  6.  
  7. Parcel* parcel = (Parcel*)env->GetIntField(  
  8.  
  9. argParcel, no.native_parcel);  
  10.  
  11. const sp& control(getSurfaceControl(env, clazz));  
  12.  
  13. //还好,只是把数据序列化到Parcel中  
  14.  
  15. SurfaceControl::writeSurfaceToParcel(control, parcel);  
  16.  
  17. if (flags & PARCELABLE_WRITE_RETURN_VALUE) {  
  18.  
  19. setSurfaceControl(env, clazz, 0);  
  20.  
  21. }  
  22.  
  23. } 

那看看客户端的Surface_readFromParcel吧。

[----->Surface_readFromParcel]

  1. static void Surface_readFromParcel(  
  2.  
  3. JNIEnv* env, jobject clazz, jobject argParcel)  
  4.  
  5. {  
  6.  
  7. Parcel* parcel = (Parcel*)env->GetIntField( argParcel, no.native_parcel);  
  8.  
  9. //客户端这边还没有surface呢  
  10.  
  11. const sp& control(getSurface(env, clazz));  
  12.  
  13. //不过我们看到希望了,根据服务端那边Parcel信息来构造一个新的surface  
  14.  
  15. sp rhs = new Surface(*parcel);  
  16.  
  17. if (!Surface::isSameSurface(control, rhs)) {  
  18.  
  19. setSurface(env, clazz, rhs); //把这个新surface赋给客户端。终于我们有了surface!  
  20.  
  21. }  
  22.  
  23. } 

到此,我们终于七拐八绕的得到了surface,这其中经历太多曲折了。下一节,我们将精简这其中复杂的操作,统一归到Native层,以这样为切入点来了解Surface的工作流程和原理。

好,反正你知道ViewRoot调用了relayout后,Surface就真正从WindowManagerService那得到了。继续回到ViewRoot,其中还有一个重要地方是我们知道却不了解的。

  1. private void performTraversals() {  
  2.  
  3. // cache mView since it is used so much below...  
  4.  
  5. final View host = mView;  
  6.  
  7. boolean initialized = false;  
  8.  
  9. boolean contentInsetsChanged = false;  
  10.  
  11. boolean visibleInsetsChanged;  
  12.  
  13. try {  
  14.  
  15. relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);  
  16.  
  17. // relayoutWindow完后,我们得到了一个无比宝贵的Surface  
  18.  
  19. //那我们画界面的地方在哪里?就在这个函数中,离relayoutWindow不远处。  
  20.  
  21. ....  
  22.  
  23. boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();  
  24.  
  25. if (!cancelDraw && !newSurface) {  
  26.  
  27. mFullRedrawNeeded = false;  
  28.  
  29. draw(fullRedrawNeeded); //draw?draw什么呀?  
  30.  
  31. } 

[--->ViewRoot::draw()]

  1. private void draw(boolean fullRedrawNeeded) {  
  2.  
  3. Surface surface = mSurface; //嘿嘿,不担心了,surface资源都齐全了  
  4.  
  5. if (surface == null || !surface.isValid()) {  
  6.  
  7. return;  
  8.  
  9. }  
  10.  
  11. if (mAttachInfo.mViewScrollChanged) {  
  12.  
  13. mAttachInfo.mViewScrollChanged = false;  
  14.  
  15. mAttachInfo.mTreeObserver.dispatchOnScrollChanged();  
  16.  
  17. }  
  18.  
  19. int yoff;  
  20.  
  21. final boolean scrolling = mScroller != null && mScroller.computeScrollOffset();  
  22.  
  23. if (scrolling) {  
  24.  
  25. yoff = mScroller.getCurrY();  
  26.  
  27. } else {  
  28.  
  29. yoff = mScrollY;  
  30.  
  31. }  
  32.  
  33. if (mCurScrollY != yoff) {  
  34.  
  35. mCurScrollY = yoff;  
  36.  
  37. fullRedrawNeeded = true;  
  38.  
  39. }  
  40.  
  41. float appScale = mAttachInfo.mApplicationScale;  
  42.  
  43. boolean scalingRequired = mAttachInfo.mScalingRequired;  
  44.  
  45. Rect dirty = mDirty;  
  46.  
  47. if (mUseGL) { //我们不用OPENGL  
  48.  
  49. ...  
  50.  
  51. }  
  52.  
  53. Canvas canvas;  
  54.  
  55. try {  
  56.  
  57. int left = dirty.left;  
  58.  
  59. int top = dirty.top;  
  60.  
  61. int right = dirty.right;  
  62.  
  63. int bottom = dirty.bottom;  
  64.  
  65. //从Surface中锁定一块区域,这块区域是我们认为的需要重绘的区域  
  66.  
  67. canvas = surface.lockCanvas(dirty);  
  68.  
  69. // TODO: Do this in native  
  70.  
  71. canvas.setDensity(mDensity);  
  72.  
  73. }  
  74.  
  75. try {  
  76.  
  77. if (!dirty.isEmpty() || mIsAnimating) {  
  78.  
  79. long startTime = 0L;  
  80.  
  81. try {  
  82.  
  83. canvas.translate(0, -yoff);  
  84.  
  85. if (mTranslator != null) {  
  86.  
  87. mTranslator.translateCanvas(canvas);  
  88.  
  89. }  
  90.  
  91. canvas.setScreenDensity(scalingRequired  
  92.  
  93. ? DisplayMetrics.DENSITY_DEVICE : 0);  
  94.  
  95. //mView就是之前的decoreView,  
  96.  
  97. mView.draw(canvas);  
  98.  
  99. }  
  100.  
  101. } finally {  
  102.  
  103. //我们的图画完了,告诉surface释放这块区域  
  104.  
  105. surface.unlockCanvasAndPost(canvas);  
  106.  
  107. }  
  108.  
  109. if (scrolling) {  
  110.  
  111. mFullRedrawNeeded = true;  
  112.  
  113. scheduleTraversals();  
  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这块代码基本重写一遍,希望能够简单精炼点。


【安卓】Surface是什么? 安卓中的Surface理解起来有点抽象,它本质上是什么呢? Surface的本质 我们先来看一下官方源码的注释解释吧: // frameworks/base/core/java/android/view/Surface.java /** * Handle onto a raw buffer that is being managed by the screen compositor. * * <p>A Surface is generally created by or from a co 阅读详情

相关推荐

Android基础回顾】六:安卓显示机制Surface 、 SurfaceFlinger、Choreographer

在 Android 系统中,Surface 和 SurfaceFlinger 是图形渲染系统的核心组件,负责屏幕显示内容的合成与管理。它们协同工作,使各种 App 和系统界面能够高效地显示在屏幕上。

Shujie 1538

Android 显示框架:Surface详解之一

所有surface在native的构建都需要一个graphicbufferproducer作为参数。而这也真好印证了中所提到的surface在java层由三种构建方式,同样对于IGraphicBufferProducer也有不同的渠道获得。

爱睡觉的潘sky的博客 6650

团结引擎Android App View:在3D场景中无缝嵌入原生安卓应用

在智能座舱、XR和数字孪生等前沿领域,实现3D虚拟场景与原生2D应用的无缝融合是提升用户体验的关键技术挑战。其核心原理在于跨进程的图形渲染与输入事件同步,通过捕获应用界面帧缓冲区并转换为动态纹理,同时将3D交互事件精准映射回原生应用。这项技术为车载HMI、混合现实等场景带来了巨大的工程价值,使得开发者无需重写应用,即可将高德地图、网易云音乐等完整功能App或自定义View组件,深度集成到由游戏引擎驱动的3D环境中。本文以团结引擎的Android App View功能为例,深入解析了其实现架构、系统权限配置、

weixin_34175509的博客 391

Android Surface

在Android系统中,Surface是一种用于图形和视频渲染的抽象概念,它可以用来将应用程序绘制的图形或视频显示在屏幕上。一个Surface代表一个屏幕表面,可以是整个屏幕或者应用程序UI的一个独立窗口。Surface通过一个SurfaceHolder对象来提供访问接口。

liuning1985622的博客 2032

【SEED Labs 2.0】V*N Tunneling Lab

本文为 SEED Labs 2.0 - V*N Tunneling Lab 的实验记录。本实验工作量较大,但难度不大,重点在于搞清是谁发给谁,走的什么路径。另外,本次实验由于网络中的设备较多,修改了环境变量方便分清哪个窗口是哪个设备。这样操作起来就方便多了。...

Chenyang的博客 6979

别再只调OpenCV参数了!手把手教你用Python bm3d库搞定图像去噪(附完整代码与效果对比)

本文详细介绍了BM3D算法在图像去噪中的高级应用,通过Python bm3d库实现超越OpenCV传统方法的去噪效果。文章包含完整代码示例、参数调优技巧及工程化应用场景,帮助开发者掌握这一被称为'传统方法天花板'的技术,显著提升图像信噪比和细节保留能力。

weixin_28362173的博客 366

java 创建 surface_详解android开发下的Surface系统

Surface对应了一块屏幕缓冲区,每个window对应一个Surface,任何View都是画在Surface上的,传统的view共享一块屏幕缓冲区,所有的绘制必须在UI线程中进行。SurfaceView是一个View也许不够严谨,然而从定义中 public class SurfaceView extends View {...}显示SurfaceView确实是派生自View,但是SurfaceV...

weixin_42298622的博客 562

Java写项目前的准备工作指南(技术栈选择 环境搭建和工具配置 项目结构设计与模块划分)

在开始编写一个 Java 项目之前,做好充分的准备工作是至关重要的。很多初学者可能在没有清晰规划的情况下就开始编写代码,导致项目开发进度缓慢、结构混乱,甚至最终无法按预期完成。而事实上,项目的成功不仅仅取决于编码能力,还包括项目初期的规划和准备。准备工作是确保项目顺利进行的基础,它包括技术选型、环境搭建、需求分析、团队协作及项目管理等多个方面。通过这些准备,我们可以确保项目的开发不仅高效而且有可维护性,最终能交付出高质量的代码。在项目开发过程中,异常处理是非常关键的一部分。

eternal__day的博客 1188

深入浅出 详解Android Surface系统(2)

文章来自:http://mobile.51cto.com/android-259922_1.htm 本文详细介绍了Android中的Surface系统,采用情景分析的办法,详解了何为SurfaceFlinger,以及SurfaceFlinger的工作流程,以Activity函数调用为切入点来研究SurfaceFlinger。 JNI层 上面两个类的JN

探索。。。发现。。。 1731

TensorRT-LLM环境配置避坑指南:从安装到实战

本文是一份详尽的TensorRT-LLM环境配置避坑指南。文章重点解析了从Python、CUDA、PyTorch版本选择,到解决mpi4py安装这一核心难题的完整流程,并提供了经过验证的稳定版本组合与实战排错方案,帮助开发者高效部署大语言模型推理优化环境。

a0b1c2d3的专栏 985

ESP32-S3 + TB6600驱动42步进电机:从基础接线到AccelStepper库平滑加减速实战

本文详细介绍了如何使用ESP32-S3开发板和TB6600驱动器控制42步进电机,从基础接线到利用AccelStepper库实现平滑加减速控制。内容涵盖硬件连接、基础驱动代码、AccelStepper库的安装与参数调优,以及高级技巧与故障排除,帮助创客实现工业级精准控制。

weixin_42632434的博客 284

深入浅出 详解Android Surface系统(1)

文章来自:http://mobile.51cto.com/android-259922.htm   本文详细介绍了Android中的Surface系统,采用情景分析的办法,详解了何为SurfaceFlinger,以及SurfaceFlinger的工作流程,以Activity函数调用为切入点来研究SurfaceFlinger。 一 目的 本篇文

探索。。。发现。。。 1277

Altium Designer许可证冲突?手把手教你用Windows防火墙彻底禁止AD联网(附详细截图)

本文详细介绍了如何通过Windows防火墙精准管控Altium Designer的网络权限,解决许可证冲突问题。通过配置入站和出站规则,彻底阻断AD的联网行为,提升软件运行效率。适用于需要稳定使用Altium Designer的工程师,附有详细截图和高级验证方法。

weixin_38170137的博客 423

安卓虚拟摄像头深度解析:基于Xposed框架的高效视频替换方案

安卓虚拟摄像头(VCAM)是一款基于Xposed框架的专业级虚拟相机模块,能够为Android 5.0+系统提供灵活的视频替换功能。通过HOOK系统相机API,该模块将真实摄像头输入替换为预置视频流,为开发者、测试人员和隐私保护需求者提供强大的虚拟相机解决方案。 ## 技术架构与核心原理 ### Xposed框架的深度集成 VCAM的核心技术在于对Android Camera API的精准HO

gitblog_00988的博客 227

3DGS实战笔记:从相机坐标系到屏幕像素,手把手推导高斯椭球的投影变换矩阵

本文详细解析了3D高斯散射(3DGS)技术中从相机坐标系到屏幕像素的几何变换过程,重点推导了高斯椭球的投影变换矩阵。通过视图变换、投影变换和视口变换的完整流程,结合雅可比矩阵处理协方差矩阵,实现了高效的三维重建与渲染。文章提供了Python代码示例,帮助开发者深入理解3DGS的核心几何变换机制。

weixin_28691741的博客 438

lammps教程:聚合物压缩,避免“bond atoms missing”

如果使用ms建模,聚合物的链会伸出到盒子外面,在导出data文件后,盒子外面的原子被周期性算法折叠进盒子对面,这就造成了边界附近的bond原子可能被分布到了盒子的两侧。并且,这个data文件不能使用npt系综进行压缩,npt系综必须使用周期性边界,在压缩时,bond原子同样会移动到对面的边界。所以,最好的解决办法是使用一个反射墙墙进行压缩,设置一个反射墙墙既可以实现压缩效果,也可以组织原子跨越box边界移动到对面。解决这个问题的原则只有一个:导出data时确保所有的bond原子没有跨越边界。

lammps_jiayou的博客 3819

Android图形Buffer申请全链路解析:从Surface到Gralloc内存分配

在移动应用开发中,图形渲染性能直接影响用户体验,而图形缓冲区的管理是其中的核心技术。其原理基于生产者-消费者模型,通过BufferQueue机制协调应用与显示系统的数据交换。这项技术的核心价值在于实现高效的内存复用与零拷贝数据传输,避免画面卡顿和内存抖动。在Android平台上,Surface作为统一接口,GraphicBuffer承载图形数据,Gralloc驱动负责底层内存分配。典型的应用场景包括视频编解码、相机预览和游戏渲染。本文聚焦Android显示系统的Buffer申请流程,深入分析dequeueB

weixin_33898876的博客 283

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

WebGL 音乐可视化器实战:基于 Web Audio API 与 AnalyserNode 的浏览器音频响应动画指南

> 本文源自 webgl-fundamentals 开源仓库中的问答文档 [How to write a web-based music visualizer](https://link.gitcode.com/i/d6c6b8f1ce4ecf8deb153b892047a2ac),面向想在浏览器中构建"音频响应(audio reactive)"视觉效果的开发者。读完本文,你将掌握用 Web Au

gitblog_00275的博客 359

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

解密Android图形显示系统:从Surface到BufferQueue的底层运作机制

本文深入解析Android图形显示系统的底层运作机制,重点探讨了从Surface到BufferQueue的核心组件及其协同工作原理。通过剖析SurfaceFlinger合成流程、VSync同步机制和三缓冲策略,揭示了Android实现流畅图形显示的技术奥秘,为开发者优化应用性能提供理论基础和实践指导。

mango的博客 319

从零拆解安卓虚拟摄像头:Xposed Hook 拦截相机 API 与 MediaCodec 硬解码实战笔记

如果你曾好奇过"安卓虚拟摄像头"到底是怎么实现的——为什么一个普通 App 可以把视频通话里的画面换成一段 mp4,把拍照结果换成一张图片——这篇文章就是为你准备的。我会以第一人称复盘一次完整的实现:从 Xposed Hook 在 Zygote 阶段的注入原理,到两代相机接口的拦截思路,再到 MediaCodec 硬解码流水线,最后附上我踩过的坑与排查清单,保证新手也能从头跟到尾。 ## 从一次

gitblog_00654的博客 799

Android图像缓存与Surface初始化

在Android系统中,绘制渲染与渲染管理是分开的,分别由Surface和SurfaceControl负责。Surface是SurfaceFlinger管理的一块缓存,Surface的创建依赖SurfaceControl。SurfaceControl用于管理Surface的绘制层级与获取。Surface和SurfaceControl声明在ViewRootImpl的全局成员变量中,在创建ViewRootImpl时会一起创建Surface和SurfaceControl。

LeeDuoZuiShuai的博客 2198
上一篇: DBUS
下一篇: svn命令
zzobin
博客等级 码龄18年 95粉丝 82原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值