zoukankan      html  css  js  c++  java
  • SurfaceView

      在Android系统中,有一种特殊的视图,称为SurfaceView,它拥有独立的绘图表面,即它不与其宿主窗口共享同一个绘图表面。由于拥有独立的绘图表面,因此SurfaceView的UI就可以在一个独立的线程中进行绘制。又由于不会占用主线程资源,SurfaceView一方面可以实现复杂而高效的UI,另一方面又不会导致用户输入得不到及时响应。在本文中,我们就详细分析SurfaceView的实现原理。

    android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!

            在前面Android控件TextView的实现原理分析一文中提到,普通的Android控件,例如TextView、Button和CheckBox等,它们都是将自己的UI绘制在宿主窗口的绘图表面之上,这意味着它们的UI是在应用程序的主线程中进行绘制的。由于应用程序的主线程除了要绘制UI之外,还需要及时地响应用户输入,否则的话,系统就会认为应用程序没有响应了,因此就会弹出一个ANR对话框出来。对于一些游戏画面,或者摄像头预览、视频播放来说,它们的UI都比较复杂,而且要求能够进行高效的绘制,因此,它们的UI就不适合在应用程序的主线程中进行绘制。这时候就必须要给那些需要复杂而高效UI的视图生成一个独立的绘图表面,以及使用一个独立的线程来绘制这些视图的UI。

            在前面Android应用程序与SurfaceFlinger服务的关系概述和学习计划Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划这两个系统的文章中,我们主要分析了Android应用程序窗口是如何通过SurfaceFlinger服务来绘制自己的UI的。一般来说,每一个窗口在SurfaceFlinger服务中都对应有一个Layer,用来描述它的绘图表面。对于那些具有SurfaceView的窗口来说,每一个SurfaceView在SurfaceFlinger服务中还对应有一个独立的Layer或者LayerBuffer,用来单独描述它的绘图表面,以区别于它的宿主窗口的绘图表面。

            无论是LayerBuffer,还是Layer,它们都是以LayerBase为基类的,也就是说,SurfaceFlinger服务把所有的LayerBuffer和Layer都抽象为LayerBase,因此就可以用统一的流程来绘制和合成它们的UI。由于LayerBuffer的绘制和合成与Layer的绘制和合成是类似的,因此本文不打算对LayerBuffer的绘制和合成操作进行分析。需要深入理解LayerBuffer的绘制和合成操作的,可以参考Android应用程序与SurfaceFlinger服务的关系概述和学习计划Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划这两个系统的文章。

            为了接下来可以方便地描述SurfaceView的实现原理分析,我们假设在一个Activity窗口的视图结构中,除了有一个DecorView顶层视图之外,还有两个TextView控件,以及一个SurfaceView视图,这样该Activity窗口在SurfaceFlinger服务中就对应有两个Layer或者一个Layer的一个LayerBuffer,如图1所示:

    图1 SurfaceView及其宿主Activity窗口的绘图表面示意图

             在图1中,Activity窗口的顶层视图DecorView及其两个TextView控件的UI都是绘制在SurfaceFlinger服务中的同一个Layer上面的,而SurfaceView的UI是绘制在SurfaceFlinger服务中的另外一个Layer或者LayerBuffer上的。

             注意,用来描述SurfaceView的Layer或者LayerBuffer的Z轴位置是小于用来其宿主Activity窗口的Layer的Z轴位置的,但是前者会在后者的上面挖一个“洞”出来,以便它的UI可以对用户可见。实际上,SurfaceView在其宿主Activity窗口上所挖的“洞”只不过是在其宿主Activity窗口上设置了一块透明区域。

            从总体上描述了SurfaceView的大致实现原理之后,接下来我们就详细分析它的具体实现过程,包括它的绘图表面的创建过程、在宿主窗口上面进行挖洞的过程,以及绘制过程。

            1. SurfaceView的绘图表面的创建过程

            由于SurfaceView具有独立的绘图表面,因此,在它的UI内容可以绘制之前,我们首先要将它的绘图表面创建出来。尽管SurfaceView不与它的宿主窗口共享同一个绘图表面,但是它仍然是属于宿主窗口的视图结构的一个结点的,也就是说,SurfaceView仍然是会参与到宿主窗口的某些执行流程中去。

            从前面Android应用程序窗口(Activity)的绘图表面(Surface)的创建过程分析一文可以知道,每当一个窗口需要刷新UI时,就会调用ViewRoot类的成员函数performTraversals。ViewRoot类的成员函数performTraversals在执行的过程中,如果发现当前窗口的绘图表面还没有创建,或者发现当前窗口的绘图表面已经失效了,那么就会请求WindowManagerService服务创建一个新的绘图表面,同时,它还会通过一系列的回调函数来让嵌入在窗口里面的SurfaceView有机会创建自己的绘图表面。

    一、Surface
    
       Surface在SDK的文档中的描述是这样的:Handle onto a raw buffer that is being managed by the screen compositor,Android中的Surface就是一个用来画图形(graphics)或图像(image)的地方,对于View及其子类,都是画在Surface上,各Surface对象通过Surfaceflinger合成到frameBuffer,每个Surface都是双缓冲,它有一个backBuffer和一个frontBuffer,Surface中创建了Canvas对象,用来管理Surface绘图操作,Canvas对应Bitmap,存储Surface中的内容。流程为:
    
       1:创建一个Bitmap对象。
    
         2:创建一个Canvas对象关联创建的Bitmap对象。
    
         3:在Canvas上进行绘制。
    
         4:锁定Canvas画布。
    
         5:将Bitmap内容绘制到backBuffer中去。
    
         6:解锁Canvas画布。
    二、SurfaceView
    
       SurfaceView是视图类View的子类,且实现了Parcelable接口且实现了Parcelable接口,其中内嵌了一个专门用于绘制的Surface,SurfaceView可以控制这个Surface的格式和尺寸,以及Surface的绘制位置。可以理解为Surface就是管理数据的地方,SurfaceView就是展示数据的地方。
    三、SurfaceHolder
    
       SurfaceHolder是一个接口,类似于一个surace的监听器。通过下面三个回调方法监听Surface的创建、销毁或者改变。
    
        SurfaceView中调用getHolder方法,可以获得当前SurfaceView中的surface对应的SurfaceHolder,SurfaceHolder中重要的方法有:
    
        1: abstract  void addCallback(SurfaceHolder.Callback callback );为SurfaceHolder添加一个SurfaceHolder.Callback回调接口。
    
       2:  abstract  Canvas lockCanvas() ;获取Surface中的Canvas对象,并锁定之。所得到的Canvas对象。
    
        3:abstract  void unlockCanvasAndPost(Canvas canvas);当修改Surface中的数据完成后,释放同步锁,并提交改变,然后将新的数据进行展示。
    四、SurfaceHolder.Callback
    
       SurfaceHolder.Callback是SurfaceHolder接口内部的静态子接口,SurfaceHolder.Callback中定义了三个接口方法:
       1:public void sufaceChanged(SurfaceHolder holder,int format,int width,int height){}//Surface的大小发生改变时调用。
       2: public void surfaceCreated(SurfaceHolder holder){}//Surface创建时激发,一般在这里调用画面的线程。
       3: public void surfaceDestroyed(SurfaceHolder holder){}//销毁时激发,一般在这里将画面的线程停止、释放。
    
        SurfaceView和View最本质的区别在于:SurfaceView是在一个新起的单独线程中可以重新绘制画面而View必须在UI的主线程中更新画面。下面是SurfaceView的例子:
    
    [java] view plain copy
    
        import android.content.Context;  
        import android.graphics.Bitmap;  
        import android.graphics.Canvas;  
        import android.graphics.Color;  
        import android.graphics.Paint;  
        import android.graphics.Paint.Style;  
        import android.graphics.drawable.BitmapDrawable;  
        import android.view.SurfaceHolder;  
        import android.view.SurfaceView;  
        import android.view.KeyEvent;  
        import android.view.MotionEvent;  
        import android.view.SurfaceHolder.Callback;  
          
        public class MySurfaceView extends SurfaceView implements Runnable, Callback {  
            private SurfaceHolder mHolder; // 用于控制SurfaceView  
            private Thread t; // 声明一条线程  
            private volatile boolean flag; // 线程运行的标识,用于控制线程  
            private Canvas mCanvas; // 声明一张画布  
            private Paint p; // 声明一支画笔  
            float m_circle_r = 10;  
          
            public MySurfaceView(Context context) {  
                super(context);  
          
                mHolder = getHolder(); // 获得SurfaceHolder对象  
                mHolder.addCallback(this); // 为SurfaceView添加状态监听  
                p = new Paint(); // 创建一个画笔对象  
                p.setColor(Color.WHITE); // 设置画笔的颜色为白色  
                setFocusable(true); // 设置焦点  
            }  
          
            /** 
             * 当SurfaceView创建的时候,调用此函数 
             */  
            @Override  
            public void surfaceCreated(SurfaceHolder holder) {  
                t = new Thread(this); // 创建一个线程对象  
                flag = true; // 把线程运行的标识设置成true  
                t.start(); // 启动线程  
            }  
          
            /** 
             * 当SurfaceView的视图发生改变的时候,调用此函数 
             */  
            @Override  
            public void surfaceChanged(SurfaceHolder holder, int format, int width,  
                    int height) {  
            }  
          
            /** 
             * 当SurfaceView销毁的时候,调用此函数 
             */  
            @Override  
            public void surfaceDestroyed(SurfaceHolder holder) {  
                flag = false; // 把线程运行的标识设置成false  
                mHolder.removeCallback(this);  
            }  
          
            /** 
             * 当屏幕被触摸时调用 
             */  
            @Override  
            public boolean onTouchEvent(MotionEvent event) {  
          
                return true;  
            }  
          
            /** 
             * 当用户按键时调用 
             */  
            @Override  
            public boolean onKeyDown(int keyCode, KeyEvent event) {  
                if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {  
                }  
                return super.onKeyDown(keyCode, event);  
            }  
          
            @Override  
            public boolean onKeyUp(int keyCode, KeyEvent event) {  
                surfaceDestroyed(mHolder);  
                return super.onKeyDown(keyCode, event);  
            }  
          
            @Override  
            public void run() {  
                while (flag) {  
                    try {  
                        synchronized (mHolder) {  
                            Thread.sleep(100); // 让线程休息100毫秒  
                            Draw(); // 调用自定义画画方法  
                        }  
                    } catch (InterruptedException e) {  
                        e.printStackTrace();  
                    } finally {  
                        if (mCanvas != null) {  
                            // mHolder.unlockCanvasAndPost(mCanvas);//结束锁定画图,并提交改变。  
          
                        }  
                    }  
                }  
            }  
          
            /** 
             * 自定义一个方法,在画布上画一个圆 
             */  
            protected void Draw() {  
                mCanvas = mHolder.lockCanvas(); // 获得画布对象,开始对画布画画  
                if (mCanvas != null) {  
                    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);  
                    paint.setColor(Color.BLUE);  
                    paint.setStrokeWidth(10);  
                    paint.setStyle(Style.FILL);  
                    if (m_circle_r >= (getWidth() / 10)) {  
                        m_circle_r = 0;  
                    } else {  
                        m_circle_r++;  
                    }  
                    Bitmap pic = ((BitmapDrawable) getResources().getDrawable(  
                            R.drawable.qq)).getBitmap();  
                    mCanvas.drawBitmap(pic, 0, 0, paint);  
                    for (int i = 0; i < 5; i++)  
                        for (int j = 0; j < 8; j++)  
                            mCanvas.drawCircle(  
                                    (getWidth() / 5) * i + (getWidth() / 10),  
                                    (getHeight() / 8) * j + (getHeight() / 16),  
                                    m_circle_r, paint);  
                    mHolder.unlockCanvasAndPost(mCanvas); // 完成画画,把画布显示在屏幕上  
                }  
            }  
        }  
  • 相关阅读:
    How to build Linux system from kernel to UI layer
    Writing USB driver for Android
    Xposed Framework for Android 8.x Oreo is released (in beta)
    Linux Smartphone Operating Systems You Can Install Today
    Librem 5 Leads New Wave of Open Source Mobile Linux Contenders
    GUADEC: porting GNOME to Android
    Librem 5 – A Security and Privacy Focused Phone
    GNOME and KDE Join Librem 5 Linux Smartphone Party
    Purism计划推出安全开源的Linux Librem 5智能手机
    国产系统之殇:你知道的这些系统都是国外的
  • 原文地址:https://www.cnblogs.com/yaowen/p/7283834.html
Copyright © 2011-2022 走看看