首页 > 代码库 > 017.View与窗口:AttachInfo

017.View与窗口:AttachInfo

每一个View都需要依赖于窗口来显示,而View和窗口的关系则是放在View.AttachInfo中,关于View.AttachInfo的文章少,因为这个是View的内部类而且不是公共的,在应用层用的很少,只有在ViewRootImpl等类中才用到了,不过我觉得这个还是有点学习的必要,因此在这篇文章中就从源码入手学习下AttachInfo这个类。


    AttachInfo 看到这个类名,我们就知道,他是代表着绑定的信息,View.AttachInfo 里面的信息,就是View和Window之间的信息。每一个被添加到窗口上的View我们都会看到有一个AttachInfo,比如我们看DecorView和Window的绑定,可以在ViewRootImpl#perfromTraversals方法中看到:

[java] view plain copy 技术分享技术分享
  1. final View.AttachInfo attachInfo = mAttachInfo;  
  2.   
  3.        final int viewVisibility = getHostVisibility();  
  4.        boolean viewVisibilityChanged = mViewVisibility != viewVisibility  
  5.                || mNewSurfaceNeeded;  
  6.   
  7.        WindowManager.LayoutParams params = null;  
  8.        if (mWindowAttributesChanged) {  
  9.            mWindowAttributesChanged = false;  
  10.            surfaceChanged = true;  
  11.            params = lp;  
  12.        }  
  13.        CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();  
  14.        if (compatibilityInfo.supportsScreen() == mLastInCompatMode) {  
  15.            params = lp;  
  16.            mFullRedrawNeeded = true;  
  17.            mLayoutRequested = true;  
  18.            if (mLastInCompatMode) {  
  19.                params.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;  
  20.                mLastInCompatMode = false;  
  21.            } else {  
  22.                params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;  
  23.                mLastInCompatMode = true;  
  24.            }  
  25.        }  
  26.   
  27.        mWindowAttributesChangesFlag = 0;  
  28.   
  29.        Rect frame = mWinFrame;  
  30.        if (mFirst) {  
  31.            mFullRedrawNeeded = true;  
  32.            mLayoutRequested = true;  
  33.   
  34.            if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL  
  35.                    || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {  
  36.                // NOTE -- system code, won‘t try to do compat mode.  
  37.                Point size = new Point();  
  38.                mDisplay.getRealSize(size);  
  39.                desiredWindowWidth = size.x;  
  40.                desiredWindowHeight = size.y;  
  41.            } else {  
  42.                DisplayMetrics packageMetrics =  
  43.                    mView.getContext().getResources().getDisplayMetrics();  
  44.                desiredWindowWidth = packageMetrics.widthPixels;  
  45.                desiredWindowHeight = packageMetrics.heightPixels;  
  46.            }  
  47.   
  48.            // For the very first time, tell the view hierarchy that it  
  49.            // is attached to the window.  Note that at this point the surface  
  50.            // object is not initialized to its backing store, but soon it  
  51.            // will be (assuming the window is visible).  
  52.            attachInfo.mSurface = mSurface;  
  53.            // We used to use the following condition to choose 32 bits drawing caches:  
  54.            // PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888  
  55.            // However, windows are now always 32 bits by default, so choose 32 bits  
  56.            attachInfo.mUse32BitDrawingCache = true;  
  57.            attachInfo.mHasWindowFocus = false;  
  58.            attachInfo.mWindowVisibility = viewVisibility;  
  59.            attachInfo.mRecomputeGlobalAttributes = false;  
  60.            viewVisibilityChanged = false;  
  61.            mLastConfiguration.setTo(host.getResources().getConfiguration());  
  62.            mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;  
  63.            // Set the layout direction if it has not been set before (inherit is the default)  
  64.            if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {  
  65.                host.setLayoutDirection(mLastConfiguration.getLayoutDirection());  
  66.            }  
  67.            host.dispatchAttachedToWindow(attachInfo, 0);  
  68.    


AttachInfo 会通过View的diapatchAttachedTowWindow分发给View。如果是一个ViewGroup 那么这个这个AttachInfo也会分发给所有子View,以引用的方式。
下面我们可以看下AttachInfo这个类,这个类是View的内部类,不是声明为public的,所以只有view这个包中的类才用到。

[java] view plain copy 技术分享技术分享
  1. static class AttachInfo {  
  2.         interface Callbacks {  
  3.             void playSoundEffect(int effectId);  
  4.             boolean performHapticFeedback(int effectId, boolean always);  
  5.         }  
  6.   
  7.         /** 
  8.          * InvalidateInfo is used to post invalidate(int, int, int, int) messages 
  9.          * to a Handler. This class contains the target (View) to invalidate and 
  10.          * the coordinates of the dirty rectangle. 
  11.          * 
  12.          * For performance purposes, this class also implements a pool of up to 
  13.          * POOL_LIMIT objects that get reused. This reduces memory allocations 
  14.          * whenever possible. 
  15.          */  
  16.         static class InvalidateInfo {  
  17.             private static final int POOL_LIMIT = 10;  
  18.   
  19.             private static final SynchronizedPool<InvalidateInfo> sPool =  
  20.                     new SynchronizedPool<InvalidateInfo>(POOL_LIMIT);  
  21.   
  22.             View target;  
  23.   
  24.             int left;  
  25.             int top;  
  26.             int right;  
  27.             int bottom;  
  28.   
  29.             public static InvalidateInfo obtain() {  
  30.                 InvalidateInfo instance = sPool.acquire();  
  31.                 return (instance != null) ? instance : new InvalidateInfo();  
  32.             }  
  33.   
  34.             public void recycle() {  
  35.                 target = null;  
  36.                 sPool.release(this);  
  37.             }  
  38.         }  
  39.   
  40.         final IWindowSession mSession;  
  41.   
  42.         final IWindow mWindow;  
  43.   
  44.         final IBinder mWindowToken;  
  45.   
  46.         final Display mDisplay;  
  47.   
  48.         final Callbacks mRootCallbacks;  
  49.   
  50.         HardwareCanvas mHardwareCanvas;  
  51.   
  52.         IWindowId mIWindowId;  
  53.         WindowId mWindowId;  
  54.   
  55.         /** 
  56.          * The top view of the hierarchy. 
  57.          */  
  58.         View mRootView;  
  59.   
  60.         IBinder mPanelParentWindowToken;  
  61.         Surface mSurface;  
  62.   
  63.         boolean mHardwareAccelerated;  
  64.         boolean mHardwareAccelerationRequested;  
  65.         HardwareRenderer mHardwareRenderer;  
  66.   
  67.         boolean mScreenOn;  
  68.   
  69.         /** 
  70.          * Scale factor used by the compatibility mode 
  71.          */  
  72.         float mApplicationScale;  
  73.   
  74.         /** 
  75.          * Indicates whether the application is in compatibility mode 
  76.          */  
  77.         boolean mScalingRequired;  
  78.   
  79.         /** 
  80.          * If set, ViewRootImpl doesn‘t use its lame animation for when the window resizes. 
  81.          */  
  82.         boolean mTurnOffWindowResizeAnim;  
  83.   
  84.         /** 
  85.          * Left position of this view‘s window 
  86.          */  
  87.         int mWindowLeft;  
  88.   
  89.         /** 
  90.          * Top position of this view‘s window 
  91.          */  
  92.         int mWindowTop;  
  93.   
  94.         /** 
  95.          * Indicates whether views need to use 32-bit drawing caches 
  96.          */  
  97.         boolean mUse32BitDrawingCache;  
  98.   
  99.         /** 
  100.          * For windows that are full-screen but using insets to layout inside 
  101.          * of the screen areas, these are the current insets to appear inside 
  102.          * the overscan area of the display. 
  103.          */  
  104.         final Rect mOverscanInsets = new Rect();  
  105.   
  106.         /** 
  107.          * For windows that are full-screen but using insets to layout inside 
  108.          * of the screen decorations, these are the current insets for the 
  109.          * content of the window. 
  110.          */  
  111.         final Rect mContentInsets = new Rect();  
  112.   
  113.         /** 
  114.          * For windows that are full-screen but using insets to layout inside 
  115.          * of the screen decorations, these are the current insets for the 
  116.          * actual visible parts of the window. 
  117.          */  
  118.         final Rect mVisibleInsets = new Rect();  
  119.   
  120.         /** 
  121.          * The internal insets given by this window.  This value is 
  122.          * supplied by the client (through 
  123.          * {@link ViewTreeObserver.OnComputeInternalInsetsListener}) and will 
  124.          * be given to the window manager when changed to be used in laying 
  125.          * out windows behind it. 
  126.          */  
  127.         final ViewTreeObserver.InternalInsetsInfo mGivenInternalInsets  
  128.                 = new ViewTreeObserver.InternalInsetsInfo();  
  129.   
  130.         /** 
  131.          * Set to true when mGivenInternalInsets is non-empty. 
  132.          */  
  133.         boolean mHasNonEmptyGivenInternalInsets;  
  134.   
  135.         /** 
  136.          * All views in the window‘s hierarchy that serve as scroll containers, 
  137.          * used to determine if the window can be resized or must be panned 
  138.          * to adjust for a soft input area. 
  139.          */  
  140.         final ArrayList<View> mScrollContainers = new ArrayList<View>();  
  141.   
  142.         final KeyEvent.DispatcherState mKeyDispatchState  
  143.                 = new KeyEvent.DispatcherState();  
  144.   
  145.         /** 
  146.          * Indicates whether the view‘s window currently has the focus. 
  147.          */  
  148.         boolean mHasWindowFocus;  
  149.   
  150.         /** 
  151.          * The current visibility of the window. 
  152.          */  
  153.         int mWindowVisibility;  
  154.   
  155.         /** 
  156.          * Indicates the time at which drawing started to occur. 
  157.          */  
  158.         long mDrawingTime;  
  159.   
  160.         /** 
  161.          * Indicates whether or not ignoring the DIRTY_MASK flags. 
  162.          */  
  163.         boolean mIgnoreDirtyState;  
  164.   
  165.         /** 
  166.          * This flag tracks when the mIgnoreDirtyState flag is set during draw(), 
  167.          * to avoid clearing that flag prematurely. 
  168.          */  
  169.         boolean mSetIgnoreDirtyState = false;  
  170.   
  171.         /** 
  172.          * Indicates whether the view‘s window is currently in touch mode. 
  173.          */  
  174.         boolean mInTouchMode;  
  175.   
  176.         /** 
  177.          * Indicates that ViewAncestor should trigger a global layout change 
  178.          * the next time it performs a traversal 
  179.          */  
  180.         boolean mRecomputeGlobalAttributes;  
  181.   
  182.         /** 
  183.          * Always report new attributes at next traversal. 
  184.          */  
  185.         boolean mForceReportNewAttributes;  
  186.   
  187.         /** 
  188.          * Set during a traveral if any views want to keep the screen on. 
  189.          */  
  190.         boolean mKeepScreenOn;  
  191.   
  192.         /** 
  193.          * Bitwise-or of all of the values that views have passed to setSystemUiVisibility(). 
  194.          */  
  195.         int mSystemUiVisibility;  
  196.   
  197.         /** 
  198.          * Hack to force certain system UI visibility flags to be cleared. 
  199.          */  
  200.         int mDisabledSystemUiVisibility;  
  201.   
  202.         /** 
  203.          * Last global system UI visibility reported by the window manager. 
  204.          */  
  205.         int mGlobalSystemUiVisibility;  
  206.   
  207.         /** 
  208.          * True if a view in this hierarchy has an OnSystemUiVisibilityChangeListener 
  209.          * attached. 
  210.          */  
  211.         boolean mHasSystemUiListeners;  
  212.   
  213.         /** 
  214.          * Set if the window has requested to extend into the overscan region 
  215.          * via WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN. 
  216.          */  
  217.         boolean mOverscanRequested;  
  218.   
  219.         /** 
  220.          * Set if the visibility of any views has changed. 
  221.          */  
  222.         boolean mViewVisibilityChanged;  
  223.   
  224.         /** 
  225.          * Set to true if a view has been scrolled. 
  226.          */  
  227.         boolean mViewScrollChanged;  
  228.   
  229.         /** 
  230.          * Global to the view hierarchy used as a temporary for dealing with 
  231.          * x/y points in the transparent region computations. 
  232.          */  
  233.         final int[] mTransparentLocation = new int[2];  
  234.   
  235.         /** 
  236.          * Global to the view hierarchy used as a temporary for dealing with 
  237.          * x/y points in the ViewGroup.invalidateChild implementation. 
  238.          */  
  239.         final int[] mInvalidateChildLocation = new int[2];  
  240.   
  241.   
  242.         /** 
  243.          * Global to the view hierarchy used as a temporary for dealing with 
  244.          * x/y location when view is transformed. 
  245.          */  
  246.         final float[] mTmpTransformLocation = new float[2];  
  247.   
  248.         /** 
  249.          * The view tree observer used to dispatch global events like 
  250.          * layout, pre-draw, touch mode change, etc. 
  251.          */  
  252.         final ViewTreeObserver mTreeObserver = new ViewTreeObserver();  
  253.   
  254.         /** 
  255.          * A Canvas used by the view hierarchy to perform bitmap caching. 
  256.          */  
  257.         Canvas mCanvas;  
  258.   
  259.         /** 
  260.          * The view root impl. 
  261.          */  
  262.         final ViewRootImpl mViewRootImpl;  
  263.   
  264.         /** 
  265.          * A Handler supplied by a view‘s {@link android.view.ViewRootImpl}. This 
  266.          * handler can be used to pump events in the UI events queue. 
  267.          */  
  268.         final Handler mHandler;  
  269.   
  270.         /** 
  271.          * Temporary for use in computing invalidate rectangles while 
  272.          * calling up the hierarchy. 
  273.          */  
  274.         final Rect mTmpInvalRect = new Rect();  
  275.   
  276.         /** 
  277.          * Temporary for use in computing hit areas with transformed views 
  278.          */  
  279.         final RectF mTmpTransformRect = new RectF();  
  280.   
  281.         /** 
  282.          * Temporary for use in transforming invalidation rect 
  283.          */  
  284.         final Matrix mTmpMatrix = new Matrix();  
  285.   
  286.         /** 
  287.          * Temporary for use in transforming invalidation rect 
  288.          */  
  289.         final Transformation mTmpTransformation = new Transformation();  
  290.   
  291.         /** 
  292.          * Temporary list for use in collecting focusable descendents of a view. 
  293.          */  
  294.         final ArrayList<View> mTempArrayList = new ArrayList<View>(24);  
  295.   
  296.         /** 
  297.          * The id of the window for accessibility purposes. 
  298.          */  
  299.         int mAccessibilityWindowId = View.NO_ID;  
  300.   
  301.         /** 
  302.          * Flags related to accessibility processing. 
  303.          * 
  304.          * @see AccessibilityNodeInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS 
  305.          * @see AccessibilityNodeInfo#FLAG_REPORT_VIEW_IDS 
  306.          */  
  307.         int mAccessibilityFetchFlags;  
  308.   
  309.         /** 
  310.          * The drawable for highlighting accessibility focus. 
  311.          */  
  312.         Drawable mAccessibilityFocusDrawable;  
  313.   
  314.         /** 
  315.          * Show where the margins, bounds and layout bounds are for each view. 
  316.          */  
  317.         boolean mDebugLayout = SystemProperties.getBoolean(DEBUG_LAYOUT_PROPERTY, false);  
  318.   
  319.         /** 
  320.          * Point used to compute visible regions. 
  321.          */  
  322.         final Point mPoint = new Point();  
  323.   
  324.         /** 
  325.          * Used to track which View originated a requestLayout() call, used when 
  326.          * requestLayout() is called during layout. 
  327.          */  
  328.         View mViewRequestingLayout;  
  329.   
  330.         /** 
  331.          * Creates a new set of attachment information with the specified 
  332.          * events handler and thread. 
  333.          * 
  334.          * @param handler the events handler the view must use 
  335.          */  
  336.         AttachInfo(IWindowSession session, IWindow window, Display display,  
  337.                 ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer) {  
  338.             mSession = session;  
  339.             mWindow = window;  
  340.             mWindowToken = window.asBinder();  
  341.             mDisplay = display;  
  342.             mViewRootImpl = viewRootImpl;  
  343.             mHandler = handler;  
  344.             mRootCallbacks = effectPlayer;  
  345.         }  
  346.     }  

首先是声明了回调接口类:callBacks 这个类第一个是playSoundEffect ,这个用于播放按键声音,参数是这个点击事件的类型,可以看SoundEffectConstants中的声明,一般是SoundEffectConstants中几个常量中的一个,在AttachInfo有一个CallBack对象 :mRootCallBacks 这个的实现可以看ViewRootImpl类,ViewRootImpl中,我们可以看到:

[java] view plain copy 技术分享技术分享
  1. public void playSoundEffect(int effectId) {  
  2.       checkThread();  
  3.   
  4.       if (mMediaDisabled) {  
  5.           return;  
  6.       }  
  7.   
  8.       try {  
  9.           final AudioManager audioManager = getAudioManager();  
  10.   
  11.           switch (effectId) {  
  12.               case SoundEffectConstants.CLICK:  
  13.                   audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);  
  14.                   return;  
  15.               case SoundEffectConstants.NAVIGATION_DOWN:  
  16.                   audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN);  
  17.                   return;  
  18.               case SoundEffectConstants.NAVIGATION_LEFT:  
  19.                   audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT);  
  20.                   return;  
  21.               case SoundEffectConstants.NAVIGATION_RIGHT:  
  22.                   audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT);  
  23.                   return;  
  24.               case SoundEffectConstants.NAVIGATION_UP:  
  25.                   audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP);  
  26.                   return;  
  27.               default:  
  28.                   throw new IllegalArgumentException("unknown effect id " + effectId +  
  29.                           " not defined in " + SoundEffectConstants.class.getCanonicalName());  
  30.           }  
  31.       } catch (IllegalStateException e) {  
  32.           // Exception thrown by getAudioManager() when mView is null  
  33.           Log.e(TAG, "FATAL EXCEPTION when attempting to play sound effect: " + e);  
  34.           e.printStackTrace();  
  35.       }  
  36.   }  
那么我们在自定义的View上,重写playSoundEffect方法就可以了。每一个View都有playSoundEffect方法,我们可以改动这个方法。
CallBack中还有一个方法:
performHapticFeedback这个意思就是触感反馈,参数可以看HapticFeedBack这个类,当用户在系统打开触感反馈选项,我们View的performHapticFeedback(int feedBackContants )这个方法,当然,如果我们调用performHapticFeedback(int feedbackConstant, int flags) 的时候,把参数FLAG_IGNORE_GLOBAL_SETTING 就可以忽略全局设置,而如果我们HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING 就可以忽略我们在View里面设置的android:hapticFeedbackEnabled 
关于CallBack就讲到这里,接下来我们继续往下看InvalidateInfo。InvalidateInfo用于刷新UI,当我们刷新UI的时候,会生成一个新的InvalidateInfo对象,然后根据这个来刷新UI。这个比较简单,就不详细说了。
在AttachInfo中,还有其他的信息,在这边,我们可以拿到和Window相关的信息:
[java] view plain copy 技术分享技术分享
  1. <span style="white-space:pre">    </span>final IWindowSession mSession;  
  2. <span style="white-space:pre">    </span>  
  3.         final IWindow mWindow;  
  4.   
  5.         final IBinder mWindowToken;  
  6.         IBinder   mPanelParentWindowToken ;  

一般来说,IWinodwSession是通过:WindowManagerGlobal.peekWindowSession() 或者是WindowManagerGlobal.getWindowSession() 来获取的
两者一般来说是差不多的,就是peek返回的可能为空 get一般返回是不为空的。另外,IWindowSession 、IWindow 、mWindowToken 都是从IWindowManager.Stub.asInterface(ServiceManager.getService("window"))获取到IWindowManager来获取的。
相关的可以看前面的第10篇:Binder进阶:系统服务中的Binder 以及ServiceManager的源码来看。
mPanelParentWindowToken 如果该窗口时子窗口,那么该值就是父窗口的W对象,如果mWindowToken不为空,则说明没有父窗口…嗯,和mWindowToken有点相对的意思。比如说Activity 的DecorView 的AttachInfo这个值就是null,而我们弹出了一个对话框,这个对话框的这个就不为null,因为这个对话框是有父窗口的。


017.View与窗口:AttachInfo