资讯专栏INFORMATION COLUMN

Android异步消息机制

blair / 1980人阅读

摘要:在子线程中发送消息,主线程接受到消息并且处理逻辑。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。

目录介绍

1.Handler的常见的使用方式

2.如何在子线程中定义Handler

3.主线程如何自动调用Looper.prepare()

4.Looper.prepare()方法源码分析

5.Looper中用什么存储消息

6.Handler发送消息如何运作

7.Looper.loop()方法源码分析

8.runOnUiThread如何实现子线程更新UI

9.Handler的post方法和view的post方法

10.主线程中Looper的轮询死循环为何没阻塞主线程

11.得出部分结论

好消息

01.基础组件(9篇)

02.IPC机制(0篇)

03.View原理(7篇)

04.动画机制(2篇)

05.View事件(9篇)

06.消息机制(6篇)

07.多媒体(9篇)

08.View事件(4篇)

09.多线程(4篇)

10.Window(11篇)

11.WebView(4篇)

12.网络相关(7篇)

13.注解(14篇)

14.音视频(13篇)

15.优化相关(8篇)

16.设计模式(4篇)

20.零碎笔记(12篇)

21.kotlin学习(1篇)

22.源码分析(11篇)

23.架构技术(13篇)

25.RecyclerView(21篇)

博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计N篇[近100万字,陆续搬到网上],转载请注明出处,谢谢!

链接地址:https://github.com/yangchong2...

如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!

1.Handler的常见的使用方式

handler机制大家都比较熟悉呢。在子线程中发送消息,主线程接受到消息并且处理逻辑。如下所示

一般handler的使用方式都是在主线程中定义Handler,然后在子线程中调用mHandler.sendXx()方法,这里有一个疑问可以在子线程中定义Handler吗?

public class MainActivity extends AppCompatActivity {

    private TextView tv ;

    /**
     * 在主线程中定义Handler,并实现对应的handleMessage方法
     */
    public static Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 101) {
                Log.i("MainActivity", "接收到handler消息...");
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = (TextView) findViewById(R.id.tv);
        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread() {
                    @Override
                    public void run() {
                        // 在子线程中发送异步消息
                        mHandler.sendEmptyMessage(1);
                    }
                }.start();
            }
        });
    }
}

2.如何在子线程中定义Handler

直接在子线程中创建handler,看看会出现什么情况?

运行后可以得出在子线程中定义Handler对象出错,难道Handler对象的定义或者是初始化只能在主线程中?其实不是这样的,错误信息中提示的已经很明显了,在初始化Handler对象之前需要调用Looper.prepare()方法

tv.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        new Thread() {
            @Override
            public void run() {
                Handler mHandler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        if (msg.what == 1) {
                            Log.i(TAG, "在子线程中定义Handler,接收并处理消息");
                        }
                    }
                };
            }
        }.start();
    }
});

如何正确运行。在这里问一个问题,在子线程中可以吐司吗?答案是可以的,只不过又条件,详细可以看这篇文章02.Toast源码深度分析

这样程序已经不会报错,那么这说明初始化Handler对象的时候我们是需要调用Looper.prepare()的,那么主线程中为什么可以直接初始化Handler呢?难道是主线程创建handler对象的时候,会自动调用Looper.prepare()方法的吗?

tv.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                Handler mHandler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        if (msg.what == 1) {
                            Log.i(TAG, "在子线程中定义Handler,接收并处理消息");
                        }
                    }
                };
                Looper.loop();
            }
        }.start();
    }
});
3.主线程如何自动调用Looper.prepare()

首先直接可以看在App初始化的时候会执行ActivityThread的main方法中的代码,如下所示

可以看到Looper.prepare()方法在这里调用,所以在主线程中可以直接初始化Handler了。

public static void main(String[] args) {
    //省略部分代码
    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"));
    }
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

并且可以看到还调用了:Looper.loop()方法,可以知道一个Handler的标准写法其实是这样的

Looper.prepare();
Handler mHandler = new Handler() {
   @Override
   public void handleMessage(Message msg) {
      if (msg.what == 101) {
         Log.i(TAG, "在子线程中定义Handler,并接收到消息");
       }
   }
};
Looper.loop();

4.Looper.prepare()方法源码分析

源码如下所示

可以看到Looper中有一个ThreadLocal成员变量,熟悉JDK的同学应该知道,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

public static void prepare() {
    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()能否调用两次或者多次

如果运行,则会报错,并提示prepare中的Excetion信息。由此可以得出在每个线程中Looper.prepare()能且只能调用一次

//这里Looper.prepare()方法调用了两次
Looper.prepare();
Looper.prepare();
Handler mHandler = new Handler() {
   @Override
   public void handleMessage(Message msg) {
       if (msg.what == 1) {
          Log.i(TAG, "在子线程中定义Handler,并接收到消息。。。");
       }
   }
};
Looper.loop();

5.Looper中用什么存储消息

先看一下下面得源代码

看Looper对象的构造方法,可以看到在其构造方法中初始化了一个MessageQueue对象。MessageQueue也称之为消息队列,特点是先进先出,底层实现是单链表数据结构

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(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

得出结论

Looper.prepare()方法初始话了一个Looper对象并关联在一个MessageQueue对象,并且一个线程中只有一个Looper对象,只有一个MessageQueue对象。

6.Handler发送消息如何运作

首先看看构造方法

可以看出在Handler的构造方法中,主要初始化了一下变量,并判断Handler对象的初始化不应再内部类,静态类,匿名类中,并且保存了当前线程中的Looper对象。

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class 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;
}

看handler.sendMessage(msg)方法

关于下面得源码,是步步追踪,看enqueueMessage这个方法,原来msg.target就是Handler对象本身;而这里的queue对象就是我们的Handler内部维护的Looper对象关联的MessageQueue对象。

handler.sendMessage(message);

//追踪到这一步
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);
}

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

看MessageQueue对象的enqueueMessage方法

看到这里MessageQueue并没有使用列表将所有的Message保存起来,而是使用Message.next保存下一个Message,从而按照时间将所有的Message排序

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

7.Looper.loop()方法源码分析

看看里面得源码,如下所示

看到Looper.loop()方法里起了一个死循环,不断的判断MessageQueue中的消息是否为空,如果为空则直接return掉,然后执行queue.next()方法

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

看queue.next()方法源码

大概的实现逻辑就是Message的出栈操作,里面可能对线程,并发控制做了一些限制等。获取到栈顶的Message对象之后开始执行:msg.target.dispatchMessage(msg)

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

那么msg.target是什么呢?通过追踪可以知道就是定义的Handler对象,然后查看一下Handler类的dispatchMessage方法:

可以看到,如果我们设置了callback(Runnable对象)的话,则会直接调用handleCallback方法

在初始化Handler的时候设置了callback(Runnable)对象,则直接调用run方法。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

private static void handleCallback(Message message) {
    message.callback.run();
}

8.runOnUiThread如何实现子线程更新UI

看看源码,如下所示

如果msg.callback为空的话,会直接调用我们的mCallback.handleMessage(msg),即handler的handlerMessage方法。由于Handler对象是在主线程中创建的,所以handler的handlerMessage方法的执行也会在主线程中。

在runOnUiThread程序首先会判断当前线程是否是UI线程,如果是就直接运行,如果不是则post,这时其实质还是使用的Handler机制来处理线程与UI通讯。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

@Override
public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

9.Handler的post方法和view的post方法

Handler的post方法实现很简单,如下所示

mHandler.post(new Runnable() {
    @Override
    public void run() {

    }
});

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

view的post方法也很简单,如下所示

可以发现其调用的就是activity中默认保存的handler对象的post方法

public boolean post(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        return attachInfo.mHandler.post(action);
    }
    ViewRootImpl.getRunQueue().post(action);
    return true;
}

public void post(Runnable action) {
    postDelayed(action, 0);
}

public void postDelayed(Runnable action, long delayMillis) {
    final HandlerAction handlerAction = new HandlerAction(action, delayMillis);

    synchronized (this) {
        if (mActions == null) {
            mActions = new HandlerAction[4];
        }
        mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
        mCount++;
    }
}

10.主线程中Looper的轮询死循环为何没阻塞主线程

造成ANR的原因

造成ANR的原因一般有两种:

当前的事件没有机会得到处理(即主线程正在处理前一个事件,没有及时的完成或者looper被某种原因阻塞住了)

当前的事件正在处理,但没有及时完成

为了避免ANR异常,android使用了Handler消息处理机制。让耗时操作在子线程运行。

问题描述

在处理消息的时候使用了Looper.loop()方法,并且在该方法中进入了一个死循环,同时Looper.loop()方法是在主线程中调用的,那么为什么没有造成阻塞呢?

ActivityThread中main方法

ActivityThread类的注释上可以知道这个类管理着我们平常所说的主线程(UI线程)

首先 ActivityThread 并不是一个 Thread,就只是一个 final 类而已。我们常说的主线程就是从这个类的 main 方法开始,main 方法很简短

public static final void main(String[] args) {
    ...
    //创建Looper和MessageQueue
    Looper.prepareMainLooper();
    ...
    //轮询器开始轮询
    Looper.loop();
    ...
}

Looper.loop()方法无限循环

看看Looper.loop()方法无限循环部分的代码

while (true) {
   //取出消息队列的消息,可能会阻塞
   Message msg = queue.next(); // might block
   ...
   //解析消息,分发消息
   msg.target.dispatchMessage(msg);
   ...
}

为什么这个死循环不会造成ANR异常呢?

因为Android 的是由事件驱动的,looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者说Activity的生命周期都是运行在 Looper.loop() 的控制之下,如果它停止了,应用也就停止了。只能是某一个消息或者说对消息的处理阻塞了 Looper.loop(),而不是 Looper.loop() 阻塞它。

处理消息handleMessage方法

如下所示

可以看见Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时则采用相应措施。

如果某个消息处理时间过长,比如你在onCreate(),onResume()里面处理耗时操作,那么下一次的消息比如用户的点击事件不能处理了,整个循环就会产生卡顿,时间一长就成了ANR。

public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        case LAUNCH_ACTIVITY: {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
            final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
            r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
            handleLaunchActivity(r, null);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
        break;
        case RELAUNCH_ACTIVITY: {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
            ActivityClientRecord r = (ActivityClientRecord) msg.obj;
            handleRelaunchActivity(r);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
        break;
        case PAUSE_ACTIVITY:
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
            handlePauseActivity((IBinder) msg.obj, false, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 2) != 0);
            maybeSnapshot();
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            break;
        case PAUSE_ACTIVITY_FINISHING:
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
            handlePauseActivity((IBinder) msg.obj, true, (msg.arg1 & 1) != 0, msg.arg2, (msg.arg1 & 1) != 0);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            break;
        ...........
    }
}

loop的循环消耗性能吗?

主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。

得出结论

简单的来说:ActivityThread的main方法主要就是做消息循环,一旦退出消息循环,那么你的程序也就可以退出了。

11.得出部分结论

得出得结论如下所示

1.主线程中定义Handler对象,ActivityThread的main方法中会自动创建一个looper,并且与其绑定。如果是子线程中直接创建handler对象,则需要手动创建looper。不过手动创建不太友好,需要手动调用quit方法结束looper。这个后面再说

2.一个线程中只存在一个Looper对象,只存在一个MessageQueue对象,可以存在N个Handler对象,Handler对象内部关联了本线程中唯一的Looper对象,Looper对象内部关联着唯一的一个MessageQueue对象。

3.MessageQueue消息队列不是通过列表保存消息(Message)列表的,而是通过Message对象的next属性关联下一个Message从而实现列表的功能,同时所有的消息都是按时间排序的。

其他介绍 01.关于博客汇总链接

1.技术博客汇总

2.开源项目汇总

3.生活博客汇总

4.喜马拉雅音频汇总

5.其他汇总

02.关于我的博客

github:https://github.com/yangchong211

知乎:https://www.zhihu.com/people/...

简书:http://www.jianshu.com/u/b7b2...

csdn:http://my.csdn.net/m0_37700275

喜马拉雅听书:http://www.ximalaya.com/zhubo...

开源中国:https://my.oschina.net/zbj161...

泡在网上的日子:http://www.jcodecraeer.com/me...

邮箱:yangchong211@163.com

阿里云博客:https://yq.aliyun.com/users/a... 239.headeruserinfo.3.dT4bcV

segmentfault头条:https://segmentfault.com/u/xi...

掘金:https://juejin.im/user/593943...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/74519.html

相关文章

  • Android异步消息机制

    摘要:在子线程中发送消息,主线程接受到消息并且处理逻辑。也称之为消息队列,特点是先进先出,底层实现是单链表数据结构得出结论方法初始话了一个对象并关联在一个对象,并且一个线程中只有一个对象,只有一个对象。 目录介绍 1.Handler的常见的使用方式 2.如何在子线程中定义Handler 3.主线程如何自动调用Looper.prepare() 4.Looper.prepare()方法源码分析...

    王晗 评论0 收藏0

发表评论

0条评论

blair

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<