View的两种更新方法-从源码角度分析invalidate()和postInvalidate()的区别

介绍

最近在看各种Android多线程开发的各种知识,网上看到有关线程和View的东西,其中View的两种更新方法invalidate()和postInvalidate()。记得当时在深圳去面试其中有个题目就是说明这两个的区别。今天突然又看到就需要把不懂得总结出来。

分析invalidate

首先不管这两个的使用问题,就从源码看。
先来到android.view.View这个最重要的类。Ctr+F搜索invalidate这个关键词会有大量的检索到的信息,一直向下,会发现标记到的有注释说明和方法内部调用。如果你留心的话就会发现很多关键信息。
比如:

  • setEnabled改变View可选状态的方法,它内部操作其实就是改变内部Flags标志位状态,刷新内部Drawable显示状态DrawableState,然后就调用invalidate(true),所以invalidate肯定是会导致视图重绘的方法。代码如下。
 @RemotableViewMethod
    public void setEnabled(boolean enabled) {
        if (enabled == isEnabled()) return;

        setFlags(enabled ? ENABLED : DISABLED, ENABLED_MASK);

        /*
         * The View most likely has to change its appearance, so refresh
         * the drawable state.
         */
        refreshDrawableState();

        // Invalidate too, since the default behavior for views is to be
        // be drawn at 50% alpha rather than to change the drawable.
        invalidate(true);

        if (!enabled) {
            cancelPendingInputEvents();
        }
    }
  • 同样的还有onFocusChanged()setAlphasetScrollIndicators()等也在内部有类似的调用过程,先不管它们真正的操作,光看名字就知道都是改变视图状态的方法。

    最后来到这里invalidate(ture)真正的方法。

/**
     * This is where the invalidate() work actually happens. A full invalidate()
     * causes the drawing cache to be invalidated, but this function can be
     * called with invalidateCache set to false to skip that invalidation step
     * for cases that do not need it (for example, a component that remains at
     * the same dimensions with the same content).
     *
     * @param invalidateCache Whether the drawing cache for this view should be
     *            invalidated as well. This is usually true for a full
     *            invalidate, but may be set to false if the View's contents or
     *            dimensions have not changed.
     */
    void invalidate(boolean invalidateCache) {
        invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
    }

先看注释,大概意思就是会使得绘画的缓存(drawing cache)失效,现在没有前后文理解也搞不清楚具体的意思。再看它的参数mRight - mLeft, mBottom - mTop这就是显示范围的宽高。也是和视图显示相关的。
(坐标原点在屏幕左上角,横为左右值,竖为顶底值)

右-左=当前View的宽显示范围。
底-高=当前View的高显示范围。

总的来说:invalidate会直接在方法内改变View的显示效果与线程没有明显的关系。
引用Android官网API的参考文档

Invalidate the whole view. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future.This must be called from a UI thread. To call from a non-UI thread, call postInvalidate().
大概翻译:如果当前视图是可见的,onDraw(真正的绘制方法)会未来的某个时刻调用,这个方法必须在UI主线程调用,如果不是主线程需要调用postInvalidate方法。

分析postInvalidate

postInvalidate()方法相对就在View源码中就比较少,
我检索到的两个方法

    //不带参数 
    public void postInvalidate() {
        postInvalidateDelayed(0);
    }

    //带视图范围参数 
    public void postInvalidate(int left, int top, int right, int bottom) {
        postInvalidateDelayed(0, left, top, right, bottom);
    }

上面两个方法区别只在于是否带视图信息的参数。最后都的逻辑都是一样的。通过绑定的ViewRootImpl对象发送出消息,参数this就是把自己View对象给发送了出去,这很关键。

 attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);

ViewRootImpl就是当我们视图在子线程更新UI向系统发出异常的类,它的工具就是视图顶级管理类负责管理所有的View对象。

异常长这样:android.view.ViewRootImpl$calledfromwrongthreadexception only
the original thread

点进去看ViewRootImpl的源码,已经没什么好说的了,就是一个Handler的发送消息方法。把传过来的View对象和int类型的标志位MSG_INVALIDATE打包成Message消息对象,发送延时消息。

public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
        Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
        mHandler.sendMessageDelayed(msg, delayMilliseconds);
    }

MSG_INVALIDATE这个what标志去找,跳转到ViewRootHandler类

        //省略很多代码
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_INVALIDATE:
                ((View) msg.obj).invalidate();
                break;
         //省略很多代码

真相大白!
ViewRootHandler的handleMessage处理消息方法就是把Message消息打包的Object强转回View对象再调用invalidate方法。
转了一大圈postInvalidate最后还是调用了invalidate方法,只是不是在随意线程调用,而是通过ViewRootImpl这个顶级视图检查管理类(The top of a view hierarchy)去负责分发轮询处理,然后在主线程调用View方法,实现View控件的线程安全。

引用Android官网API的参考文档

Cause an invalidate to happen on a subsequent cycle through the event loop. Use this to invalidate the View from a non-UI thread.
This method can be invoked from outside of the UI thread only when this View is attached to a window.
大概翻译:通过周期性的轮询使得能够在非UI线程调用得到invalidate方法。

总结

  • 今天这篇博文心血来潮写的,写得有点乱。
  • 从源码结合文档大概的说明了invalidate()和postInvalidate()在线程问题上的原理。
  • 明天再更新,凌晨1点了我要去看算法书了。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android 中,ImageView 是一个常用的控件,通常用于显示图片。其中的 setVisible 方法是用来设置 ImageView 是否可见的方法。下面从源码角度分析一下这个方法的实现。 ImageView 的 setVisible 方法实际上是从 View 类继承而来的,其源码如下: ``` @Override public void setVisible(boolean visible, boolean restart) { super.setVisible(visible, restart); mPrivateFlags &= ~PFLAG_DRAWABLE_STATE_DIRTY; } ``` 在这个方法中,首先调用了 View 类的 setVisible 方法,而该方法中的实现主要是设置 View 的 visibility 属性。而 ImageView 这个子类中,visibility 属性的设置方法是 setVisibility 方法。因此,如果想要设置 ImageView 的可见性,实际上应该调用的是 setVisibility 方法。 setVisible 方法的第二个参数 restart 表示是否需要重绘 View。在 ImageView 中,如果需要重绘,则会调用 ImageViewinvalidate 方法,使其重绘。而在 invalidate 方法中,会标记 View 的状态为需要重绘,这个标记的状态就是 mPrivateFlags 变量中的 PFLAG_DRAWABLE_STATE_DIRTY 标记。因此,在 setVisible 方法中,还需要将这个标记清除,以便在下一次需要重绘时,重新标记。 综上所述,ImageView 的 setVisible 方法主要是继承自 View 类,并调用了其父类的 setVisible 方法,同时还需要清除重绘标记。如果想要设置 ImageView 的可见性,应该调用 setVisibility 方法

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值