Handler探究

2018-11-23  本文已影响5人  Duzzi

Handler相关概念

什么Handler

Handler有两个主要用途:

  1. 在未来的某个时刻发送message和执行runnables;
  2. 2.将一个action放入消息队列,而不是你自己的线程。

post和sendMessage

原理

当为您的应用程序创建进程时,其主线程运行了一个消息队列,负责管理顶级应用程序对象(activity,broadcastReceiver等)和他们创建的window。您可以创建自己的线程,并通过Handler与主线程进行通信。这是通过像以前一样调用相同的post或sendMessage方法来完成的,但是来自你的新线程。然后,将在Handler的消息队列中调度给定的Runnable或Message ,并在适当时进行处理。

构造方法干了啥

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 " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    //mLooper消息队列赋值
    mQueue = mLooper.mQueue;
    //自定义回调实现
    mCallback = callback;
    //是否同步,默认同步
    mAsynchronous = async;
}

问题:

1.为什么Looper.myLooper()可能为空?

/**
 * 返回和当前线程关联的Looper。如果没有关联,返回null
 */
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

looper顶部的注释有写到:

所以在新线程需要这么用:

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }
}

2.为什么主线程就可以直接创建Handler,主线程什么时候创建了与之关联的Looper ?

我们可以看到looper有个prepareMainLooper方法

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        //如果sMainLooper已经创建,再手动创建就报错啦
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
} 

注释中说这个main looper是android环境创建的,我们看看是哪里调用的

    public static void main(String[] args) {
    //......................

    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);
    
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

可以看到是在ActivityThread的main方法中调用了Looper.prepareMainLooper()和Looper.loop(),
而这个main方法就是应用程序的入口,即应用程序创建时就给主线程创建了main looper,所以我们就可以直接在activity创建handler。

3.发送消息时,new message和obtainMessage有什么区别

看下源码

/**
 * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
 * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
 *  If you don't want that facility, just call Message.obtain() instead.
 */
public final Message obtainMessage()
{
    return Message.obtain(this);
}

从全局message pool中返回一个message。比创建分配新实例更加高效,因为避免了创建过多实例。返回的message会将handler设置给这个实例(Message.target == this)。如果您不想要该设置,就调用Message.obtain()。

public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;//这里把handler赋值给message的target

    return m;
}

实际调用还是在Message

/**
 * Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 */
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

可以判断出sPool是一个链表结构,sPool本身就是Message。

我们来看看message的成员

public final class Message implements Parcelable {
    // sometimes we store linked lists of these things
    /*package*/ Message next;

    private static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;

    private static final int MAX_POOL_SIZE = 50;
}

可以看出sPool就是一个全局的链表结构的消息池,next记录链表中的下一个元素,sPoolSize记录链表长度,MAX_POOL_SIZE表示链表的最大长度为50。

void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    // Clear out all other details.
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;

    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

message对象回收时,会将对象的属性置空,小于50会放入sPool中,否则交给gc处理。

4.为什么Android要设计只能通过Handler机制更新UI?

最根本的目的就是解决多线程并发问题。
假设如果在一个Activity当中,有多个线程去更新UI,并且都没有加锁机制,那么会产生什么样的问题?(更新界面错乱)如果对更新UI的操作都进行加锁处理,又会产生什么样的问题?(性能下降)
基于对以上目的的考虑,android给我们提供了一套更新UI的机制,我们只需遵循这样的机制,无需考虑多线程问题。

5.为什么handler会带来内存泄漏?如何避免?

原因

如何避免

6.什么是ThreadLocal

上一篇 下一篇

猜你喜欢

热点阅读