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

来源:互联网 发布:物联网数字域名 编辑:程序博客网 时间:2024/05/21 18:47

文章来自:http://mobile.51cto.com/android-259922_1.htm

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


    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.  

    它的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.  

    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.  

    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.  

    [--->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.  

    这里仅仅是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.  

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

    太诡异了!竟然有这么多花花肠子。我相信如果没有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.  

    那看看客户端的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.  

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

    [--->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.  

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

    原创粉丝点击