Android的异步消息机制在Android系统中的重要性读者应该都很清楚,无论是在平时开发中,还是笔试、面试中,这方面的内容都是无法避免的。Android提供了Handler和Looper来满足线程间通信,而MessageQueue则是用来存放线程放入的消息。下面我们就结合源码分析一下这三者的关系。

(1)Handler的基本使用方式:

public class MainActivity extends AppCompatActivity {    private MyTextView title;    private static final String TAG = MainActivity.class.getSimpleName();    private static final int HANDLER1 = 1;    /**     * 在主线程中定义Handler,并实现对应的handleMessage方法     */    public static Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            if (msg.what == HANDLER1) {                Log.i(TAG, "接收到handler消息...");            }        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        title = (MyTextView) findViewById(R.id.title);        title.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                new Thread() {                    @Override                    public void run() {                        // 在子线程中发送异步消息                        mHandler.sendEmptyMessage(HANDLER1);                    }                }.start();            }        });    }}

相信上面的用法大家都很熟悉,但现在有个问题,如果想把Handler放到子线程中使用,是怎么样子的呢?下面我们看一段代码。

public class MainActivity extends AppCompatActivity {    private MyTextView title;    private static final String TAG = MainActivity.class.getSimpleName();    private static final int HANDLER1 = 1;    /**     * 在主线程中定义Handler,并实现对应的handleMessage方法     */    Handler mHandler;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        title = (MyTextView) findViewById(R.id.title);        title.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                mHandler.sendEmptyMessage(HANDLER1);            }        });        new Thread() {            @Override            public void run() {                // 在子线程中发送异步消息                mHandler = new Handler() {                    @Override                    public void handleMessage(Message msg) {                        if (msg.what == HANDLER1) {                            Log.i(TAG, "接收到handler消息...");                        }                    }                };            }        }.start();    }}

点击按钮运行后会报下面的错。

E/AndroidRuntime: FATAL EXCEPTION: Thread-95              Process: com.eyck.androidsample, PID: 2552              java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()                  at android.os.Handler.(Handler.java:200)                  at android.os.Handler.(Handler.java:114)                  at com.eyck.androidsample.MainActivity$2$1.(MainActivity.java:0)                  at com.eyck.androidsample.MainActivity$2.run(MainActivity.java:41)

根据提示信息我们可以很清晰的看到,在初始化Handler对象之前需要先调用Looper.prepare()方法,那么我们就加上这句代码再执行一次。

new Thread() {            @Override            public void run() {                // 在子线程中发送异步消息                Looper.prepare();                mHandler = new Handler() {                    @Override                    public void handleMessage(Message msg) {                        if (msg.what == HANDLER1) {                            Log.i(TAG, "接收到handler消息...");                        }                    }                };                Looper.loop();            }        }.start();

这次运行就不会报错了,说明程序在初始化Handler之前需要调用Looper.prepare(),那么主线程为什么能直接初始化Handler?相信有的读者已经想到,是不是在初始化主线程时系统已经帮我们,我们来到应用程序的入口,在APP初始化时会执行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());    AndroidKeyStoreProvider.install();    // 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"));    }    // End of event ActivityThreadMain.    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);    Looper.loop();    throw new RuntimeException("Main thread loop unexpectedly exited");}

可以看到这里调用了Looper.prepareMainLooper();和Looper.loop();两个方法。接下来我们看一下Handler的源码。

首先就是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();    }}

可以看到在prepareMainLooper()中调用了prepare(false);方法。

接下来看一下Looper.prepare();
public static void prepare() {
prepare(true);
}

可以看到,上面两个方法都调用了prepare(boolean quitAllowed);那接下来我们就来看一下这个方法。

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中有一个ThreadLocal的成员变量,关于ThreadLocal在这里不做详细说明,它的主要作用是为每个使用该变量的线程提供一个独立的变量副本,所以每一个线程都可以独立的改变自己的副本,而不会影响其他线程所对应的副本。而且从中我们也可以看出在每个线程中Looper.prepare()只能调用一次,不然就会抛出”Only one Looper may be created per thread”异常。

接下来我们继续看new Looper(quitAllowed);

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

可以清楚的看到在构造方法中,初始化了一个MessageQueue对象,以及获取了当前的线程。

按着代码的顺序,接下来我们看一下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;}

在Handler的构造方法中,主要对一些相关的变量进行初始化。我们再看一下mLooper = Looper.myLooper();这句代码

public static @Nullable Looper myLooper() {    return sThreadLocal.get();}

通过上面的代码我们可以清晰看到Looper.myLooper();就是从sThreadLocal中得到我们之前保存的Looper对象,这样就和前面的sThreadLocal.set(new Looper(quitAllowed));联系起来了。

接下来我们再看一下sendEmptyMessage();

在Handler中有这么几个方法:

sendMessage(Message msg);sendEmptyMessage(int what);sendMessageDelayed(msg, 0);sendEmptyMessageDelayed(what, 0);

最后都调用了sendMessageAtTime(Message msg, long uptimeMillis)这个方法

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(queue, msg, uptimeMillis);

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

可以看到这里的msg.target就是Handler对象,queue对象就是我们的Handler内部维护的Looper对象关联的MessageQueue对象。

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

可以看到这里Message.next按照时间将所有的Message连接起来。

接下来继续看Looper.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();    }}

这一段代码中有一个for的死循环,在死循环中不断获取MessageQueue中的消息,Message msg = queue.next(); // might block,接下来如果获取到不为空的消息,就会调用msg.target.dispatchMessage(msg);进行消息的分发,我们在前面已经介绍过msg.target就是一个Handler对象,也就是接下来就会调用Handler中的dispatchMessage(msg);

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

可以清晰的看到,这里调用了handleMessage(msg);,这个方法不正是我们在MainActivity里面调用的回调接口吗?

关于Android异步消息相关的就分析到这里,如果有什么不足的地方,希望读者指出,谢谢。

更多相关文章

  1. android媒体--stagefright概述【一】
  2. android 登陆、提交数据或加载数据时提示页面
  3. Visual Studio跨平台开发实战(5) - Xamarin Android多页面应用程
  4. 第一行代码 Android读书笔记(一)
  5. Android的MediaPlayer架构介绍
  6. Testing和Instrumentation
  7. Android(安卓)触控事件解析(Mastering the Android(安卓)Touch Sy
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. centos 普通用户使用root的权限
  2. Python的容器有哪些?分别有什么作用?
  3. android 问题汇总系列之七
  4. 不再纠结,一文详解pandas中的map、apply、
  5. 90题细品吴恩达《机器学习》,感受被刷题支
  6. 谈谈统计学正态分布阈值原理在数据分析工
  7. 学Flask框架有什么用?为什么学习Flask?
  8. n种方式教你用python读写excel等数据文件
  9. AKS网络模型介绍及创建技巧
  10. Python地信专题 | 基于geopandas的空间数