android 详解Handler内部源码机制

来源:互联网 发布:华为盒子无法连接网络 编辑:程序博客网 时间:2024/05/22 15:07

handler是我们android开发一定会用到的,如果说你没用到,那你还说是做android开发的,谁都不信,上帝也不会相信的,但是如果只停留在使用上,而不去分析内部实现,如果你去面试问你这个都不会,估计不太好吧,至少在面试官的影响中肯定是减分的.


我们知道Handler里面会涉及到几个类,都说轮询也就是Looper,还有什么消息队列,就是MessageQueue,那么他们之间是什么关系呢? 那就去Handler源码中神游下.



首先从Handler构造函数入手,


/** * Default constructor associates this handler with the {@link Looper} for the * current thread. * * If this thread does not have a looper, this handler won't be able to receive messages * so an exception is thrown. */public Handler() {    this(null, false);}

我们调用一个无参的构造函数,但是它是去调用二个参数的构造函数,这不是主要的,主要的是它上面的注释,意思是通过一个默认的构造函数创建一个Handler对象,而且是在当前线程,也就是说你在哪个线程中new Handler,还有就是如果这个线程中没有与之关联的Looper对象,那这个handler无法接受到消息,


那么在UI线程中创建了一个Handler,但是并没有创建Looper啊,有的,只是系统帮我们做了,在activity的启动的时候通过ActivityThread中的入口main()方法中,


public static void main(String[] args) {    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");    SamplingProfilerIntegration.start();    // CloseGuard defaults to true and can be quite spammy.  We    // disable it here, but selectively enable it later (via    // StrictMode) on debug builds, but using DropBox, not logs.    CloseGuard.setEnabled(false);    Environment.initForCurrentUser();    // Set the reporter for event logging in libcore    EventLogger.setReporter(new EventLoggingReporter());    // Make sure TrustedCertificateStore looks in the right place for CA certificates    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());    TrustedCertificateStore.setDefaultUserDirectory(configDir);    Process.setArgV0("<pre-initialized>");    Looper.prepareMainLooper();    ActivityThread thread = new ActivityThread();    thread.attach(false);    if (sMainThreadHandler == null) {        sMainThreadHandler = thread.getHandler();    }    if (false) {        Looper.myLooper().setMessageLogging(new                LogPrinter(Log.DEBUG, "ActivityThread"));    }    // End of event ActivityThreadMain.    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);    Looper.loop();    throw new RuntimeException("Main thread loop unexpectedly exited");}

Looper.prepareMainLooper();通过这个代码,就会去创建Looper对象了,点击进去,看到底干了什么事,


/** * 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) {        if (sMainLooper != null) {            throw new IllegalStateException("The main Looper has already been prepared.");        }        sMainLooper = myLooper();    }}

看这个注释就知道了,在当前线程中创建一个Looper,它是应用启动的时候就创建了,所以你永远不需要调用这个方法.

它的第一行代码就是prepare(false),查下这个方法:


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));}

你会发现这个方法是一个private修饰的,内部使用的,它是创建了一个Looper对象然后保存在ThreadLocal中去,ThreadLocal它的作用是可以在每个线程中存储数据的安全,比如你在A线程中发送了数据,B线程中发送了数据,怎么保证线程的数据不乱,就是通过ThreadLocal实现的,


现在回到Handler的构造函数中,如果是参数的构造函数,会调用带二个参数的构造函数,


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());        }    }    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;}

首先是Looper.myLooper()方法获取Looper对象,


/** * Return the Looper object associated with the current thread.  Returns * null if the calling thread is not associated with a Looper. */public static @Nullable Looper myLooper() {    return sThreadLocal.get();}

看注释是返回与当前线程关联的Looper对象,如果调用线程与一个Looper没有关联,则为null.

mQueue = mLooper.mQueue;再看下这个是返回一个MessageQueue对象,就是消息队列,mQueue是在哪赋值的,

final MessageQueue mQueue;这是在Looper类中定义的成员变量,赋值是在构造函数中的,


private Looper(boolean quitAllowed) {    mQueue = new MessageQueue(quitAllowed);    mThread = Thread.currentThread();}

从这可以看的出来,一个Looper对象对应一个MessageQueue,MessageQueue是一个链表的结构,那么我们在子线程中发送消息(Message),消息就入队列了,


/** * Pushes a message onto the end of the message queue after all pending messages * before the current time. It will be received in {@link #handleMessage}, * in the thread attached to this handler. *   * @return Returns true if the message was successfully placed in to the  *         message queue.  Returns false on failure, usually because the *         looper processing the message queue is exiting. */public final boolean sendMessage(Message msg){    return sendMessageDelayed(msg, 0);}

看这个注释,最终会调到这个方法:


public boolean sendMessageAtTime(Message msg, long uptimeMillis) {    MessageQueue queue = mQueue;    if (queue == null) {        RuntimeException e = new RuntimeException(                this + " sendMessageAtTime() called with no mQueue");        Log.w("Looper", e.getMessage(), e);        return false;    }    return enqueueMessage(queue, msg, uptimeMillis);}


enqueueMessage()方法就是把Message放入到队列中,


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

msg.targt = this,这是把Handler赋值给了Message.target,用来判断这个消息是从哪个Handler发送过来的,

queue.enqueueMessage(msg, uptimeMillis)这个就是真正把消息放入到哦队列中的,既然有消息入队列,我们都知道Looper是消息轮询的,轮询是Looper中的,


/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the 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.isTagEnabled(traceTag)) {            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();    }}

但是我们好像从来没有调用过这个方法啊,对吧,它是怎么去轮询这个MessageQueue队列中的消息呢?这是在ActivityThread中的main()方法倒数第二行代码被调用了,


if (false) {    Looper.myLooper().setMessageLogging(new            LogPrinter(Log.DEBUG, "ActivityThread"));}// End of event ActivityThreadMain.Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);Looper.loop();

这就是为啥我们自己不手动调用loop()方法,如果有消息会怎么办,loop()方法中有一个重要的代码


try {    msg.target.dispatchMessage(msg);} finally {    if (traceTag != 0) {        Trace.traceEnd(traceTag);    }}

之前说了targete就是handler,调用dispatchMessage分发Message,


/** * Handle system messages here. */public void dispatchMessage(Message msg) {    if (msg.callback != null) {        handleCallback(msg);    } else {        if (mCallback != null) {            if (mCallback.handleMessage(msg)) {                return;            }        }        handleMessage(msg);    }}

如果msg.callback==null,就调用handleMessage()处理这消息,那么这个消息处理是在子线程中还是主线程中呢,你这么想handler是异步消息处理机制,是更新UI的,如果是子线程中,能更新UI么,所以说这个消息处理一定是在主线程中,


现在花个图理解下:




其实这就是典型的多线程的生产者消费者模式,在子线程中生产(Message),在主线程中消费(Message).


现在几个常见的问题,也是面试常见的问题举例分析下:

1:我们都知道Handler的创建是在主线程,那么在子线程中能创建么,写个,


new Thread(){    @Override    public void run() {        super.run();        Handler handler = new Handler();    }}.start();

运行起来后发现报错了,




它说不能创建handler对象在子线程中,因为没有调用perpare()方法,之前我们也分析了这个方法,再把这个方法看下:


public static void prepare() {    prepare(true);}

调用带参数的prepare(true)


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对象的,所以说prepare()方法是创建Looper对象的,如果你创建Handler对象,只顾着发送消息,但是没有处理这个消息,那么队列中的消息是不是堆满了,对内存消息挺大,所以它就报错了,那解决的方法就是调用prepare()方法


new Thread(){    @Override    public void run() {        super.run();        Looper.prepare();        Handler handler = new Handler();    }}.start();

2:handler可以在主线程中发送消息么.那是可以的,只是如果在主线程中发送消息,处理消息也在主线程中,那干嘛还用Handler.


3:能在子线程中更新UI么? 可以的


new Thread(){    @Override    public void run() {        super.run();        Looper.prepare();        Handler handler = new Handler(){            @Override            public void handleMessage(Message msg) {                super.handleMessage(msg);                Toast.makeText(getApplicationContext(), "在子线程中更新UI", Toast.LENGTH_LONG).show();            }        };        handler.sendEmptyMessage(1);        Looper.loop();    }}.start();

有二个必须条件:

1:必须调用Looper.prepare()方法创建Looper对象

2:必须调用Looper.loop()去消息队列中轮询消息,否则你发送消息,没有Looper处理消息.


4:消息回收

在Looper.loop()轮询的时候,方法最后一行代码:


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();

最后一行代码可以看到msg调用了recycleUnchecked(),进入到这个方法


/** * Recycles a Message that may be in-use. * Used internally by the MessageQueue and Looper when disposing of queued Messages. */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++;        }    }}

经历一系列的赋null操作,消息池中最大的消息数是50,


private static final int MAX_POOL_SIZE = 50;

5:很多框架的回调接口怎么让他在主线程中执行;


Handler handler = new Handler(Looper.getMainLooper());handler.post(new Runnable() {    @Override    public void run() {        //回调接口    }});














原创粉丝点击