Handler的使用

2017-01-02  本文已影响67人  BlackNeko

新建handler并为其指定运行线程。

在主线程中创建Handler

Handler handler = new Handler();

Handler构造函数:

/** Handler.java */
public Handler() {
    this(null, false);
}

/** Handler.java */
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

    //取Looper
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

创建Handler需要Lopper对象,而Looper对象通过Looper.myLooper()获取 :

/** Looper.java */
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

ThreadLocal用于保存线程中的数据,在Looper类中是以静态变量的形式存在的,sThreadLocal对象是在Looper类加载阶段创建的:

/** Looper.java */
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

sThreadLocal不属于任何Looper的实例,在一个app中,每个进程Process有且仅有一个sThreadLocal。

ThreadLocal内部维护一个ThreadLocalMap,用于保存Looper对象。

ThreadLocal#get(),获取当前线程中保存的Looper对象:

/** ThreadLocal.java */
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}

getMap:

/** ThreadLocal.java */
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

threadLocals是Thread类中包访问级别的非静态变量:ThreadLocal.ThreadLocalMap threadLocals = null;,ThreadLocal和Thread是在同一个包目录下面的,所以ThreadLocal中可以取得Thread类中的threadLocals变量。

通过getMap(Thread)方法取得对应线程中的ThreadLocalMap后,再用map.getEntry(this)获取Entry节点,然后返回value。这里用ThreadLocal类实例的哈希值作为取资源的键值,说明一个ThreadLocal对象在Thread中只能对应一个value值,要想要存储多个对象就要创建多个对应的ThreadLocal对象。

ThreadLocal#get() 函数有用到Thread t = Thread.currentThread(),因此,只能在对应的线程中才能获取到存入的值。

以上就是Looper.myLooper()获取当前线程Looper对象的过程。

创建Handler需要Looper,没有的话会报Can't create handler inside thread that has not called Looper.prepare()异常,在主线程中创建Handler时是没有问题的,因为app启动时已经为当前线程创建了Looper对象。

在新线程中创建Handler

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        Log.i(TAG, "mHandler thread start");
        Looper.prepare();
        mHandler = new Handler();
        Looper.loop();
        Log.i(TAG, "mHandler thread end");
    }
}, "mHandlerThread");
thread.start();

首先调用 Looper.prepare() 创建Looper对象:

/* Looper.java */
public static void prepare() {
    prepare(true);
}

/* Looper.java */
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对象并添加到静态成员变量sThreadLocal中,如果当前线程已创建过Looper就抛异常。

ThreadLocal#set(T value) 把当前线程的Looper对象存储到当前线程的ThreadLocalMap中

/* ThreadLocal.java */
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#getMap(Thread t) :

/* ThreadLocal.java */
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

第一次取的时候map为null,创建并添加,ThreadLocal#createMap(Thread t, T firstValue) :

/* ThreadLocal.java */
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

这样Handler就创建完了,下面就是使用了。

Handler,Looper,MessageQueue,Message

简单地说就是MessageQueue存储Message,Looper跑循环,不断从MessageQueue里面取任务然后执行,如果MessageQueue为空,就卡在那里等,然后Handler发信息,Message携带信息。

创建完Handler后,要在线程里面调用Looper.loop();

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        final long traceTag = me.mTraceTag;
        if (traceTag != 0) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}

停止Handler

Looper只有在MessageQueue#next()为null时才会停下来

调用链:

Looper.myLooper().quitSafely() -> MessageQueue#quit(true) 清空MessageQueue -> Looper.loop()终止

Looper.myLooper().quitSafely()要在对应的线程中调用,就是说要用Handler实例发事件才行。

内存泄露:创建Handler的线程一直在执行,所以创建的Handler对象是不会被系统回收的,Handler中保存的对象引用也是不会被回收的,非静态内部类保存有外部类的引用。

解决办法:

Handler发送事件

mHandler.post(new Runnable() {
    @Override
    public void run() {
        Log.i(TAG, "current thread name=" + Thread.currentThread().getName();
    }
});

//or
mHandler.sendMessage(new Message());

post 也是调用的sendMessage:

public final boolean post(Runnable r)
{
    return  sendMessageDelayed(getPostMessage(r), 0);
}

最终调用 Handler#enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)把Message放入MessageQueue中

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

猜你喜欢

热点阅读