Android SurfaceView原理分析

首先,我们看看SurfaceView的官方介绍:

Provides a dedicated drawing surface embedded inside of a view hierarchy. You can control the format of this surface and, if you like, its size; the SurfaceView takes care of placing the surface at the correct location on the screen

在View的层次结构中,嵌入了一个用于画图的Surface,你可以控制这个Surface的格式,像大小;SurfaceView负责将Surface放置在屏幕上的正确位置。

One of the purposes of this class is to provide a surface in which a secondary thread can render into the screen. If you are going to use it this way, you need to be aware of some threading semantics:

这个类的其中一个目的就是提供一个Surface,它可以在第二个线程中渲染到屏幕。如果你打算使用这个,那么需要主要:

All SurfaceView and SurfaceHolder.Callback methods will be called from the thread running the SurfaceView’s window (typically the main thread of the application). They thus need to correctly synchronize with any state that is also touched by the drawing thread.

所有的SurfaceView和SurfaceView.Callback方法都运行SurfaceView的那个窗口的线程被调用。因此,它们需要与绘图线程也触及的任何状态正确同步

You must ensure that the drawing thread only touches the underlying Surface while it is valid – between SurfaceHolder.Callback.surfaceCreated() and SurfaceHolder.Callback.surfaceDestroyed().

你必须确保绘图线程仅在其有效时触及底层Surface。在SurfaceHolder.Callback.surfaceCreated() 和 SurfaceHolder.Callback.surfaceDestroyed()之间。

也就是说,SurfaceView拥有独立的绘图表面Surface,即它不与其宿主窗口共享一个Surface。由于拥有独立的Surface,因此SurfaceView的UI就可以在一个单独的线程中进行绘制。
对于Android普通的控件,例如TextView,Button等,它们都是将自己的UI绘制在宿主窗口的绘图表面上,这意味着它们的UI在是应用线程的主线程中进行绘制的。当我们对UI绘制的操作花费很多时间,就很有可能被系统认为应用程序没有响应了,因此就会弹出一个ANR对话框出来。

关于SurfaceView的使用可以参考:
https://juejin.im/entry/59671fcd5188255d1c6a5628

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

从Java层的UI绘制过程中,我们可以知道,当要需要绘制UI时,会调用ViewRootImpl的performTraversals方法。在该方法中,如果发现当前窗口的绘图表面还没有创建或者是无效的,就会请求WindowManagerService去创建一个新的绘图表面,同时它会通过一系列的回调方法来让嵌入在窗口里面的SurfaceView有机会创建自己的绘图表面。

  private void performTraversals() {
   
        // cache mView since it is used so much below...
        final View host = mView;
       ... ...
        mIsInTraversal = true;
        mWillDrawSoon = true;
       ... ...
        WindowManager.LayoutParams lp = mWindowAttributes;
       ... ...
        final int viewVisibility = getHostVisibility();
        final boolean viewVisibilityChanged = !mFirst
                && (mViewVisibility != viewVisibility || mNewSurfaceNeeded
                // Also check for possible double visibility update, which will make current
                // viewVisibility value equal to mViewVisibility and we may miss it.
                || mAppVisibilityChanged);
        ... ...
        if (mFirst) {
   
            ... ...
           
            host.dispatchAttachedToWindow(mAttachInfo, 0);
            mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
           ... ...
        } else {
   
           ... ...
        }
        if (viewVisibilityChanged) {
   
            mAttachInfo.mWindowVisibility = viewVisibility;
            host.dispatchWindowVisibilityChanged(viewVisibility);
           ... ...
        }
        ... ...
        mFirst = false;
       ... ...
        mIsInTraversal = false;
    }

我们先看一下这几个变量的含义:

  • host由mView赋值,指向一个DecorView
  • mAttachInfo,在Android系统中,每一个视图附加到它的宿主窗口的时候,都会获得一个AttachInfo对象,用来描述被附加的窗口的信息
  • viewVisibility 描述的是当前窗口的可见性
  • viewVisibilityChanged描述的是当前窗口的可见性是否发生了变化

ViewRootImpl类的成员变量mFirst表示当前窗口是否是第一次UI,如果是的话,它的值就会为true,这个时候,当前窗口的Surface没有创建,这个时候,就会调用host.dispatchAttachedToWindow(mAttachInfo, 0);来通知每一个子视图要被附加到宿主窗口上了。
接下来,检查viewVisibilityChanged的值是否为true,如果发生了变化,就会从当前窗口的顶层视图开始,通知每一个子视图它的宿主窗口的可见性发生了变化。这是通过调用host.dispatchWindowVisibilityChanged(viewVisibility);来实现的。

我们先看host.dispatchAttachedToWindow(mAttachInfo, 0)
我们知道host指向的是一个DecorView对象,这个方法,DecorView没有重写,它的父类FrameLayout也没有重写,因此它在ViewGroup中:

 @Override
    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
   
        mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
        super.dispatchAttachedToWindow(info, visibility);
        mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;

        final int count = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < count; i++) {
   
            final View child = children[i];
            child.dispatchAttachedToWindow(info,
                    combineVisibility(visibility, child.getVisibility()));
        }
        final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
        for (int i = 0; i < transientCount; ++i) {
   
            View view = mTransientViews.get(i);
            view.dispatchAttachedToWindow(info,
                    combineVisibility(visibility
  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值