handler机制

本文介绍了Android中Handler跨线程通讯的原理。线程内可有多个Handler对象,但只有一个MessageQueue和Looper对象,Looper的prepare()方法借助ThreadLocal为线程创建MessageQueue。调用Looper.loop()方法会轮询队列数据,最终调用Handler的dispatchMessage()处理消息,消息发送时插入C消息队列实现跨线程通讯。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.在线程的内部,可能 有多个handler对象,但是只能有一个MessageQueue对象,因此也只有一个Looper对象。

在Looper类的静态方法prepare()中为线程创建MessageQueue()对象,里面会使用ThreadLocal类,该类就是提供“线程局部存储”。也就是在同一个线程中会得到相同的数据,但是在不同线程中读取就会有不同的数据。

在ThreadLocal中存储的就是Looper对象,这样在不同的线程中进行读取的时候就找到不同的looper.

而在Looper的构造器中初始化了MessageQueue,因此在一个线程中只会有一个Looper对象和一个MessageQueue对象。

对于ThreadLocal的实现,其实就是将数据存放在map中,然后是根据线程名进行存储。因此当调用set()方法和get()方法的时候,存储和获取都是在当前线程。

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

在这里插入图片描述在这里插入图片描述

2.调用Looper.loop()方法

其实在ActivityThread类中的main()方法,就已经调用了prepare()方法和Looper.loop()方法。这就是在分线程中进行数据传递还需要调用Looper.prepare()方法的

下面是ActivityThread类中main()的代码:

 public static void main(String[] args) {

  Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

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

下面就是loop()方法

下面就是一个死循环,不停的轮询队列中的数据,如果没有数据就挂起线程,否则就调用dispatchMessage()方法进行消息的处理。

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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

        final long traceTag = me.mTraceTag;

        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {

            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));

        }

        final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();

        final long end;

        try {

            msg.target.dispatchMessage(msg);

            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();

        } finally {

            if (traceTag != 0) {

                Trace.traceEnd(traceTag);

            }

        }

        if (slowDispatchThresholdMs > 0) {

            final long time = end - start;

            if (time > slowDispatchThresholdMs) {

                Slog.w(TAG, "Dispatch took " + time + "ms on "

                        + Thread.currentThread().getName() + ", h=" +

                        msg.target + " cb=" + msg.callback + " msg=" + msg.what);

            }

        }


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

    }

}

3.上面最终调用的就是handler对象的dispatchMessage()方法

如果没有Runnable对象,那么就调用handleMessage(msg)进行回调,进行处理数据。这里的handleMessage()方法就是我们定义Handler的时候重写的方法。

  public void dispatchMessage(Message msg) {

    if (msg.callback != null) {

        handleCallback(msg);

    } else {

        if (mCallback != null) {

            if (mCallback.handleMessage(msg)) {

                return;

            }

        }
        handleMessage(msg);
    }

}

在handleCallback中其实就是调用线程的run()方法,这里的callback就是Runnable对象。

private static void handleCallback(Message message) {

    message.callback.run();

}

@FunctionalInterface

public interface Runnable {

  public abstract void run();

}

4.MessageQueue类的next()方法

其中nativePollOnce()方法是调用的C方法。就是在本地的消息队列中取出一条消息。

然后判断时间间隔,如果时间到了,那么就返回message

Message next() {

    // Return here if the message loop has already quit and been disposed.

    // This can happen if the application tries to restart a looper after quit

    // which is not supported.

    final long ptr = mPtr;

    if (ptr == 0) {

        return null;

    }


    int pendingIdleHandlerCount = -1; // -1 only during first iteration

    int nextPollTimeoutMillis = 0;

    for (;;) {

        if (nextPollTimeoutMillis != 0) {

            Binder.flushPendingCommands();

        }

        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {

            // Try to retrieve the next message.  Return if found.

            final long now = SystemClock.uptimeMillis();

            Message prevMsg = null;

            Message msg = mMessages;

            if (msg != null && msg.target == null) {

                // Stalled by a barrier.  Find the next asynchronous message in the queue.

                do {

                    prevMsg = msg;

                    msg = msg.next;

                } while (msg != null && !msg.isAsynchronous());

            }

            if (msg != null) {

                if (now < msg.when) {

                    // Next message is not ready.  Set a timeout to wake up when it is ready.

                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);

                } else {

                    // Got a message.

                    mBlocked = false;

                    if (prevMsg != null) {

                        prevMsg.next = msg.next;

                    } else {

                        mMessages = msg.next;

                    }

                    msg.next = null;

                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);

                    msg.markInUse();

                    return msg;

                }

            } else {

                // No more messages.

                nextPollTimeoutMillis = -1;

            }


            // Process the quit message now that all pending messages have been handled.

            if (mQuitting) {

                dispose();

                return null;

            }


            // If first time idle, then get the number of idlers to run.

            // Idle handles only run if the queue is empty or if the first message

            // in the queue (possibly a barrier) is due to be handled in the future.

            if (pendingIdleHandlerCount < 0

                    && (mMessages == null || now < mMessages.when)) {

                pendingIdleHandlerCount = mIdleHandlers.size();

            }

            if (pendingIdleHandlerCount <= 0) {

                // No idle handlers to run.  Loop and wait some more.

                mBlocked = true;

                continue;

            }


            if (mPendingIdleHandlers == null) {

                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];

            }

            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);

        }

        // Run the idle handlers.

        // We only ever reach this code block during the first iteration.

        for (int i = 0; i < pendingIdleHandlerCount; i++) {

            final IdleHandler idler = mPendingIdleHandlers[i];

            mPendingIdleHandlers[i] = null; // release the reference to the handler



            boolean keep = false;

            try {

                keep = idler.queueIdle();

            } catch (Throwable t) {

                Log.wtf(TAG, "IdleHandler threw exception", t);

            }

            if (!keep) {

                synchronized (this) {

                    mIdleHandlers.remove(idler);

                }

            }

        }

        // Reset the idle handler count to 0 so we do not run them again.

        pendingIdleHandlerCount = 0;


        // While calling an idle handler, a new message could have been delivered

        // so go back and look again for a pending message without waiting.

        nextPollTimeoutMillis = 0;

    }

}

5.当发送消息的时候,最终是将消息插入到队列中。其实是插入到c中的消息队列中。

注意这里,当插入消息的时候,msg.target直接指向的就是handler本身。这样当插入消息到队列中的时候,将消息存储的线程仍然是在主线程中。因此能够实现跨线程的数据传递。只不过里面有了一层队列的操作。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

    msg.target = this;

    if (mAsynchronous) {

        msg.setAsynchronous(true);

    }

    return queue.enqueueMessage(msg, uptimeMillis);

}


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;

}

android中使用handler进行主线程和分线程,分线程和分线程通讯,请看下面博客:

https://blog.youkuaiyun.com/yuezheyue123/article/details/82970662

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值