最近在看 LayoutInflater 源码的时候发现了一个有意思的注释:

@SystemService(Context.LAYOUT_INFLATER_SERVICE)
public abstract class LayoutInflater {
    ...
    private static final String TAG_1995 = "blink";
    /**
     * Tries to create a view from a tag name using the supplied attribute set.
     *
     * @hide for use by precompiled layouts.
     *
     * @param parent the parent view, used to inflate layout params
     * @param name the name of the XML tag used to define the view
     * @param context the inflation context for the view, typically the {@code parent} or base layout inflater context
     * @param attrs the attribute set for the XML tag used to define the view
     */
    @UnsupportedAppUsage(trackingBug = 122360734)
    @Nullable
    public final View tryCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) {
        if (name.equals(TAG_1995)) {
            // Let's party like it's 1995!
            return new BlinkLayout(context, attrs);
        }
        ...
    }
}

可以看到第二个参数是指定义 View 的布局 TAG 名称,当它为 TAG_1995 时,直接返回一个 BlinkLayout,而重点在这句的注释上:“Let's party like it's 1995!”

这句话中文翻译过来就是:“让我们像 1995 年那样狂欢吧!”

TAG_1995 的值可以看到是 blink,中文是闪烁的意思,我们写个 Demo 来看看效果:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <blink
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="Let's Party"
            android:textSize="64sp" />
    </blink>
</RelativeLayout>

效果如下:

BlinkLayout

接下来看看它是如何实现的。

BlinkLayoutLayoutInflater 的内部类,继承自 FrameLayout,实现并不复杂,完整代码如下:

@SystemService(Context.LAYOUT_INFLATER_SERVICE)
public abstract class LayoutInflater {
    ...
    private static class BlinkLayout extends FrameLayout {
        private static final int MESSAGE_BLINK = 0x42;
        private static final int BLINK_DELAY = 500;
        private boolean mBlink;
        private boolean mBlinkState;
        private final Handler mHandler;

        public BlinkLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
            mHandler = new Handler(new Handler.Callback() {
                @Override
                public boolean handleMessage(Message msg) {
                    if (msg.what == MESSAGE_BLINK) {
                        if (mBlink) {
                            mBlinkState = !mBlinkState;
                            makeBlink();
                        }
                        invalidate();
                        return true;
                    }
                    return false;
                }
            });
        }

        private void makeBlink() {
            Message message = mHandler.obtainMessage(MESSAGE_BLINK);
            mHandler.sendMessageDelayed(message, BLINK_DELAY);
        }

        @Override
        protected void onAttachedToWindow() {
            super.onAttachedToWindow();
            mBlink = true;
            mBlinkState = true;
            makeBlink();
        }

        @Override
        protected void onDetachedFromWindow() {
            super.onDetachedFromWindow();
            mBlink = false;
            mBlinkState = true;
            mHandler.removeMessages(MESSAGE_BLINK);
        }

        @Override
        protected void dispatchDraw(Canvas canvas) {
            if (mBlinkState) {
                super.dispatchDraw(canvas);
            }
        }
    }
}

BlinkLayout 回调 onAttachedToWindow() 时,调用 makeBlink() 方法,makeBlink() 方法内部用 Handler 发送一个延迟 500ms 的消息。从构造函数中又可以看到,在接受到 Handler 消息后,又接着调用 makeBlink(),循环发送消息,然后刷新页面。

BlinkLayout 回调 onDetachedFromWindow() 时,移除这个这个消息队列中的所有消息,结束循环。

接下来再看这个 Message 的处理,在 handleMessage() 中,会对 mBlinkState 这个布尔变量取反,然后在 dispatchDraw() 回调时,则判断这个状态变量来决定是否需要绘制界面,由于是每隔 500ms 发送一次消息,所以 mBlinkState 的值也会每隔 500ms 反转一次,View 相应的也会 500ms 绘制一次,500ms 消失一次,就形成了闪烁。

实际上你在写布局文件时会发现,<blink> 标签在『Android Studio』中会报「Cannot resolve class blink」,所以我们一般开发中也不会使用,尽管它能够通过编译,因此也算得上是 Android 官方埋下的一枚彩蛋吧。

关于 1995 年发生了什么,我想了一下与 Android 相关的事件,只想到 Sun 公司在 1995 年发布了 Java,不过随着 Sun 被 Oracle 收购,Google 和 Oracle 的官司也成为了 Android 程序员津津乐道的消息。

后来查了一下,发现 1995 年还是 Google 两位联合创始人 Lawrence Edward PageSergey Mikhaylovich Brin 在 Stanford 相遇的时间。

Lawrence Edward Page & Sergey Mikhaylovich Brin

2002 年 Andy Rubin 在 Stanford 给硅谷工程师讲课,恰好 Google 两位大佬也是听众。

2003 年 Andy Rubin 创立 Android,随后 2005 年 Google 便收购了这个仅成立了 22 个月的公司,并在 2007 年正式向外部公布 Android 操作系统,随后 Android 生态壮大发展,越居移动操作系统市场占有率榜首。