Android异步消息处理机制详解及源码分析(1)

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;

}

可以看到,在第11行调用了mLooper = Looper.myLooper();语句,然后获取了一个Looper对象mLooper ,如果mLooper实例为空,则会抛出一个运行时异常(Can’t create handler inside thread that has not called Looper.prepare()!)。

3-1-2 Looper实例化源码

好奇的你指定在想什么时候mLooper 对象才可能为空呢?很简单,跳进去看下吧,Looper类的静态方法myLooper如下:

/**

  • Return the Looper object associated with the current thread. Returns

  • null if the calling thread is not associated with a Looper.

*/

public static Looper myLooper() {

return sThreadLocal.get();

}

咦?这里好简单。单单就是从sThreadLocal对象中get了一个Looper对象返回。跟踪了一下sThreadLocal对象,发现他定义在Looper中,是一个static final类型的ThreadLocal<Looper>对象(在Java中,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的,各个线程中访问的是不同的对象。)。所以可以看出,如果sThreadLocal中有Looper存在就返回Looper,没有Looper存在自然就返回null了。

这时候你一定有疑惑,既然这里是通过sThreadLocal的get获得Looper,那指定有地方对sThreadLocal进行set操作吧?是的,我们在Looper类中跟踪发现如下:

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的static方法prepare没有?这段代码首先判断sThreadLocal中是否已经存在Looper了,如果还没有则创建一个新的Looper设置进去。

那就看下Looper的实例化,如下:

private Looper(boolean quitAllowed) {

mQueue = new MessageQueue(quitAllowed);

mThread = Thread.currentThread();

}

可以看见Looper构造函数无非就是创建了一个MessageQueue(它是一个消息队列,用于将所有收到的消息以队列的形式进行排列,并提供入队和出队的方法。)和货到当前Thread实例引用而已。通过这里可以发现,一个Looper只能对应了一个MessageQueue。

你可能会说上面的例子在子线程中明明先调运的是Looper.prepare();方法,这里怎么有参数了?那就继续看吧,如下:

public static void prepare() {

prepare(true);

}

可以看见,prepare()仅仅是对prepare(boolean quitAllowed) 的封装而已,默认传入了true,也就是将MessageQueue对象中的quitAllowed标记标记为true而已,至于MessageQueue后面会分析。

稀奇古怪的事情来了!如果你足够留意上面的例子,你会发现我们在UI Thread中创建Handler时没有调用Looper.prepare();,而在initData方法中创建的Child Thread中首先就调运了Looper.prepare();。你指定很奇怪吧?UI Thread为啥不需要呢?上面源码分析明明需要先保证mLooper对象不为null呀?

这是由于在UI线程(Activity等)启动的时候系统已经帮我们自动调用了Looper.prepare()方法。

那么在哪启动的呢?这个涉及Android系统架构问题比较多,后面文章会分析Activity的启动流程。这里你只要知道,以前一直都说Activity的人口是onCreate方法,其实android上一个应用的入口应该是ActivityThread类的main方法就行了。

所以为了解开UI Thread为何不需要创建Looper对象的原因,我们看下ActivityThread的main方法,如下:

public static void main(String[] args) {

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

Security.addProvider(new AndroidKeyStoreProvider());

// Make sure TrustedCertificateStore looks in the right place for CA certificates

final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());

TrustedCertificateStore.setDefaultUserDirectory(configDir);

Process.setArgV0(“”);

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

}

Looper.loop();

throw new RuntimeException(“Main thread loop unexpectedly exited”);

}

看见22行没?没说错吧?Looper.prepareMainLooper();,我们跳到Looper看下prepareMainLooper方法,如下:

public static void prepareMainLooper() {

prepare(false);

synchronized (Looper.class) {

if (sMainLooper != null) {

throw new IllegalStateException(“The main Looper has already been prepared.”);

}

sMainLooper = myLooper();

}

}

可以看到,UI线程中会始终存在一个Looper对象(sMainLooper 保存在Looper类中,UI线程通过getMainLooper方法获取UI线程的Looper对象),从而不需要再手动去调用Looper.prepare()方法了。如下Looper类提供的get方法:

public static Looper getMainLooper() {

synchronized (Looper.class) {

return sMainLooper;

}

}

看见没有,到这里整个Handler实例化与为何子线程在实例化Handler之前需要先调运Looper.prepare();语句的原理分析完毕。

3-1-3 Handler与Looper实例化总结

到此先初步总结下上面关于Handler实例化的一些关键信息,具体如下:

  1. 在主线程中可以直接创建Handler对象,而在子线程中需要先调用Looper.prepare()才能创建Handler对象,否则运行抛出”Can’t create handler inside thread that has not called Looper.prepare()”异常信息。

  2. 每个线程中最多只能有一个Looper对象,否则抛出异常。

  3. 可以通过Looper.myLooper()获取当前线程的Looper实例,通过Looper.getMainLooper()获取主(UI)线程的Looper实例。

  4. 一个Looper只能对应了一个MessageQueue。

  5. 一个线程中只有一个Looper实例,一个MessageQueue实例,可以有多个Handler实例。

Handler对象也创建好了,接下来就该用了吧,所以下面咱们从Handler的收发消息角度来分析分析源码。

3-2 继续看看Handler消息收发机制源码

3-2-1 通过Handler发消息到消息队列

还记得上面的例子吗?我们在Child Thread的最后通过主线程的Handler对象调运sendEmptyMessage方法发送出去了一条消息。

当然,其实Handler类提供了许发送消息的方法,我们这个例子只是用了最简单的发送一个empty消息而已,有时候我们会先定义一个Message,然后通过Handler提供的其他方法进行发送。通过分析Handler源码发现Handler中提供的很多个发送消息方法中除了sendMessageAtFrontOfQueue()方法之外,其它的发送消息方法最终都调用了sendMessageAtTime()方法。所以,咱们先来看下这个sendMessageAtTime方法,如下:

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

}

再看下Handler中和其他发送方法不同的sendMessageAtFrontOfQueue方法,如下:

public final boolean sendMessageAtFrontOfQueue(Message msg) {

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, 0);

}

对比上面两个方法可以发现,表面上说Handler的sendMessageAtFrontOfQueue方法和其他发送方法不同,其实实质是相同的,仅仅是sendMessageAtFrontOfQueue方法是sendMessageAtTime方法的一个特例而已(sendMessageAtTime最后一个参数传递0就变为了sendMessageAtFrontOfQueue方法)。所以咱们现在继续分析sendMessageAtTime方法,如下分析:

sendMessageAtTime(Message msg, long uptimeMillis)方法有两个参数;msg是我们发送的Message对象,uptimeMillis表示发送消息的时间,uptimeMillis的值等于从系统开机到当前时间的毫秒数再加上延迟时间。

在该方法的第二行可以看到queue = mQueue,而mQueue是在Handler实例化时构造函数中实例化的。在Handler的构造函数中可以看见mQueue = mLooper.mQueue;,而Looper的mQueue对象上面分析过了,是在Looper的构造函数中创建的一个MessageQueue。

接着第9行可以看到,上面说的两个参数和刚刚得到的queue对象都传递到了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);

}

这个方法首先将我们要发送的消息Message的target属性设置为当前Handler对象(进行关联);接着将msg与uptimeMillis这两个参数都传递到MessageQueue(消息队列)的enqueueMessage()方法中,所以接下来我们就继续分析MessageQueue类的enqueueMessage方法,如下:

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(“MessageQueue”, 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;

}

通过这个方法名可以看出来通过Handler发送消息实质就是把消息Message添加到MessageQueue消息队列中的过程而已。

通过上面遍历等next操作可以看出来,MessageQueue消息队列对于消息排队是通过类似c语言的链表来存储这些有序的消息的。其中的mMessages对象表示当前待处理的消息;然后18到49行可以看出,消息插入队列的实质就是将所有的消息按时间(uptimeMillis参数,上面有介绍)进行排序。所以还记得上面sendMessageAtFrontOfQueue方法吗?它的实质就是把消息添加到MessageQueue消息队列的头部(uptimeMillis为0,上面有分析)。

到此Handler的发送消息及发送的消息如何存入到MessageQueue消息队列的逻辑分析完成。

那么问题来了!既然消息都存入到了MessageQueue消息队列,当然要取出来消息吧,不然存半天有啥意义呢?我们知道MessageQueue的对象在Looper构造函数中实例化的;一个Looper对应一个MessageQueue,所以说Handler发送消息是通过Handler构造函数里拿到的Looper对象的成员MessageQueue的enqueueMessage方法将消息插入队列,也就是说出队列一定也与Handler和Looper和MessageQueue有关系。

还记不记得上面实例部分中Child Thread最后调运的Looper.loop();方法呢?这个方法其实就是取出MessageQueue消息队列里的消息方法。具体在下面分析。

3-2-2 通过Handler接收发送的消息

先来看下上面例子中Looper.loop();这行代码调运的方法,如下:

/**

  • 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

Printer logging = me.mLogging;

if (logging != null) {

logging.println(">>>>> Dispatching to " + msg.target + " " +

msg.callback + ": " + msg.what);

}

msg.target.dispatchMessage(msg);

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

}

}

可以看到,第6行首先得到了当前线程的Looper对象me,接着第10行通过当前Looper对象得到与Looper对象一一对应的MessageQueue消息队列(也就类似上面发送消息部分,Handler通过myLoop方法得到Looper对象,然后获取Looper的MessageQueue消息队列对象)。17行进入一个死循环,18行不断地调用MessageQueue的next()方法,进入MessageQueue这个类查看next方法,如下:

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 (false) Log.v(“MessageQueue”, "Returning message: " + msg);

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(“MessageQueue”, “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;

}

}

可以看出来,这个next方法就是消息队列的出队方法(与上面分析的MessageQueue消息队列的enqueueMessage方法对比)。可以看见上面代码就是如果当前MessageQueue中存在待处理的消息mMessages就将这个消息出队,然后让下一条消息成为mMessages,否则就进入一个阻塞状态(在上面Looper类的loop方法上面也有英文注释,明确说到了阻塞特性),一直等到有新的消息入队。

继续看loop()方法的第30行(msg.target.dispatchMessage(msg);),每当有一个消息出队就将它传递到msg.target的dispatchMessage()方法中。其中这个msg.target其实就是上面分析Handler发送消息代码部分Handler的enqueueMessage方法中的msg.target = this;语句,也就是当前Handler对象。所以接下来的重点自然就是回到Handler类看看我们熟悉的dispatchMessage()方法,如下:

/**

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

}

}

可以看见dispatchMessage方法中的逻辑比较简单,具体就是如果mCallback不为空,则调用mCallback的handleMessage()方法,否则直接调用Handler的handleMessage()方法,并将消息对象作为参数传递过去。

这样我相信大家就都明白了为什么handleMessage()方法中可以获取到之前发送的消息了吧!

对了,既然上面说了获取消息在MessageQueue消息队列中是一个死循环的阻塞等待,所以Looper的quit方法也很重要,这样在不需要时可以退出这个死循环,如上面实例部分使用所示。

3-2-3 结束MessageQueue消息队列阻塞死循环源码分析

如下展示了Looper类的quit方法源码:

public void quit() {

mQueue.quit(false);

}

看见没有,quit方法实质就是调运了MessageQueue消息队列的quit,如下:

void quit(boolean safe) {

if (!mQuitAllowed) {

throw new IllegalStateException(“Main thread not allowed to quit.”);

}

synchronized (this) {

if (mQuitting) {

return;

}

mQuitting = true;

if (safe) {

removeAllFutureMessagesLocked();

} else {

removeAllMessagesLocked();

}

// We can assume mPtr != 0 because mQuitting was previously false.

nativeWake(mPtr);

}

}

看见上面2到4行代码没有,通过判断标记mQuitAllowed来决定该消息队列是否可以退出,然而当mQuitAllowed为fasle时抛出的异常竟然是”Main thread not allowed to quit.”,Main Thread,所以可以说明Main Thread关联的Looper一一对应的MessageQueue消息队列是不能通过该方法退出的。

你可能会疑惑这个mQuitAllowed在哪设置的?

其实他是MessageQueue构造函数传递参数传入的,而MessageQueue对象的实例化是在Looper的构造函数实现的,所以不难发现mQuitAllowed参数实质是从Looper的构函数传入的。上面实例化Handler模块源码分析时说过,Looper实例化是在Looper的静态方法prepare(boolean quitAllowed)中处理的,也就是说mQuitAllowed是由Looper.prpeare(boolean quitAllowed)参数传入的。追根到底说明mQuitAllowed就是Looper.prpeare的参数,我们默认调运的Looper.prpeare();其中对mQuitAllowed设置为了true,所以可以通过quit方法退出,而主线程ActivityThread的main中使用的是Looper.prepareMainLooper();,这个方法里对mQuitAllowed设置为false,所以才会有上面说的”Main thread not allowed to quit.”。

回到quit方法继续看,可以发现实质就是对mQuitting标记置位,这个mQuitting标记在MessageQueue的阻塞等待next方法中用做了判断条件,所以可以通过quit方法退出整个当前线程的loop循环。

到此整个Android的一次完整异步消息机制分析使用流程结束。接下来进行一些总结提升与拓展。

3-3 简单小结下Handler整个使用过程与原理

通过上面的源码分析原理我们可以总结出整个异步消息处理流程的关系图如下:

这里写图片描述

这幅图很明显的表达出了Handler异步机制的来龙去脉,不做过多解释。

上面实例部分我们只是演示了Handler的局部方法,具体Handler还有很多方法,下面详细介绍。

3-4 再来看看Handler源码的其他常用方法

在上面例子中我们只是演示了发送消息的sendEmptyMessage(int what)方法,其实Handler有如下一些发送方式:

sendMessage(Message msg); sendEmptyMessage(int what); sendEmptyMessageDelayed(int what, long delayMillis); sendEmptyMessageAtTime(int what, long uptimeMillis); sendMessageDelayed(Message msg, long delayMillis); sendMessageAtTime(Message msg, long uptimeMillis); sendMessageAtFrontOfQueue(Message msg);方法。

这些方法不再做过多解释,用法雷同,顶一个Message决定啥时发送到target去。

post(Runnable r); postDelayed(Runnable r, long delayMillis);等post系列方法。

该方法实质源码其实就是如下:

public final boolean postDelayed(Runnable r, long delayMillis)

{

return sendMessageDelayed(getPostMessage®, delayMillis);

}

额,原来post方法的实质也是调运sendMessageDelayed()方法去处理的额,看见getPostMessage®方法没?如下源码:

private static Message getPostMessage(Runnable r) {

Message m = Message.obtain();

m.callback = r;

return m;

}

如上方法仅仅是将消息的callback字段指定为传入的Runnable对象r。其实这个Message对象的m.callback就是上面分析Handler接收消息回调处理dispatchMessage()方法中调运的。在Handler的dispatchMessage方法中首先判断如果Message的callback等于null才会去调用handleMessage()方法,否则就调用handleCallback()方法。那就再看下Handler的handleCallback()方法源码,如下:

private static void handleCallback(Message message) {

message.callback.run();

}

额,这里竟然直接执行了Runnable对象的run()方法。所以说我们在Runnable对象的run()方法里更新UI的效果完全和在handleMessage()方法中更新UI相同,特别强调这个Runnable的run方法还在当前线程中阻塞执行,没有创建新的线程(很多人以为是Runnable就创建了新线程)。

Activity.runOnUiThread(Runnable);方法。

首先看下Activity的runOnUiThread方法源码:

public final void runOnUiThread(Runnable action) {

if (Thread.currentThread() != mUiThread) {

mHandler.post(action);

} else {

action.run();

}

}

看见没有,实质还是在UI线程中执行了Runnable的run方法。不做过多解释。

View.post(Runnable);和View.postDelayed(Runnable action, long delayMillis);方法。

首先看下View的postDelayed方法源码:

public boolean postDelayed(Runnable action, long delayMillis) {

final AttachInfo attachInfo = mAttachInfo;

if (attachInfo != null) {

return attachInfo.mHandler.postDelayed(action, delayMillis);

}

// Assume that post will succeed later

ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);

return true;

}

看见没有,实质还是在UI线程中执行了Runnable的run方法。不做过多解释。

到此基本上关于Handler的所有发送消息方式都被解释明白了。既然会用了基本的那就得提高下,接下来看看关于Message的一点优化技巧。

3-5 关于Handler发送消息的一点优化分析

还记得我们说过,当发送一个消息时我们首先会new一个Message对象,然后再发送吗?你是不是觉得每次new Message很浪费呢?那么我们就来分析一下这个问题。

如下是我们正常发送消息的代码局部片段:

Message message = new Message();

message.arg1 = 110;

message.arg2 = 119;

message.what = 0x120;

message.obj = “Test Message Content!”;

mHandler.sendMessage(message);

相信很多初学者都是这么发送消息的吧?当有大量多次发送时如上写法会不太高效。不卖关子,先直接看达到同样效果的优化写法,如下:

mHandler.sendMessage(mHandler.obtainMessage(0x120, 110, 119, ““Test Message Content!””));

咦?怎么send时没实例化Message?这是咋回事?我们看下mHandler.obtainMessage(0x120, 110, 119, "\"Test Message Content!\"")这一段代码,obtainMessage是Handler提供的一个方法,看下源码:

总结

最后小编想说:不论以后选择什么方向发展,目前重要的是把Android方面的技术学好,毕竟其实对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

这里附上我整理的几十套腾讯、字节跳动,京东,小米,头条、阿里、美团等公司19年的Android面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

技术进阶之路很漫长,一起共勉吧~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

当发送一个消息时我们首先会new一个Message对象,然后再发送吗?你是不是觉得每次new Message很浪费呢?那么我们就来分析一下这个问题。

如下是我们正常发送消息的代码局部片段:

Message message = new Message();

message.arg1 = 110;

message.arg2 = 119;

message.what = 0x120;

message.obj = “Test Message Content!”;

mHandler.sendMessage(message);

相信很多初学者都是这么发送消息的吧?当有大量多次发送时如上写法会不太高效。不卖关子,先直接看达到同样效果的优化写法,如下:

mHandler.sendMessage(mHandler.obtainMessage(0x120, 110, 119, ““Test Message Content!””));

咦?怎么send时没实例化Message?这是咋回事?我们看下mHandler.obtainMessage(0x120, 110, 119, "\"Test Message Content!\"")这一段代码,obtainMessage是Handler提供的一个方法,看下源码:

总结

最后小编想说:不论以后选择什么方向发展,目前重要的是把Android方面的技术学好,毕竟其实对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

这里附上我整理的几十套腾讯、字节跳动,京东,小米,头条、阿里、美团等公司19年的Android面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

[外链图片转存中…(img-EodjIlEo-1714423056253)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

技术进阶之路很漫长,一起共勉吧~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值