Handler 相关面试题

1.一个线程可以有多个Handler吗?

答:可以。例如,Android 的 每一个Activity都可以创建Handler对象,但它们都是运行在同一个主线程中的。

2. 一个线程有几个Looper?如何保证的?

答:一个线程只有一个Looper对象。

 private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

从Looper的源码得知,在looper的prepare()函数中,其通过threadlocal来获取looper的对象。

 /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

上面为threadlocal的get()函数。可以看出threadlocal的内部维护了一个ThreadLocalMap类。该类是以当前thread做为key的。因此可以得知,一个线程最多只能有一个looper对象。

 /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

以上为threadlocal的set方法。

3.Handler内存泄露的原因?为什么其他的内部类没有说有这个问题?

1.所有的非静态内部类都会持有外部类的引用。当外部类需要销毁而内部类仍在运行时,java的GC机制将导致外部类的引用不会被销毁,从而导致内存泄露。所以所有的非静态内部类都可能会导致内存泄露。
2.MessageQueue存储了Message,而Message的target属性为handler对象,handler又持有的Activity等context的对象,这就导致了内存泄露。

 public static void loop() {
//省略代码
  try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
//省略代码
}

以上为looper的loop方法。

   private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

以上为handler的enqueueMessage函数(所有的send和post方法最终都会调用该函数)

4.怎么处理handler的内存泄露;

1.静态内部类+弱引用;
2.针对与在子线程开启的消息队列,可以在需要结束的时候,调用lopper的quitSafely()方法。
上代码:

  public void quit() {
        mQueue.quit(false);
    }

    /**
     * Quits the looper safely.
     * 

* Causes the {@link #loop} method to terminate as soon as all remaining messages * in the message queue that are already due to be delivered have been handled. * However pending delayed messages with due times in the future will not be * delivered before the loop terminates. *

* Any attempt to post messages to the queue after the looper is asked to quit will fail. * For example, the {@link Handler#sendMessage(Message)} method will return false. *

*/ public void quitSafely() { mQueue.quit(true); }

以上为looper的quit函数。

    void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }

            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

以上为MessageQueue的quit函数。在此函数里,消息队列会移除队列里的所有消息。此时MessageQueue将不会持有message,更不会间接的持有handler的引用。从而消除handler的内存泄露问题。
补充:
在子线程内开启消息队列,
1.先通过调用Looper.prepare()函数来创建looper对象;
2.创建handler对象;
3.调用looper.loop()函数来循环不断的从messagequeue里获取message;
4.结束消息队列时,调用looper.quit()函数来清空messagequeue里的消息,来防止内存泄露。

5.既然可以存在多个Handler往MessageQueue中添加数据(发送消息时,各个Handler可能处于不同的线程),它内部是如何保持线程安全的?

 boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

通过synchronized锁机制保证线程安全

6.使用Handler的postDelay后消息队列有什么变化?
消息队列会重新排序。

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

handler的postDelay()函数最终会调用messagequeue的enqueueMessage()函数。
该函数里会按照执行开始时间,对messagequeue里的message进行重新排序。

7.Looper死循环为什么不会导致应用卡死?

1.导致应用卡死的原因只有一下几种情况:
a)输入事件在5秒内未响应;
b)在主线程执行耗时操作;
2.Looper.loop()函数会在死循环里不断的去获取messagequeue里的message,当消息队列里没有消息的时候,looper会进入睡眠阶段。Looper为了防止message执行过于耗时的操作,导致队列阻塞,就给message设置一个ANR的状态。可见,looper的死循环和应用卡死是两个不同的概念。

你可能感兴趣的:(Handler 相关面试题)