天天看点

android之surfaceview画图

在前文中,我们分析了应用程序窗口连接到windowmanagerservice服务的过程。在这个过程中,windowmanagerservice服务会为应用程序窗口创建过一个到surfaceflinger服务的连接。有了这个连接之后,windowmanagerservice服务就可以为应用程序窗口创建绘图表面了,以便可以用来渲染窗口的ui。在本文中,我们就详细分析应用程序窗口的绘图表面的创建过程。

        对于在java层实现的android应用程序窗口来说,它也需要请求surfaceflinger服务为它创建绘图表面,这个绘图表面使用一个surface对象来描述。由于在java层实现的android应用程序窗口还要接受windowmanagerservice服务管理,因此,它的绘图表面的创建流程就会比在c++层实现的应用程序窗口复杂一些。具体来说,就是在在java层实现的android应用程序窗口的绘图表面是通过两个surface对象来描述,一个是在应用程序进程这一侧创建的,另一个是在windowmanagerservice服务这一侧创建的,它们对应于surfaceflinger服务这一侧的同一个layer对象,如图1所示:

android之surfaceview画图

图1 应用程序窗口的绘图表面的模型图

        在应用程序进程这一侧,每一个应用程序窗口,即每一个activity组件,都有一个关联的surface对象,这个surface对象是保在在一个关联的viewroot对象的成员变量msurface中的,如图2所示:

android之surfaceview画图

图2 应用程序窗口在应用程序进程这一侧的surface的实现

        在windowmanagerservice服务这一侧,每一个应用程序窗口,即每一个activity组件,都有一个对应的windowstate对象,这个windowstate对象的成员变量msurface同样是指向了一个surface对象,如图3所示:

android之surfaceview画图

图3 应用程序窗口在windowmanagerservice服务这一侧的surface的实现

       一个应用程序窗口分别位于应用程序进程和windowmanagerservice服务中的两个surface对象有什么区别呢?虽然它们都是用来操作位于surfaceflinger服务中的同一个layer对象的,不过,它们的操作方式却不一样。具体来说,就是位于应用程序进程这一侧的surface对象负责绘制应用程序窗口的ui,即往应用程序窗口的图形缓冲区填充ui数据,而位于windowmanagerservice服务这一侧的surface对象负责设置应用程序窗口的属性,例如位置、大小等属性。这两种不同的操作方式分别是通过c++层的surface对象和surfacecontrol对象来完成的,因此,位于应用程序进程和windowmanagerservice服务中的两个surface对象的用法是有区别的。之所以会有这样的区别,是因为绘制应用程序窗口是独立的,由应用程序进程来完即可,而设置应用程序窗口的属性却需要全局考虑,即需要由windowmanagerservice服务来统筹安排,例如,一个应用程序窗口的z轴坐标大小要考虑它到的窗口类型以及它与系统中的其它窗口的关系。

        说到这里,另外一个问题又来了,由于一个应用程序窗口对应有两个surface对象,那么它们是如何创建出来的呢?简单地说,就是按照以下步骤来创建:

       1. 应用程序进程请求windowmanagerservice服务为一个应用程序窗口创建一个surface对象;

       2. windowmanagerservice服务请求surfaceflinger服务创建一个layer对象,并且获得一个isurface接口;

       3. windowmanagerservice服务将获得的isurface接口保存在其内部的一个surface对象中,并且将该isurface接口返回给应用程序进程;

       4. 应用程序进程得到windowmanagerservice服务返回的isurface接口之后,再将其封装成其内部的另外一个surface对象中。

android之surfaceview画图

图4 应用程序窗口的绘图表面的创建过程

        这个过程可以分为10个步骤,接下来我们就详细分析每一个步骤。

        step 1. viewroot.requestlayout

android之surfaceview画图

public final class viewroot extends handler implements viewparent,  

        view.attachinfo.callbacks {  

    ......  

    boolean mlayoutrequested;  

    public void requestlayout() {  

        checkthread();  

        mlayoutrequested = true;  

        scheduletraversals();  

    }  

}  

        这个函数定义在文件frameworks/base/core/java/android/view/viewroot.java中。

        viewroot类的成员函数requestlayout首先调用另外一个成员函数checkthread来检查当前线程是否就是创建当前正在处理的viewroot对象的线程。如果不是的话,那么viewroot类的成员函数checkthread就会抛出一个异常出来。viewroot类是从handler类继承下来的,用来处理应用程序窗口的ui布局和渲染等消息。由于这些消息都是与ui相关的,因此它们就需要在ui线程中处理,这样我们就可以推断出当前正在处理的viewroot对象是要应用程序进程的ui线程中创建的。进一步地,我们就可以推断出viewroot类的成员函数checkthread实际上就是用来检查当前线程是否是应用程序进程的ui线程,如果不是的话,它就会抛出一个异常出来。

        通过了上述检查之后,viewroot类的成员函数requestlayout首先将其成员变量mlayoutrequested的值设置为true,表示应用程序进程的ui线程正在被请求执行一个ui布局操作,接着再调用另外一个成员函数scheduletraversals来继续执行ui布局的操作。

        step 2. viewroot.scheduletraversals

android之surfaceview画图

    boolean mtraversalscheduled;  

    public void scheduletraversals() {  

        if (!mtraversalscheduled) {  

            mtraversalscheduled = true;  

            sendemptymessage(do_traversal);  

        }  

        viewroot类的成员变量mtraversalscheduled用来表示应用程序进程的ui线程是否已经调度了一个do_traversal消息。如果已经调度了的话,它的值就会等于true。在这种情况下, viewroot类的成员函数scheduletraversals就什么也不做,否则的话,它就会首先将成员变量mtraversalscheduled的值设置为true,然后再调用从父类handler继承下来的成员函数sendemptymessage来往应用程序进程的ui线程发送一个do_traversal消息。

        这个类型为do_traversal的消息是由viewroot类的成员函数performtraversals来处理的,因此,接下来我们就继续分析viewroot类的成员函数performtraversals的实现。

        step 3. viewroot.performtraversals

android之surfaceview画图

    view mview;  

    boolean mfirst;  

    boolean mfullredrawneeded;  

    private final surface msurface = new surface();  

    private void performtraversals() {  

        ......  

        final view host = mview;  

        mtraversalscheduled = false;  

        boolean fullredrawneeded = mfullredrawneeded;  

        boolean newsurface = false;  

        if (mlayoutrequested) {  

            ......  

            host.measure(childwidthmeasurespec, childheightmeasurespec);  

            .......  

        int relayoutresult = 0;  

        if (mfirst || windowshouldresize || insetschanged  

                || viewvisibilitychanged || params != null) {  

            boolean hadsurface = msurface.isvalid();  

            try {  

                ......  

                relayoutresult = relayoutwindow(params, viewvisibility, insetspending);  

                if (!hadsurface) {  

                    if (msurface.isvalid()) {  

                        ......  

                        newsurface = true;  

                        fullredrawneeded = true;  

                    }  

                }   

            } catch (remoteexception e) {  

            }  

        final boolean didlayout = mlayoutrequested;  

        if (didlayout) {  

            mlayoutrequested = false;  

            host.layout(0, 0, host.mmeasuredwidth, host.mmeasuredheight);  

        mfirst = false;  

        boolean canceldraw = attachinfo.mtreeobserver.dispatchonpredraw();  

        if (!canceldraw && !newsurface) {  

            mfullredrawneeded = false;  

            draw(fullredrawneeded);  

        } else {  

            // try again  

            scheduletraversals();  

        viewroot类的成员函数performtraversals的实现是相当复杂的,这里我们分析它的实现框架,在以后的文章中,我们再详细分析它的实现细节。

        在分析viewroot类的成员函数performtraversals的实现框架之前,我们首先了解viewroot类的以下五个成员变量:

        --mlayoutrequested:这是一个布尔变量,用来描述应用程序进程的ui线程是否需要正在被请求执行一个ui布局操作。

        --mfirst:这是一个布尔变量,用来描述应用程序进程的ui线程是否第一次处理一个应用程序窗口的ui。

        --mfullredrawneeded:这是一个布尔变量,用来描述应用程序进程的ui线程是否需要将一个应用程序窗口的全部区域都重新绘制。

        --msurface:它指向一个java层的surface对象,用来描述一个应用程序窗口的绘图表面。

        注意,成员变量msurface所指向的surface对象在创建的时候,还没有在c++层有一个关联的surface对象,因此,这时候它描述的就是一个无效的绘图表面。另外,这个surface对象在应用程序窗口运行的过程中,也会可能被销毁,因此,这时候它描述的绘图表面也会变得无效。在上述两种情况中,我们都需要请求windowmanagerservice服务来为当前正在处理的应用程序窗口创建有一个有效的绘图表面,以便可以在上面渲染ui。这个创建绘图表面的过程正是本文所要关心的。

        理解了上述五个成员变量之后,我们就可以分析viewroot类的成员函数performtraversals的实现框架了,如下所示:

        1. 将成员变量mview和mfullredrawneeded的值分别保存在本地变量host和fullredrawneeded中,并且将成员变量mtraversalscheduled的值设置为false,表示应用程序进程的ui线程中的do_traversal消息已经被处理了。

        2. 本地变量newsurface用来描述当前正在处理的应用程序窗口在本轮的do_traversal消息处理中是否新创建了一个绘图表面,它的初始值为false。

        3. 如果成员变量mlayoutrequested的值等于true,那么就表示应用程序进程的ui线程正在被请求对当前正在处理的应用程序窗口执行一个ui布局操作,因此,这时候就会调用本地变量host所描述的一个顶层视图对象的成员函数measure来测量位于各个层次的ui控件的大小。

        4. 如果当前正在处理的应用程序窗口的ui是第一次被处理,即成员变量mfirst的值等于true,或者当前正在处理的应用程序窗口的大小发生了变化,即本地变量windowshouldresize的值等于true,或者当前正在处理的应用程序窗口的边衬发生了变化,即本地变量insetschanged的值等于true,或者正在处理的应用程序窗口的可见性发生了变化,即本地变量viewvisibilitychanged的值等于true,或者正在处理的应用程序窗口的ui布局参数发生了变化,即本地变量params指向了一个windowmanager.layoutparams对象,那么应用程序进程的ui线程就会调用另外一个成员函数relayoutwindow来请求windowmanagerservice服务重新布局系统中的所有窗口。windowmanagerservice服务在重新布局系统中的所有窗口的过程中,如果发现当前正在处理的应用程序窗口尚未具有一个有效的绘图表面,那么就会为它创建一个有效的绘图表面,这一点是我们在本文中所要关注的。

        5. 应用程序进程的ui线程在调用viewroot类的成员函数relayoutwindow来请求windowmanagerservice服务重新布局系统中的所有窗口之前,会调用成员变量msurface所指向的一个surface对象的成员函数isvalid来判断它描述的是否是一个有效的绘图表面,并且将结果保存在本地变量hadsurface中。

        6. 应用程序进程的ui线程在调用viewroot类的成员函数relayoutwindow来请求windowmanagerservice服务重新布局系统中的所有窗口之后,又会继续调用成员变量msurface所指向的一个surface对象的成员函数isvalid来判断它描述的是否是一个有效的绘图表面。如果这时候成员变量msurface所指向的一个surface对象描述的是否是一个有效的绘图表面,并且本地变量hadsurface的值等于false,那么就说明windowmanagerservice服务为当前正在处理的应用程序窗口新创建了一个有效的绘图表面,于是就会将本地变量newsurface和fullredrawneeded的值均修改为true。

        7. 应用程序进程的ui线程再次判断mlayoutrequested的值是否等于true。如果等于的话,那么就说明需要对当前正在处理的应用程序窗口的ui进行重新布局,这是通过调用本地变量host所描述的一个顶层视图对象的成员函数layout来实现的。在对当前正在处理的应用程序窗口的ui进行重新布局之前,应用程序进程的ui线程会将成员变量mlayoutrequested的值设置为false,表示之前所请求的一个ui布局操作已经得到处理了。

        8. 应用程序进程的ui线程接下来就要开始对当前正在处理的应用程序窗口的ui进行重新绘制了,不过在重绘之前,会先询问一下那些注册到当前正在处理的应用程序窗口中的tree observer,即调用它们的成员函数dispatchonpredraw,看看它们是否需要取消接下来的重绘操作,这个询问结果保存在本地变量canceldraw中。

        9. 如果本地变量canceldraw的值等于false,并且本地变量newsurface的值也等于false,那么就说明注册到当前正在处理的应用程序窗口中的tree observer不要求取消当前的这次重绘操作,并且当前正在处理的应用程序窗口也没有获得一个新的绘图表面。在这种情况下,应用程序进程的ui线程就会调用viewroot类的成员函数draw来对当前正在处理的应用程序窗口的ui进行重绘。在重绘之前,还会将viewroot类的成员变量mfullredrawneeded的值重置为false。

       10. 如果本地变量canceldraw的值等于true,或者本地变量newsurface的值等于true,那么就说明注册到当前正在处理的应用程序窗口中的tree observer要求取消当前的这次重绘操作,或者当前正在处理的应用程序窗口获得了一个新的绘图表面。在这两种情况下,应用程序进程的ui线程就不能对当前正在处理的应用程序窗口的ui进行重绘了,而是要等到下一个do_traversal消息到来的时候,再进行重绘,以便使得当前正在处理的应用程序窗口的各项参数可以得到重新设置。下一个do_traversal消息需要马上被调度,因此,应用程序进程的ui线程就会重新执行viewroot类的成员函数scheduletraversals。

       这样,我们就分析完成viewroot类的成员函数performtraversals的实现框架了,接下来我们就继续分析viewroot类的成员函数relayoutwindow的实现,以便可以看到当前正在处理的应用程序窗口的绘图表面是如何创建的。

       step 4. viewroot.relayoutwindow

android之surfaceview画图

public final class viewroot extends handler implements viewparent,    

        view.attachinfo.callbacks {    

    ......    

    static iwindowsession swindowsession;    

    final w mwindow;  

    private final surface msurface = new surface();    

    private int relayoutwindow(windowmanager.layoutparams params, int viewvisibility,    

            boolean insetspending) throws remoteexception {    

        ......    

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

        return relayoutresult;    

    }    

}    

       这个函数定义在文件frameworks/base/core/java/android/view/viewroot.java中。

       viewroot类的成员函数relayoutwindow调用静态成员变量swindowsession所描述的一个实现了iwindowsession接口的binder代理对象的成员函数relayout来请求windowmanagerservice服务对成员变量mwindow所描述的一个应用程序窗口的ui进行重新布局,同时,还会将成员变量msurface所描述的一个surface对象传递给windowmanagerservice服务,以便windowmanagerservice服务可以根据需要来重新创建一个绘图表面给成员变量mwindow所描述的一个应用程序窗口使用。

       实现了iwindowsession接口的binder代理对象是由iwindowsession.stub.proxy类来描述的,接下来我们就继续分析它的成员函数relayout的实现。

       step 5. iwindowsession.stub.proxy.relayout

       iwindowsession接口是使用aidl语言来描述的,如下所示:

android之surfaceview画图

interface iwindowsession {  

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

        这个接口定义在frameworks/base/core/java/android/view/iwindowsession.aidl文件中。

        使用aidl语言来描述的iwindowsession接口被编译后,就会生成一个使用java语言来描述的iwindowsession.stub.proxy类,它的成员函数relayout的实现如下所示:

android之surfaceview画图

public interface iwindowsession extends android.os.iinterface  

{  

    public static abstract class stub extends android.os.binder implements android.view.iwindowsession  

    {  

         ......  

         private static class proxy implements android.view.iwindowsession  

         {  

             private android.os.ibinder mremote;  

             ......  

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

                    mremote.transact(stub.transaction_relayout, _data, _reply, 0);  

                    _reply.readexception();  

                    _result = _reply.readint();  

                    if ((0!=_reply.readint())) {  

                        outframe.readfromparcel(_reply);  

                        outcontentinsets.readfromparcel(_reply);  

                        outvisibleinsets.readfromparcel(_reply);  

                        outconfig.readfromparcel(_reply);  

                        outsurface.readfromparcel(_reply);  

                } finally {  

                    _reply.recycle();  

                    _data.recycle();  

                }  

                return _result;  

        这个函数定义在文件out/target/common/obj/java_libraries/framework_intermediates/src/core/java/android/view/iwindowsession.java中。

        iwindowsession.stub.proxy类的成员函数relayout首先将从前面传进来的各个参数写入到parcel对象_data中,接着再通过其成员变量mremote所描述的一个binder代理对象向运行在windowmanagerservice服务内部的一个session对象发送一个类型为transaction_relayout的进程间通信请求,其中,这个session对象是用来描述从当前应用程序进程到windowmanagerservice服务的一个连接的。

        当运行在windowmanagerservice服务内部的session对象处理完成当前应用程序进程发送过来的类型为transaction_relayout的进程间通信请求之后,就会将处理结果写入到parcel对象_reply中,并且将这个parcel对象_reply返回给当前应用程序进程处理。返回结果包含了一系列与参数window所描述的应用程序窗口相关的参数,如下所示:

       1. 窗口的大小:最终保存在输出参数outframe中。

       2. 窗口的内容区域边衬大小:最终保存在输出参数outcontentinsets中。

       3. 窗口的可见区域边衬大小:最终保存在输出参数outvisibleinsets中。

       4. 窗口的配置信息:最终保存在输出参数outconfig中。

       5. 窗口的绘图表面:最终保存在输出参数outsurface中。

       这里我们只关注从windowmanagerservice服务返回来的窗口绘图表面是如何保存到输出参数outsurface中的,即关注surface类的成员函数readfromparcel的实现。从前面的调用过程可以知道,输出参数outsurface描述的便是当前正在处理的应用程序窗口的绘图表面,将windowmanagerservice服务返回来的窗口绘图表面保存在它里面,就相当于是为当前正在处理的应用程序窗口创建了一个绘图表面。

       在分析surface类的成员函数readfromparcel的实现之前,我们先分析session类的成员函数relayout的实现,因为它是用来处理类型为transaction_relayout的进程间通信请求的。

       step 6. session.relayout

android之surfaceview画图

public class windowmanagerservice extends iwindowmanager.stub  

        implements watchdog.monitor {  

    private final class session extends iwindowsession.stub  

            implements ibinder.deathrecipient {  

        public int relayout(iwindow window, windowmanager.layoutparams attrs,  

                int requestedwidth, int requestedheight, int viewflags,  

                boolean insetspending, rect outframe, rect outcontentinsets,  

                rect outvisibleinsets, configuration outconfig, surface outsurface) {  

            //log.d(tag, ">>>>>> entered relayout from " + binder.getcallingpid());  

            int res = relayoutwindow(this, window, attrs,  

                    requestedwidth, requestedheight, viewflags, insetspending,  

                    outframe, outcontentinsets, outvisibleinsets, outconfig, outsurface);  

            //log.d(tag, "<<<<<< exiting relayout to " + binder.getcallingpid());  

            return res;  

        这个函数定义在文件frameworks/base/services/java/com/android/server/windowmanagerservice.java中。

        session类的成员函数relayout调用了外部类windowmanagerservice的成员函数relayoutwindow来对参数参数window所描述的一个应用程序窗口的ui进行布局,接下来我们就继续分析windowmanagerservice类的成员函数relayoutwindow的实现。

        step 7. windowmanagerservice.relayoutwindow

android之surfaceview画图

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

        synchronized(mwindowmap) {  

            windowstate win = windowforclientlocked(session, client, false);  

            if (win == null) {  

                return 0;  

            if (viewvisibility == view.visible &&  

                    (win.mapptoken == null || !win.mapptoken.clienthidden)) {  

                    surface surface = win.createsurfacelocked();  

                    if (surface != null) {  

                        outsurface.copyfrom(surface);  

                    } else {  

                        // for some reason there isn't a surface.  clear the  

                        // caller's object so they see the same state.  

                        outsurface.release();  

                } catch (exception e) {  

                    ......  

                    return 0;  

        return (intouchmode ? windowmanagerimpl.relayout_in_touch_mode : 0)  

                | (displayed ? windowmanagerimpl.relayout_first_time : 0);  

        windowmanagerservice类的成员函数relayoutwindow的实现是相当复杂的,这里我们只关注与创建应用程序窗口的绘图表面相关的代码,在后面的文章中,我们再详细分析它的实现。

        简单来说,windowmanagerservice类的成员函数relayoutwindow根据应用程序进程传递过来的一系列数据来重新设置由参数client所描述的一个应用程序窗口的大小和可见性等信息,而当一个应用程序窗口的大小或者可见性发生变化之后,系统中当前获得焦点的窗口,以及输入法窗口和壁纸窗口等都可能会发生变化,而且也会对其它窗口产生影响,因此,这时候windowmanagerservice类的成员函数relayoutwindow就会对系统中的窗口的布局进行重新调整。对系统中的窗口的布局进行重新调整的过程是整个windowmanagerservice服务最为复杂和核心的内容,我们同样是在后面的文章中再详细分析。

        现在,我们就主要分析参数client所描述的一个应用程序窗口的绘图表面的创建过程。

        windowmanagerservice类的成员函数relayoutwindow首先获得与参数client所对应的一个windowstate对象win,这是通过调用windowmanagerservice类的成员函数windowforclientlocked来实现的。如果这个对应的windowstate对象win不存在,那么就说明应用程序进程所请求处理的应用程序窗口不存在,这时候windowmanagerservice类的成员函数relayoutwindow就直接返回一个0值给应用程序进程。

        windowmanagerservice类的成员函数relayoutwindow接下来判断参数client所描述的一个应用程序窗口是否是可见的。一个窗口只有在可见的情况下,windowmanagerservice服务才会为它创建一个绘图表面。 一个窗口是否可见由以下两个条件决定:

        1. 参数viewvisibility的值等于view.visible,表示应用程序进程请求将它设置为可见的。

        注意,当windowstate对象win的成员变量mapptoken等于null时,只要满足条件1就可以了,因为这时候参数client所描述的窗口不是一个activity组件窗口,它的可见性不像activity组件窗口一样受到activity组件的可见性影响。

        我们假设参数client所描述的是一个应用程序窗口,并且这个应用程序窗口是可见的,那么windowmanagerservice类的成员函数relayoutwindow接下来就会调用windowstate对象win的成员函数createsurfacelocked来为它创建一个绘图表面。如果这个绘图表面能创建成功,那么windowmanagerservice类的成员函数relayoutwindow就会将它的内容拷贝到输出参数outsource所描述的一个surface对象去,以便可以将它返回给应用程序进程处理。另一方面,如果这个绘图表面不能创建成功,那么windowmanagerservice类的成员函数relayoutwindow就会将输出参数outsource所描述的一个surface对象的内容释放掉,以便应用程序进程知道该surface对象所描述的绘图表面已经失效了。

        接下来,我们就继续分析windowstate类的成员函数createsurfacelocked的实现,以便可以了解一个应用程序窗口的绘图表面的创建过程。

        step 8. windowstate.createsurfacelocked

android之surfaceview画图

    private final class windowstate implements windowmanagerpolicy.windowstate {  

        surface msurface;  

        surface createsurfacelocked() {  

            if (msurface == null) {  

                mreportdestroysurface = false;  

                msurfacependingdestroy = false;  

                mdrawpending = true;  

                mcommitdrawpending = false;  

                mreadytoshow = false;  

                if (mapptoken != null) {  

                    mapptoken.alldrawn = false;  

                int flags = 0;  

                if (mattrs.memorytype == memory_type_push_buffers) {  

                    flags |= surface.push_buffers;  

                if ((mattrs.flags&windowmanager.layoutparams.flag_secure) != 0) {  

                    flags |= surface.secure;  

                int w = mframe.width();  

                int h = mframe.height();  

                if ((mattrs.flags & layoutparams.flag_scaled) != 0) {  

                    // for a scaled surface, we always want the requested  

                    // size.  

                    w = mrequestedwidth;  

                    h = mrequestedheight;  

                // something is wrong and surfaceflinger will not like this,  

                // try to revert to sane values  

                if (w <= 0) w = 1;  

                if (h <= 0) h = 1;  

                msurfaceshown = false;  

                msurfacelayer = 0;  

                msurfacealpha = 1;  

                msurfacex = 0;  

                msurfacey = 0;  

                msurfacew = w;  

                msurfaceh = h;  

                    msurface = new surface(  

                            msession.msurfacesession, msession.mpid,  

                            mattrs.gettitle().tostring(),  

                            0, w, h, mattrs.format, flags);  

                } catch (surface.outofresourcesexception e) {  

                    reclaimsomesurfacememorylocked(this, "create");  

                    return null;  

                surface.opentransaction();  

                    try {  

                        msurfacex = mframe.left + mxoffset;  

                        msurfacey = mframe.top + myoffset;  

                        msurface.setposition(msurfacex, msurfacey);  

                        msurfacelayer = manimlayer;  

                        msurface.setlayer(manimlayer);  

                        msurfaceshown = false;  

                        msurface.hide();  

                        if ((mattrs.flags&windowmanager.layoutparams.flag_dither) != 0) {  

                            ......  

                            msurface.setflags(surface.surface_dither,  

                                    surface.surface_dither);  

                        }  

                    } catch (runtimeexception e) {  

                        reclaimsomesurfacememorylocked(this, "create-init");  

                    mlasthidden = true;  

                    surface.closetransaction();  

            return msurface;  

        在创建一个应用程序窗口的绘图表面之前,我们需要知道以下数据:

        1. 应用程序窗口它所运行的应用程序进程的pid。

        2. 与应用程序窗口它所运行的应用程序进程所关联的一个surfacesession对象。

        3. 应用程序窗口的标题。

        4. 应用程序窗口的像素格式。

        5. 应用程序窗口的宽度。

        6. 应用程序窗口的高度。

        7. 应用程序窗口的图形缓冲区属性标志。

        第1个和第2个数据可以通过当前正在处理的windowstate对象的成员变量msession所描述的一个session对象的成员变量mpid和msurfacesession来获得;第3个和第4个数据可以通过当前正在处理的windowstate对象的成员变量mattr所描述的一个windowmanager.layoutparams对象的成员函数gettitle以及成员变量format来获得;接下来我们就分析后面3个数据是如何获得的。

        windowstate类的成员变量mframe的类型为rect,它用来描述应用程序窗口的位置和大小,它们是由windowmanagerservice服务根据屏幕大小以及其它属性计算出来的,因此,通过调用过它的成员函数width和height就可以得到要创建绘图表面的应用程序窗口的宽度和高度,并且保存在变量w和h中。但是,当当前正在处理的windowstate对象的成员变量mattr所描述的一个windowmanager.layoutparams对象的成员变量flags的layoutparams.flag_scaled位不等于0时,就说明应用程序进程指定了该应用程序窗口的大小,这时候指定的应用程序窗口的宽度和高度就保存在windowstate类的成员变量mrequestedwidth和mrequestedheight中,因此,我们就需要将当前正在处理的windowstate对象的成员变量mrequestedwidth和mrequestedheight的值分别保存在变量w和h中。经过上述两步计算之后,如果得到的变量w和h等于0,那么就说明当前正在处理的windowstate对象所描述的应用程序窗口的大小还没有经过计算,或者还没有被指定过,这时候就需要将它们的值设置为1,避免接下来创建一个大小为0的绘图表面。

        如果当前正在处理的windowstate对象的成员变量mattr所描述的一个windowmanager.layoutparams对象的成员变量memorytype的值等于memory_type_push_buffers,那么就说明正在处理的应用程序窗口不拥有专属的图形缓冲区,这时候就需要将用来描述正在处理的应用程序窗口的图形缓冲区属性标志的变量flags的surface.push_buffers位设置为1。

       此外,如果当前正在处理的windowstate对象的成员变量mattr所描述的一个windowmanager.layoutparams对象的成员变量flags的windowmanager.layoutparams.flag_secure位不等于0,那么就说明正在处理的应用程序窗口的界面是安全的,即是受保护的,这时候就需要将用来描述正在处理的应用程序窗口的图形缓冲区属性标志的变量flags的surface.secure位设置为1。当一个应用程序窗口的界面是受保护时,surfaceflinger服务在执行截屏功能时,就不能把它的界面截取下来。

       上述数据准备就绪之后,就可以创建当前正在处理的windowstate对象所描述的一个应用程序窗口的绘图表面了,不过在创建之前,还会初始化该windowstate对象的以下成员变量:

       --mreportdestroysurface的值被设置为false。当一个应用程序窗口的绘图表面被销毁时,windowmanagerservice服务就会将相应的windowstate对象的成员变量mreportdestroysurface的值设置为true,表示要向该应用程序窗口所运行在应用程序进程发送一个绘图表面销毁通知。

       --msurfacependingdestroy的值被设置为false。当一个应用程序窗口的绘图表面正在等待销毁时,windowmanagerservice服务就会将相应的windowstate对象的成员变量mreportdestroysurface的值设置为true。

       --mdrawpending的值被设置为true。当一个应用程序窗口的绘图表面处于创建之后并且绘制之前时,windowmanagerservice服务就会将相应的windowstate对象的成员变量mdrawpending的值设置为true,以表示该应用程序窗口的绘图表面正在等待绘制。

       --mcommitdrawpending的值被设置为false。当一个应用程序窗口的绘图表面绘制完成之后并且可以显示出来之前时,windowmanagerservice服务就会将相应的windowstate对象的成员变量mcommitdrawpending的值设置为true,以表示该应用程序窗口正在等待显示。

       --mreadytoshow的值被设置为false。有时候当一个应用程序窗口的绘图表面绘制完成并且可以显示出来之后,由于与该应用程序窗口所关联的一个activity组件的其它窗口还未准备好显示出来,这时候windowmanagerservice服务就会将相应的windowstate对象的成员变量mreadytoshow的值设置为true,以表示该应用程序窗口需要延迟显示出来,即需要等到与该应用程序窗口所关联的一个activity组件的其它窗口也可以显示出来之后再显示。

       --msurfaceshown的值被设置为false,表示应用程序窗口还没有显示出来,它是用来显示调试信息的。

       --msurfacelayer的值被设置为0,表示应用程序窗口的z轴位置,它是用来显示调试信息的。

       --msurfacealpha的值被设置为1,表示应用程序窗口的透明值,它是用来显示调试信息的。

       --msurfacex的值被设置为0,表示应用程序程序窗口的x轴位置,它是用来显示调试信息的。

       --msurfacey的值被设置为0,表示应用程序程序窗口的y轴位置,它是用来显示调试信息的。

       --msurfacew的值被设置为w,表示应用程序程序窗口的宽度,它是用来显示调试信息的。

       --msurfaceh的值被设置为h,表示应用程序程序窗口的高度,它是用来显示调试信息的。

       一个应用程序窗口的绘图表面在创建完成之后,函数就会将得到的一个surface对象保存在当前正在处理的windowstate对象的成员变量msurface中。注意,如果创建绘图表面失败,并且从surface类的构造函数抛出来的异常的类型为surface.outofresourcesexception,那么就说明系统当前的内存不足了,这时候函数就会调用windowmanagerservice类的成员函数reclaimsomesurfacememorylocked来回收内存。

       如果一切正常,那么函数接下来还会设置当前正在处理的windowstate对象所描述应用程序窗口的以下属性:

      1. x轴和y轴位置。前面提到,windowstate类的成员变量mframe是用来描述应用程序窗口的位置和大小的,其中,位置就是通过它所描述的一个rect对象的成员变量left和top来表示的,它们分别应用窗口在x轴和y轴上的位置。此外,当一个windowstate对象所描述的应用程序窗口是一个壁纸窗口时,该windowstate对象的成员变量mxoffset和myoffset用来描述壁纸窗口相对当前要显示的窗口在x轴和y轴上的偏移量。因此,将windowstate类的成员变量mxoffset的值加上另外一个成员变量mframe所描述的一个rect对象的成员变量left的值,就可以得到一个应用程序窗口在x轴上的位置,同样,将windowstate类的成员变量myoffset的值加上另外一个成员变量mframe所描述的一个rect对象的成员变量top的值,就可以得到一个应用程序窗口在y轴上的位置。最终得到的位置值就分别保存在windowstate类的成员变量msurfacex和msurfacey,并且会调用windowstate类的成员变量msurface所描述的一个surface对象的成员函数setposition来将它们设置到surfaceflinger服务中去。

      3. 抖动标志。如果windowstate类的成员变量mattr所描述的一个windowmanager.layoutparams对象的成员变量flags的windowmanager.layoutparams.flag_dither位不等于0,那么就说明一个应用程序窗口的图形缓冲区在渲染时,需要进行抖动处理,这时候就会调用windowstate类的成员变量msurface所描述的一个surface对象的成员函数setlayer来将对应的应用程序窗口的图形缓冲区的属性标志的surface.surface_dither位设置为1。

      4. 显示状态。由于当前正在处理的windowstate对象所描述的一个应用程序窗口的绘图表面刚刚创建出来,因此,我们就需要通知surfaceflinger服务将它隐藏起来,这是通过调用当前正在处理的windowstate对象的成员变量msurface所描述的一个surface对象的成员变量hide来实现的。这时候还会将当前正在处理的windowstate对象的成员变量msurfaceshown和mlasthidden的值分别设置为false和true,以表示对应的应用程序窗口是处于隐藏状态的。

      注意,为了避免surfaceflinger服务每设置一个应用程序窗口属性就重新渲染一次系统的ui,上述4个属性设置需要在一个事务中进行,这样就可以避免出现界面闪烁。我们通过调用surface类的静态成员函数opentransaction和closetransaction就可以分别在surfaceflinger服务中打开和关闭一个事务。

      还有另外一个地方需要注意的是,在设置应用程序窗口属性的过程中,如果抛出了一个runtimeexception异常,那么就说明系统当前的内存不足了,这时候函数也会调用windowmanagerservice类的成员函数reclaimsomesurfacememorylocked来回收内存。

      接下来,我们就继续分析surface类的构造函数的实现,以便可以了解一个应用程序窗口的绘图表面的详细创建过程。

       step 9. new surface

android之surfaceview画图

public class surface implements parcelable {  

    private int msurfacecontrol;  

    private canvas mcanvas;  

    private string mname;  

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

        mname = name;  

    private native void init(surfacesession s,  

            throws outofresourcesexception;  

        这个函数定义在文件frameworks/base/core/java/android/view/surface.java中。

        surface类有三个成员变量msurfacecontrol、mcanvas和mname,它们的类型分别是int、canvas和mname,其中,msurfacecontrol保存的是在c++层的一个surfacecontrol对象的地址值,mcanvas用来描述一块类型为compatiblecanvas的画布,mname用来描述当前正在创建的一个绘图表面的名称。画布是真正用来绘制ui的地方,不过由于现在正在创建的绘图表面是在windowmanagerservice服务这一侧使用的,而windowmanagerservice服务不会去绘制应用程序窗口的ui,它只会去设置应用程序窗口的属性,因此,这里创建的画布实际上没有什么作用,我们主要关注与成员变量msurfacecontrol所关联的c++层的surfacecontrol对象是如何创建的。

        surface类的构造函数是通过调用另外一个成员函数init来创建与成员变量msurfacecontrol所关联的c++层的surfacecontrol对象的。surface类的成员函数init是一个jni方法,它是由c++层的函数surface_init来实现的,如下所示:

android之surfaceview画图

static void surface_init(  

        jnienv* env, jobject clazz,  

        jobject session,  

        jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jint flags)  

    if (session == null) {  

        dothrow(env, "java/lang/nullpointerexception");  

        return;  

    surfacecomposerclient* client =  

            (surfacecomposerclient*)env->getintfield(session, sso.client);  

    sp<surfacecontrol> surface;  

    if (jname == null) {  

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

    if (surface == 0) {  

        dothrow(env, outofresourcesexception);  

    setsurfacecontrol(env, clazz, surface);  

        这个函数定义在文件frameworks/base/core/jni/android_view_surface.cpp中。

        因此,函数首先将参数session所指向的一个java层的surfacesession对象的成员变量mclient转换成一个surfacecomposerclient对象,然后再调用这个surfacecomposerclient对象的成员函数createsurface来请求surfaceflinger服务来为参数clazz所描述的一个java层的surface对象所关联的应用程序窗口创建一个layer对象。surfaceflinger服务创建完成这个layer对象之后,就会将该layer对象内部的一个实现了isurface接口的surfacelayer对象返回给函数,于是,函数就可以获得一个实现了isurface接口的binder代理对象。这个实现了isurface接口的binder代理对象被封装在c++层的一个surfacecontrol对象surface中。

       注意,sso是一个全局变量,它是一个类型为sso_t的结构体,它的定义如下所示:

android之surfaceview画图

struct sso_t {  

    jfieldid client;  

};  

static sso_t sso;  

        它的成员函数client用来描述java层中的surfacesession类的成员变量mclient在类中的偏移量,因此,函数surface_init通过这个偏移量就可以访问参数session所指向的一个surfacesession对象的成员变量mclient的值,从而获得一个surfacecomposerclient对象。

       得到了surfacecontrol对象surface之后,函数surface_init接下来继续调用另外一个函数setsurfacecontrol来它的地址值保存在参数clazz所指向的一个java层的surface对象的成员变量msurfacecontrol中,如下所示:

android之surfaceview画图

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

        在分析函数setsurfacecontrol的实现之前,我们先分析全局变量so的定义,如下所示:

android之surfaceview画图

struct so_t {  

    jfieldid surfacecontrol;  

    jfieldid surface;  

    jfieldid savecount;  

    jfieldid canvas;  

static so_t so;  

        它是一个类型为so_t的结构体。结构体so_t有四个成员变量surfacecontrol、surface、savecount和canvas,它们分别用来描述java层的surface类的四个成员变量msurfacecontrol、mnativesurface、msavecount和mcanvas在类中的偏移量。

       回到函数setsurfacecontrol中,它首先通过结构体so的成员变量surfacecontrol来获得参数clazz所指向的一个java层的surface对象的成员变量msurfacecontrol所关联的一个c++层的surfacecontrol对象。如果这个surfacecontrol对象存在,那么变量p的值就不会等于null,在这种情况下,就需要调用它的成员函数decstrong来减少它的强引用计数,因为接下来参数clazz所指向的一个java层的surface对象不再通过成员变量msurfacecontrol来引用它了。

       另一方面,函数setsurfacecontrol需要增加参数surface所指向的一个c++层的surfacecontrol对象的强引用计数,即调用参数surface所指向的一个c++层的surfacecontrol对象的成员函数incstrong,因为接下来参数clazz所指向的一个java层的surface对象要通过成员变量msurfacecontrol来引用它,即将它的地址值保存在参数clazz所指向的一个java层的surface对象的成员变量msurfacecontrol中。

       这一步执行完成之后,沿着调用路径层层返回,回到前面的step 8中,即windowstate类的成员函数createsurfacelocked中,这时候一个绘图表面就创建完成了。这个绘图表面最终会返回给请求创建它的应用程序进程,即前面的step 5,也就是iwindowsession.stub.proxy类的成员函数relayout。

       iwindowsession.stub.proxy类的成员函数relayout获得了从windowmanagerservice服务返回来的一个绘图表面,即一个java层的surface对象之后,就会将它的内容拷贝到参数outsurface所描述的另外一个java层的surface对象中,这里通过调用surface类的成员函数readfromparcel来实现的。注意,参数outsurface所描述的这个java层的surface对象是在应用程序进程这一侧创建的,它的作用是用来绘制应用程序窗品的ui。

       接下来,我们就继续分析surface类的成员函数readfromparcel的实现,以便可以了解在应用程序进程这一侧的surface对象是如何创建的。

       step 10. surface.readfromparcel

android之surfaceview画图

    public native   void readfromparcel(parcel source);  

       这个函数定义在文件frameworks/base/core/java/android/view/surface.java中。

       surface类的成员函数readfromparcel是一个jni方法,它是由c++层的函数surface_readfromparcel来实现的,如下所示:

android之surfaceview画图

static void surface_readfromparcel(  

        jnienv* env, jobject clazz, jobject argparcel)  

    parcel* parcel = (parcel*)env->getintfield( argparcel, no.native_parcel);  

    if (parcel == null) {  

        dothrow(env, "java/lang/nullpointerexception", null);  

    sp<surface> sur(surface::readfromparcel(*parcel));  

    setsurface(env, clazz, sur);  

       这个函数定义在文件frameworks/base/core/jni/android_view_surface.cpp中。

       参数argparcel所指向的一个parcel对象的当前位置保存的是一个java层的surface对象的内容,函数readfromparcel首先调用c++层的surface类的成员函数readfromparcel来将这些内容封装成一个c++层的surface对象,如下所示:

android之surfaceview画图

sp<surface> surface::readfromparcel(const parcel& data) {  

    mutex::autolock _l(scachedsurfaceslock);  

    sp<ibinder> binder(data.readstrongbinder());  

    sp<surface> surface = scachedsurfaces.valuefor(binder).promote();  

       surface = new surface(data, binder);  

       scachedsurfaces.add(binder, surface);  

    if (surface->msurface == 0) {  

      surface = 0;  

    cleancachedsurfaceslocked();  

    return surface;  

        这个函数定义在文件frameworks/base/libs/surfaceflinger_client/surface.cpp中。

        参数data所指向的一个parcel对象的当前位置保存的是一个binder代理对象,这个binder代理对象实现了isurface接口,它所引用的binder本地对象就是在前面的step 9中windowmanagerservice服务请求surfaceflinger服务所创建的一个layer对象的内部的一个surfacelayer对象。

        获得了一个实现了isurface接口的binder代理对象binder之后,c++层的surface类的成员函数readfromparcel就可以将它封装在一个c++层的surface对象中了,并且将这个c++层的surface对象返回给调用者。注意,c++层的surface类的成员函数readfromparcel在创建为binder代理对象binder创建一个c++层的surface对象之前,首先会在c++层的surface类的静态成员变量scachedsurfaces所描述的一个defaultkeyedvectort向量中检查是否已经为存在一个对应的c++层的surface对象了。如果已经存在,那么就会直接将这个c++层的surface对象返回给调用者。

        回到函数surface_readfromparcel中,接下来它就会调用另外一个函数setsurface来将前面所获得的一个c++层的surface对象sur保存在参数clazz所描述的一个java层的surface对象的成员变量mnativesurface中,如下所示:

android之surfaceview画图

static void setsurface(jnienv* env, jobject clazz, const sp<surface>& surface)  

    surface* const p = (surface*)env->getintfield(clazz, so.surface);  

    env->setintfield(clazz, so.surface, (int)surface.get());  

       全局变量so是一个类型为so_t的结构体,在前面的step 9中,我们已经分析过它的定义了,函数setsurface首先通过它的成员变量surface来将参数clazz所描述的一个java层的surface对象的成员变量mnativesurface转换成一个surface对象p。如果这个surface对象p存在,那么就需要调用它的成员函数decstrong来减少它的强引用计数,因为接下来参数clazz所描述的一个java层的surface对象不再通过成员变量mnativesurface来引用它了。

       函数setsurface接下来就会将参数surface所指向的一个c++层的surface对象的地址值保存在参数clazz所描述的一个java层的surface对象的成员变量mnativesurface中。在执行这个操作之前,函数setsurface需要调用参数surface所指向的一个c++层的surface对象的成员函数incstrong来增加它的强引用计数,这是因为接下来它要被参数clazz所描述的一个java层的surface对象通过成员变量mnativesurface来引用了。

       至此,我们就分析完成android应用程序窗口的绘图表面的创建过程了。通过这个过程我们就可以知道:

       1. 每一个应用程序窗口都对应有两个java层的surface对象,其中一个是在windowmanagerservice服务这一侧创建的,而另外一个是在应用程序进程这一侧创建的。

       2. 在windowmanagerservice服务这一侧创建的java层的surface对象在c++层关联有一个surfacecontrol对象,用来设置应用窗口的属性,例如,大小和位置等。

       3. 在应用程序进程这一侧创建的ava层的surface对象在c++层关联有一个surface对象,用来绘制应用程序窗品的ui。

       理解上述三个结论对理解android应用程序窗口的实现框架以及windowmanagerservice服务的实现都非常重要。 一个应用程序窗口的绘图表面在创建完成之后,接下来应用程序进程就可以在上面绘制它的ui了。在接下来的一篇文章中,我们就继续分析android应用程序窗品的绘制过程,敬请关注!