android消息循环

来源:互联网 发布:php图片$_get 编辑:程序博客网 时间:2024/06/05 22:15

android消息循环的形象描述
android消息循环的本质以及原理(pipe)
例子巩固
推荐博文

android消息循环的描述

android系统有一个核心的特点,那就是android的每一个应用程序都有一个消息队列,每一个应用程序的主线程(ActivityThread)不断地从这个消息队列里面取出消息(looper消息循环),然后由对应的回调函数处理(回调函数用Handler注册)。说白了这个应用程序的主线程在最后就是一个死循环,不断地读取消息,如果没有的话会间断性的休眠唤醒。

1,举一个简单的例子,形象的描述一下,当我们打开一个app之后,最后就进入了这个APP的ActivityThread里面的消息循环,我们一般在我们的应用程序里面会添加onClick方法,这个onClick方法,就是在读到对应的点击事件之后调用的。当我们点击button的时候,内核会产生中断,然后会向/dev/input/eventX(这里的X代表一个数字,其中有一个就是我们的触摸屏)
,在framwork层的systemserver进程里面有一个InputManagerService,这个service会不断的读取eventX里面的数据,当我们点击的时候他读取到点击事件,然后会通过pipe传递给我们的应用程序。也就是进入了我们的消息循环,然后我们的消息循环接收到这个点击事件就能判断出点击的是哪里,传递给我们的button组件,调用button注册的onClick方法,调用完了之后,有回到我们的消息循环如此重复。

通过上面的描述,大家应该可以知道为什么所有的UI操作要在这个主线程里面完成,并且我们注册的方法一定不能阻塞主线程,因为这样的话,我们的主线程就不能继续读取消息队列,造成界面假死。这也就是为什么我们要将耗时的操作放在service或者线程里面完成。

android消息循环的本质以及原理

我们知道android应用程序有一个主消息循环在ActivityThread里面最后会调用loop进入主消息循环,代码如下:

//ActivityThread.java:最后的loop()进入主消息循环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("<pre-initialized>");        Looper.prepareMainLooper();        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        AsyncTask.init();        if (false) {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG, "ActivityThread"));        }        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }

从上面的代码中我们知道最后调用loop函数,这个函数是一个死循环会不断的读取消息循环,如果没有消息则会休眠固定时间,代码如下:

//Looper.java:线程内的单例模式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;    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.recycleUnchecked();        }    }    /**     * 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();    }    public void setMessageLogging(Printer printer) {        mLogging = printer;    }    public static MessageQueue myQueue() {        return myLooper().mQueue;    }    private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();    }    /**     * Returns true if the current thread is this looper's thread.     * @hide     */    public boolean isCurrentThread() {        return Thread.currentThread() == mThread;    }    。。。。。    。。。。。}

我们看Looper的代码会感觉很像单例模式,没有public构造函数,这个类里面大部分都是静态方法,在prepare函数里面会调用sThreadLocal.get()这样就保证了一个线程里面只有一个消息循环,之所以一个线程里面只能有一个消息循环,是由于这里的loop是阻塞IO方式的,所以不能同时实时的监控多个looper调用,这个可以认为在线程内是单例模式。
下面讲解Looper里面比较重要的几个function:prepare(),prepareMainLooper(),getMainLooper(),loop()

1,prepare():为调用者线程创造looper,在这个函数里面会通过sThreadLocal.get()保证当前线程只有一个looper.
2,prepareMainLooper():这个函数是android应用程序主线程调用的保证应用程序主线程有一个消息循环,从而保证所有与UI相关的操作全部在主线程中完成,如果早其他线程中,则会出现异常。
3,getMainLooper():这个function用于得到主消息循环的
looper–MainLooper
4,loop:该函数中会不断的读取消息循环代码是:queue.next(),有兴趣的可以继续跟踪这里是如何唤醒的,在这里提示一下,这里会通过JNI调用,本质上是一个pipe(管道),管道有数据(注意不是message,仅仅起到唤醒的作用)然后looper被唤醒,得到message,然后会调用msg.target.dispatchMessage处理:这里target其实以一个handler类型的在我们发送消息的时候会指定target,这个一般我们会继承handler类,因此这里的target一般会是我们的继承的handler,调用handler的dispatchMassage,在这里我将这个函数代码粘贴如下:

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

我们可以看到这个里面会有三个优先级,首先看msg.callback也就是我们发送消息的时候对消息指定的处理函数,如果我们没有指定,那么接着会判断我们实例化Handler的时候有没有指定mCallback,如果还是没有的话,只有在这种情况下才会调用handleMessage(),在这里我不得不感叹android的强大,一个小小的pipe竟然应用的如此的淋漓尽致。膜拜。

例子巩固

public class MyTimer extends Activity {      private int num=0;    Timer timer = new Timer();      Handler handler = new Handler(){           public void handleMessage(Message msg) {              switch (msg.what) {                  case 1:                      setTitle("Mytimer:"+num);                  break;                  }                  super.handleMessage(msg);          }      };      TimerTask task = new TimerTask(){            public void run() {              Message message = new Message();              num++;                message.what = 1;                  handler.sendMessage(message);            }                };      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.main);          timer.schedule(task, 1000);      }  }  

在上面这个例子中为了简单我们利用了android的主消息循环,因此这里只是实现了handler,并且指定了handleMessage(),因此当我们的timer启动之后在run方法中handler.sendMessage(message);然后handleMessage()会处理,调用setTitle(“Mytimer”+num); 这样我们的UI就会每隔1秒钟更新一次,并且num会自加一次。并显示

推荐博文

Android应用程序线程消息循环模型分析:
http://blog.csdn.net/luoshengyang/article/details/6905587

Android应用程序消息处理机制(Looper、Handler)分析:
http://blog.csdn.net/luoshengyang/article/details/6817933

0 0