第一人称实录

第一人称实录

1. 类加载初始化

public class Test {
    public static class A {
        int a;
        int b1 = a;
        int b2 = v++;
        static int c1 = v++;
        public A() {
            a = v++;
        }
    }
    static int v = 1;
    public static void main(String[] args) {
        A aa = new A();
        System.out.println("v = " + v);
        System.out.println("c1 = " + A.c1);
        System.out.println("a = " + aa.a);
        System.out.println("b1 = " + aa.b1);
        System.out.println("b2 = " + aa.b2);
    }
}

这里考察的东西很简单,说一下我的思路:首先类加载,于是初始化静态变量,然后执行静态方法,之后是对象的构造方法和成员变量的初始化。于是在我看来,其顺序是:v -> c1 -> a -> b1 -> b2 ; 那么输出的结果不言而喻,这里不展示。
但其实这里有点问题,从字节码文件.class中可以了解到:成员变量初始化的确是放在对象构造方法内的,但还是处于原构造方法之前,那么简单理解就是说现有成员变量的初始化,再有构造方法的调用。其次,static变量初始化先于static代码块,再先于main方法。到这里其实可以大致了解了,顺序是:v -> c1 -> a=0 -> b1 -> b2 -> a = 3。不过这里还有个小细节:静态内部类的static变量和外部类的static变量的初始化顺序如何呢?这里实验后可以发现,静态内部类的内容并不会在外部类加载时也进行加载,而是等到 new A()的时候再加载,缝合的结果就是如上,结果在意料之中,总的来说问得很细,尤其是静态内部类的考察,比较到位。

2. UI操作不能在子线程的原因

那么这个问题其实也不难,我们都知道UI操作是不能在非主线程的线程上进行的,且会抛出异常。根本原因还是为了杜绝这种情况下导致的UI显示不同步问题,但落实到代码上呢?这个异常在哪里抛的,如何抛的?熟悉的同学可能都知道答案了,那我对此是一知半解,首先明显是进行了对操作线程的判断,那么进一步就是在哪里进行的判断,我本能的认为是在一个处理消息的地方,因为安卓的任务和消息总是绕不开Handler,MessageQueue,Looper,于是联想到Looper总是将对应任务进行分发,猜测分发时对对应线程做了判断。那这个答案显然是错误的,接下来梳理一遍这个过程:

首先我们拿TextView的SetText为例:

// 缺少图例

首先SetText()确实是很复杂,这里做了很多操作,那关于UI的主要是这里

// 缺少图例

在这里你会发现不论如何都会触发invalidate()方法,部分情况会触发requestLayout()方法:

// 缺少图例

requestLayout()更新UI,这里会调用ViewRootImpl的

// 缺少图例

最终在ViewRootImpl中进入requestLayout(),那么这里可以看到首先checkThread():

最后,简单的判断

3. 多个Handler中removeMessage的情况

这个问题比较有意思,简单理解含义:首先是在一个线程里面创建多个Handler,之后再分别加入同种Message,最后再remove其中一个,会发生什么,代码大致如下:

private void test() {
    Handler h1 = new Handler();
    Handler h2 = new Handler();
    h1.sendMessageDelayed(Message.obtain(h1, 100),3000);    //h1.sendEmptyMessage(100);
    h2.sendMessageDelayed(Message.obtain(h2, 100),3000);    //h2.sendEmptyMessage(100);
    h1.removeMessages(100);
}

这里为了方便观察,将message增加了延迟,实际不影响效果。

第一层思考:既然是不同的handler,那不就应该互不影响吗,那这个handler机制就存在问题了,岂不是成了设计缺陷?所以直觉告诉我应该是不互相影响的,h2还是能收到并执行这个message。

第二层思考:首先我们使用handler机制需要先初始化Looper,即执行Looper.prepare(),而Looper的初始化过程实际确定了唯一的MessageQueue,而在Handler创建过程,会去取当前Looper的对应的MessageQueue来绑定自身。因此,根本逻辑是Looper对应一个MessageQueue,对应多个Handler。那在这种逻辑下,自然而然地:MessageQueue里面的消息被remove掉,两个handler都会受到影响,即都不会继续完成处理。

那真实情况如何呢?我们来实际看下:

首先Looper对应一个MessageQueue,对应多个Handler的分析没有问题,这可以从Looper和Handler的构造方法看到。

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    /* 创建looper,配套messageQueue。*/
    public Handler(@Nullable Callback callback, boolean async) {
        ......
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        ......
    }
    /* Handler直接使用looper的messageQueue。*/

明白了以上,那可以确定的确有两个what = 100的message。再看下removeMessage的情况:

    /**
     * Remove any pending posts of messages with code 'what' that are in the
     * message queue.
     */
    public final void removeMessages(int what) {
        mQueue.removeMessages(this, what, null);
    }

明确指出了remove是根据what来的。且注释中有关键词“any”,再看MessageQueue中:

    void removeMessages(Handler h, int what, Object object) {
            ......
            while (p != null && p.target == h && p.what == what
                   && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }
            ......
    }

细看就傻眼了,通过while看出确实是“any”,不过不但what要匹配,对这个handler也要匹配,因此其实是把将要分配给对应handler的相应message删除掉,那其实也就说明了,>细看就傻眼了,通过while看出确实是“any”,不过不但what要匹配,对这个handler也要匹配,因此其实是把将要分配给对应handler的相应message删除掉,那其实也就说明了,不会影响到h2处理任务

到这里其实我是有疑问的,像sendMessageDelayed()方法传的message确实是限定了对应handler,但如果按sendEmptyMessage(int what)呢?这参数可就只有what了,那这时候没有handler怎么算呢?附上关键代码:

    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
    /* 这里obtain了一个空消息,这是handler,即msg.target = null,之后调用到enqueueMessage:*/
    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        ......
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    /* 谜底昭然若揭,handler被赋给了msg。 */

所以给出结论:不会互相影响,这里对Handler消息机制又做了深入探寻,里面的东西确实很多。