Androidハンドラーメカニズムフローの詳細な説明

Androidハンドラーメカニズムフローの詳細な説明

ハンドラーメカニズムは、次の部分で構成されています。

.Handler
.Message
.MessageQueue
.Looper

全体的なプロセスの紹介:

1.プロセスが開始
されると、メインスレッドのメインメソッドで独自のスレッドに対応するルーパーが作成され、ルーパーの作成時にMessageQueueメッセージキューが作成されて保持されます。同時に、メインメソッドでLooper.loop()が呼び出され、無限ループを実行してLooperが保持するメッセージキューをトラバースします。

次に、ハンドラーを作成
します。メインスレッドで作成された場合、ハンドラーはメインスレッドに対応するルーパーを取得して保持できます。子スレッドでハンドラーを作成する場合は、ルーパー作成メソッドを呼び出して、自分でLooper.loopを呼び出す必要があります。そうしないと、エラーが報告されます。

3.
メッセージの作成と送信作成されたメッセージは、独自のハンドラー情報をメッセージにカプセル化し、最終的にMessageQueueメッセージキューに送信されます。

4番目に、
Looper.loopでメッセージを受信し、MessageQueueからメッセージを取得して、対応するハンドラー情報に従って処理を分散します。

対応:
スレッドThreadは一意のLooperに対応し、対応する情報はThreadLocalに格納されます。
Looperオブジェクトは独自のMessageQueueを保持します。
同じスレッドでは、作成されるハンドラーの数に関係なく、1つのLooperと1つのMessageQueueしかありません。
したがって、ハンドラーが作成されると、ルーパーに従って対応するスレッドにバインドされ、別のスレッドがハンドラーに従ってメッセージを送信しても、ハンドラーに対応するルーパーのメッセージキューに送信され、分散されます。

コード分​​析:

次に、簡単な例を示し、メインプロセスに対応する元のコードを簡単に分析します。

private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
               
            }
        }
    };
    new Thread() {
            public void run() {
                    Message message = handler.obtainMessage();
                    message.what = 100;
                    message.obj = "测试+ : " + i;
                    handler.sendMessage(message);
            }
        }.start();

分析:
ハンドラー:メッセージの送信と返されたメッセージの受信、メインプロセス部分のソースコード分析を担当

public class Handler {
    //主要变量
    final Looper mLooper;//与每个线程对应的looper
    final MessageQueue mQueue;//消息队列
    final Callback mCallback;//是否同步

    //构造方法
    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            ..
        }
        mLooper = Looper.myLooper();//根据当前线程信息在ThreadLocal中获取线程对应的looper
        注:
        .如果是在子线程中创建,需要调用Looper.prepare( boolean quitAllowed)
        .该方法的作用是获取调用方法的线程信息,判断当前线程是否有looper,没有则创建。
        .在ActivityThread中创建过主线程的looper

        if (mLooper == null) {
            ..
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }


//从消息对象池中取出消息
    public final Message obtainMessage()
    {
        return Message.obtain(this);
    }


//发送消息的各种方法
handler.sendMessage(message);
handler.sendMessageDelayed(
    Message msg, long delayMillis);
handler.sendMessageAtTime(
    Message msg, long uptimeMillis);
handler.sendEmptyMessage(
    int what);
handler.sendEmptyMessageDelayed(
    int what, long delayMillis);
handler.sendMessageAtFrontOfQueue(
    Message msg);
handler.sendEmptyMessageAtTime(
    int what, long uptimeMillis);

    //最终都是调用来发送
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;//将当前handler信息封装进Message
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }


//分发消息

    /**
     * 该方法在Looper中next()方法中被调用  根据msg.target来区分
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

}

主なイベント:

1.呼び出し時:new Handler():
1. In Handler:Looper.myLooper();
現在のスレッドに対応するルーパーをThreadLocalで取得します。メインスレッドに対応するルーパーはmainメソッドで作成されています

2. mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
ルーパーのメッセージキュー(mQueue)を取り出します。コールバックオブジェクト。同期するかどうか。ハンドラーオブジェクトにカプセル化

2番目に、呼び出し時:handler.obtainMessage():
メッセージオブジェクトプール内のメッセージを取得します。

3.呼び出し時:sendMessageまたはさまざまな送信メソッド:
最後にenqueueMessageを呼び出し、現在のハンドラーオブジェクトをmessage.targetにカプセル化し、
mQueue.enqueueMessage(msg、uptimeMillis)を呼び出してメッセージをメッセージキューに送信します。

メッセージ:メッセージのカプセル化、メインプロセス部分のソースコード分析を担当

public final class Message implements Parcelable {
    public int what;
    public int arg1;
    public int arg2;
    public Object obj;
    long when;
    Bundle data;
    Handler target;
    Runnable callback;
    Message next;
    public static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 50;
    private static boolean gCheckRecycle = true;


    //被handler调用
    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;
        return m;
    }

    /**
     * 判断消息对象池中是否有消息,并从消息对象池中获取消息
     */
    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();
    }

    省略其他好多方法...

結論:ハンドラーで取得(this)を使用してメッセージプール内のメッセージオブジェクトを取得する場合、現在のthisをmessage.targetにカプセル化します。

ルーパー:ループを維持し、メッセージキューからメッセージを取得し、メッセージのターゲットに従ってメッセージを配信します。つまり、対応するハンドラーのdispatchMessageメソッドを呼び出します。メインプロセスのソースコード分析

    public final class Looper {
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // 主线程looper对象
    final MessageQueue mQueue; //消息队列
    final Thread mThread;//当前线程

    //构造方法:只有一个构造,并且创建mQueue 和给mThread 赋值
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

    //初始化,如果handler在子线程中被调用,则要先调用此方法
    public static void prepare() {
        prepare(true);
    }

    //被上面方法调用
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            //该方法内部根据当前线程信息判断是否创建了looper对象,如果有创建,则返回对应looper对象。
            //如果没有创建,则返回空
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //创建一个looper对象,并将当前线程和该looper绑定
        sThreadLocal.set(new Looper(quitAllowed));
    }

    //主线程在创建的时候在ActivityThread类中main方法中被调用
     创建主线程对应的looper和给当前sMainLooper赋值
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    //handler中获取looper的方法
    public static @Nullable
    Looper myLooper() {
        //该方法内部根据当前线程信息判断是否创建了looper对象,如果有创建,则返回对应looper对象。
        //如果没有创建,则返回空
        return sThreadLocal.get();
    }

    //开启loop循环查询队列,
    主线程对应的loop方法在main中被调用,如果在子线程中创建的handler,需要自己调用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;

        省略众多代码....

        for (; ; ) {
        
        //队列的next()方法中处理基本逻辑,如果有消息:返回。如果没有消息:阻塞||等待
            Message msg = queue.next(); 
            if (msg == null) {
                return;
            }
            省略众多代码.....
            try {
                msg.target.dispatchMessage(msg);//根据targer(即handler)进行分发
                ..
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            //将消息放回消息池
            msg.recycleUnchecked();
        }
    }
}

このクラスは、次のことを担当します。

1. prepare()が呼び出されたとき:
このメソッドは次のように定義できます。呼び出し側スレッドに対応するルーパーを作成し、呼び出されたスレッドにルーパーオブジェクトがあるかどうかを判断し、エラーがある場合は、スレッドとルーパーを作成してバインドします。

2.構築が呼び出されたとき:
MessageQueueを作成し、mThread変数(現在のスレッド)に値を割り当てます

3. myLooper()が呼び出された場合:
このメソッドは、スレッドに対応するルーパーを取得し、呼び出されたスレッドにルーパーオブジェクトがあるかどうかを判断し、ルーパーオブジェクトがある場合はnullを返さずに返すと定義できます。

4. prepareMainLooper()メソッドがmainメソッドで呼び出され、メインスレッドに対応するルーパーを作成します。

5.メインでloop()メソッドが呼び出されます。これは、無限ループとして機能し、ルーパーに対応する現在のスレッドが保持するメッセージキューからメッセージを取得し、メッセージに対応するターガーに従ってメッセージを分散します。

注:ソースコードがここで解析されると、質問があります。メインスレッドのLooperのループがANRを引き起こさない理由
説明:Looper.loop無限ループがANRを引き起こさない理由

MessageQueue:メッセージキュー、ハンドラーから送信されたメッセージを受信して​​キューに格納し、無限ループでメッセージキューをトラバースして、ルーパーに戻ります。メッセージキューにメッセージがない場合は、||待機します。メインプロセスのソースコード分析

public final class MessageQueue {

    private final boolean mQuitAllowed;
    是否允许退出
    private boolean mQuitting;//是否放弃消息队列

    @SuppressWarnings("unused")
    private long mPtr;

    private native static long nativeInit();//返回一个NativeMessageQueue,具体实现过程在Nativity层

    private native static void nativeWake(long ptr);//唤醒对应线程


    //构造
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;//是否允许退出
        mPtr = nativeInit();//返回一个NativeMessageQueue,具体实现过程在Nativity层
    }


    //该方法就是在handler中调用send系列方法最终调用的方法,将消息放入消息队列
    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {//msg.target就是发送此消息的Handler
            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;//将延迟时间封装到msg内部
            Message p = mMessages;//消息队列的第一个元素
            boolean needWake;//是否需要唤醒线程
            if (p == null || when == 0 || when < p.when) {
//如果此队列中头部元素是null(空的队列,一般是第一次),
或者此消息不是延时的消息,则此消息需要被立即处理,
此时会将这个消息作为新的头部元素,并将此消息的next指向旧的头部元素,
然后判断如果Looper获取消息的线程如果是阻塞状态则唤醒它,让它立刻去拿消息处理。

                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
//如果此消息是延时的消息,则将其添加到队列中,
原理就是链表的添加新元素,按照when,也就是延迟的时间来插入的,
延迟的时间越长,越靠后,这样就得到一条有序的延时消息链表,
取出消息的时候,延迟时间越小的,就被先获取了。插入延时消息不需要唤醒Looper线程

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

            if (needWake) {
                nativeWake(mPtr);//唤醒线程
            }
        }
        return true;
    }


    //该方法就是在Looper中被调用的方法,返回一个message,如果队列为空,则阻塞
    Message next() {
        final long ptr = mPtr;
//从注释可以看出,只有looper被放弃的时候(调用了quit方法)才返回null,
mPtr是MessageQueue的一个long型成员变量,关联的是一个在C++层的MessageQueue,
阻塞操作就是通过底层的这个MessageQueue来操作的;当队列被放弃的时候其变为0
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1;
        int nextPollTimeoutMillis = 0;
        for (; ; ) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

//阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的。
//如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
//如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
//如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
            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) {
//msg.target == null表示此消息为消息屏障(通过postSyncBarrier方法发送来的)
//如果发现了一个消息屏障,会循环找出第一个异步消息(如果有异步消息的话),所有同步消息都将忽略(平常发送的一般都是同步消息)  
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
// 如果消息此刻还没有到时间,设置一下阻塞时间nextPollTimeoutMillis,进入下次循环的时候会调用
//nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞;

                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //正常取出消息
                        //设置mBlocked = false代表目前没有阻塞
                        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 {
                    //没有消息,会一直阻塞,直到被唤醒
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
            }
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }

}

主な処理:
MessageQueue()の構築:Looperの作成時に作成されます

enqueueMessage():一連の送信側ハンドラーメソッドが最終的に同じ方法でこのメソッドに戻り、メッセージをメッセージキューに入れます

next():Looper.loop()メソッドで呼び出されてキューを走査してメッセージを返し、キューにメッセージがない場合はブロックします

公開された60元の記事 ウォン称賛25 ビュー10000 +

おすすめ

転載: blog.csdn.net/qq_41466437/article/details/104628280