Android线程消息通信(二)

创建线程消息队列

Android应用程序的消息队列是使用一个MessageQueue对象来描述的,它可以通过调用Looper类的静态成员函数prepareMainLooper或者prepare来创建,其中,前者用来为应用程序的主线程创建消息队列;而后者用来为应用程序的其它子线程创建消息队列。

在分析Android应用程序线程的消息队列的创建过程之前,先要了解一下Looper类和MessageQueue类的实现。

Looper类源代码:

public final class Looper {
    private static final String TAG = "Looper";

    // sThreadLocal.get() will return null unless you‘ve called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;
    final Thread mThread;
    volatile boolean mRun;

    private Printer mLogging;

     /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    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));
    }
 /**
     * Initialize the current thread as a looper, marking it as an
     * application‘s main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    /** Returns the application‘s main looper, which lives in the main thread of the application.
     */
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

    /**
     * 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.recycle();
        }
    }

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

    /**
     * Control logging of messages as they are processed by this Looper.  If
     * enabled, a log message will be written to <var>printer</var>
     * at the beginning and ending of each message dispatch, identifying the
     * target Handler and message contents.
     *
     * @param printer A Printer object that will receive log messages, or
     * null to disable message logging.
     */
    public void setMessageLogging(Printer printer) {
        mLogging = printer;
    }

    /**
     * Return the {@link MessageQueue} object associated with the current
     * thread.  This must be called from a thread running a Looper, or a
     * NullPointerException will be thrown.
     */
    public static MessageQueue myQueue() {
        return myLooper().mQueue;
    }

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

Looper类有一个类型为ThreadLocal的静态成员变量sThreadLocal,它是用来保存线程中的Looper对象的。我们可以将Looper类的静态成员变量sThread理解为一个线程局部变量,或者一个HashMap,每一个创建了消息队列的Android应用程序线程在里面都有一个关联的Looper对象。我们调用这个静态成员变量的成员函数get或者;当我们调用这个静态成员变量的成员函数set时,就可以将一个Looper对象与当前线程关联起来。

Looper类的静态成员函数prepare中,if语句检查当前线程是否已经建有一个Looper对象了。如果有,那么就会抛出一个异常;否则,创建一个Looper对象,然后将这个Looper对象保存在Looper类的静态成员变量sThreadLocal中。总之,prepare方法创建了一个线程独立且唯一的Looper对象,如果要访问这Looper对象,只需要调用myLooper方法。

Looper类的静态成员函数prepareMainLooper中,先调用Looper类的静态成员函数prepare在当前线程中创建一个Looper对象,然后调用Looper的静态成员函数setMainLooper将这个Looper对象保存在Looper类的静态成员变量mMainLooper中。

Looper类的静态成员函数prepareMainLooper只能在Android应用程序的主线程中调用。Android应用程序主线程是一个特殊的线程,只有它才能执行与UI相关的操作,因此,我们又将它称作UI线程。将Android应用程序的主线程Looper对象保存在一个独立的静态成员变量中,是为了让其它线程可以通过Looper类的静态成员函数getMainLooper来访问它,从而可以往它的消息队列中发送一些与UI相关的消息。

一个Looper对象在创建过程中,会在内部创建一个MessageQueue对象,并且保存在它的成员变量mQueue中。

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mRun = true;  //表示当前线程处于run状态
        mThread = Thread.currentThread();  //存储当前线程
    }

一个MessageQueue对象在创建的过程中,又会在C++层中创建一个NativeMessageQueue对象,这是通过调用MessageQueue类的成员函数nativeInit来实现的。

public final class MessageQueue {
    // True if the message queue can be quit.
    private final boolean mQuitAllowed;

    @SuppressWarnings("unused")
    private int mPtr; // used by native code
    //... ...
    private native static int nativeInit();
    MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }
}

在MessageQueue构造方法中,首先给传入的quitAllowed参数的成员变量mQuitAllowed赋值为true,表示当前消息循环允许退出;然后调用Native方法nativeInit进入Native层的初始化。

nativeInit方法由JNI层的android_os_MessageQueue_nativeInit实现,其主要工作分为三部分:

1)创建一个NativeMessageQueue类型的对象,并增加其引用;

2)NativeMessageQueue对象在创建过程中,会在其内部创建一个C++层的Looper对象。C++层的Looper对象在创建的过程中,又会在内部创建一个管道,这个管道的读端和写端分别为mWakeReadPipeFd和mWakeWritePipeFd。这个管道在一个线程的消息循环过程起到的作用非常大。首先,当一个线程没有新的消息要处理时,它就会睡眠在这个管道的读端文件描述符上,直到有新的消息需要处理为止;其次,当其它线程向这个线程的消息队列发送了一个消息之后,其它线程就会通过这个管道的写端文件描述符往这个管道写入一个数据,从而将这个线程唤醒,以便它可以对刚刚发送到它的消息队列中的消息进行处理;

3)将NativeMessageQueue与Java层的MessageQueue关联起来。Java层MessageQueue对象的mPtr成员变量保存的是JNI层nativeMessageQueue的地址,可以通过mPtr成员变量访问Native层的NativeMessageQueue对象。

Looper类、MessageQueue类、Looper(Native)类和NativeMessage类的关系图示

时间: 2024-10-14 09:57:49

Android线程消息通信(二)的相关文章

Android线程管理(二)&mdash;&mdash;ActivityThread

线程通信.ActivityThread及Thread类是理解Android线程管理的关键. 线程,作为CPU调度资源的基本单位,在Android等针对嵌入式设备的操作系统中,有着非常重要和基础的作用.本小节主要从以下三个方面进行分析: <Android线程管理(一)--线程通信> <Android线程管理(二)--ActivityThread>  <Android线程管理(三)--Thread类的内部原理.休眠及唤醒> 二.ActivityThread的主要工作及实现机

Android线程管理(二)——ActivityThread

线程通信.ActivityThread及Thread类是理解Android线程管理的关键. 线程,作为CPU调度资源的基本单位,在Android等针对嵌入式设备的操作系统中,有着非常重要和基础的作用.本小节主要从以下三个方面进行分析: <Android线程管理(一)——线程通信> <Android线程管理(二)——ActivityThread> <Android线程管理(三)——Thread类的内部原理.休眠及唤醒> 二.ActivityThread的主要工作及实现机制

线程消息通信与异步处理

转载请标明出处: http://blog.csdn.net/yujun411522/article/details/46444869 本文出自:[yujun411522的博客] 关于android内消息通信和handler的知识在之前的Handler中已经简要介绍过了,这里介绍在native层的实现机制. 相信大家都知道一个标准的Looper线程的写法: public MyLooperThread extends Thread{ private Handler mHandler; public

Android线程间通信机制

当android应用程序运行时,一个主线程被创建(也称作UI线程),此线程主要负责处理UI相关的事件,由于Android采用UI单线程模型,所以只能在主线程中对UI元素进行操作,如果在非UI线程直接对UI进行了操作,则会报错,另外,对于运算量较大的操作和IO操作,我们需要新开线程来处理这些工作,以免阻塞UI线程,子线程与主线程之间是怎样进行通信的呢?此时就要采用消息循环机制(Looper)与Handler进行处理. 一.基本概念 Looper:每一个线程都可以产生一个Looper,用来管理线程的

Android 线程间通信

进程与线程的区别? 在Android中,线程是跑在进程之中的,当手机打开一个APP就相当于打开了一个进程,比如:UI界面的更新,就是在主线程中完成的,我也可以自定义一些子线程来完成所需要的任务. 如何创建线程?创建线程的几种方式? 1.创建一个类继承Thread类 2.创建一个类实现Runnable接口 什么是多线程? 线程是程序中一个单一的顺序控制流程,在程序中同是运行多个线程完成不同的工作,称为多线程 ANR的基础知识及产生? ANR:application not responding :

Android线程间通信更新UI的方法(重点分析EventBus)

Android的UI更新只能在UI线程中,即主线程.子线程中如果要进行UI更新,都是要通知主线程来进行. 几种实现方式总结如下,欢迎补充. 1.runOnUiThread() 子线程中持有当前Activity引用(假如为Activity mActivity;),即可以调用mActivity的runOnUiThread(Runnable r)方法. 2.post()和postDelay() 子线程如果持有某个View的引用,要对该View进行更新,则可调用该View对象的post(Runnable

android 线程间通信原理

要理解原理, read the fucking source 1,从HandlerThread入手. HandlerThread是android系统提供的类,继承Thread,是一个线程.请看run方法. public void run() { mTid = Process.myTid(); Looper.prepare();// #1 synchronized (this) { mLooper = Looper.myLooper();// #2 notifyAll(); } Process.s

android线程间通信的四种实现方式

通过Handler机制. private void one() { handler=new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case 123: tv.setText(""+msg.obj); break; } } }; new Thread(){ @Override public void run() {

android Handler消息通信

1 package com.example.testhandler; 2 3 import android.os.Bundle; 4 import android.os.Handler; 5 import android.os.Message; 6 import android.app.Activity; 7 import android.view.Menu; 8 import android.widget.TextView; 9 10 public class MainActivity ext