Android Loop&Handle学习总结
2012-01-08 00:21
309 查看
昨晚偷懒,这篇博客只写了一个标题,今天早晨一看,还有15的阅读量。实在是对不起那些同学.......换了是我,也会BS这样的LZ吧!sorry 啦
-------------------------------------------------------------------------------------------------------------------------------------------------------------
菜鸟我刚刚接触android源码的时候,当时连java语言都不太熟悉(菜鸟我一直是学C/C++),看到android某个应用的源码的时候,曾为这样的代码迷惑过。
在同一个文件中不就是想调用restartPreview()函数吗?为什么不直接调用就好,还要发个消息然后在这个handleMessage()中来调用呢?初学菜鸟有没有同样的困惑?还是只有LZ一人太白痴了。
那么到底为什么要这么做呢。是为了异步,是为了多线程,将一些耗时的操作放到一个子线程里面去,大家应该知道如果在android的应用程序主线程,也就是UI线程里面如果执行耗时操作超过5s就会报ANR(application not respon)错误。所以,为了避免ANR,android应用程序将一些耗时操作放到一个独立的工作线程中去。
那么android是如何做到这点的呢?那个新的工作线程又是如何建立的?这就是android中Looper和Handler类的功劳。下面菜鸟来分析下首先看个类图:
从这个图看到Handler和HandlerThread类都有一个Looper成员变量。而且,Handler和Looper都有成员变量MessageQueue,下面我们看看Looper类和Handler类的作用。
Looper:
Looper类,来实现消息循环,内部有一个消息队列。看到Looper类的构造函数是private的,这样外部没法实例化Looper对象,Looper对象的构造只能通过Looper的内部接口,来看一段函数:
看看,perpare()函数中,有这样一句话 sThreadLocal.set(new Looper());其中sThreadLocal是一个ThreadLocal类型的变量。ThreadLocal表示这是一个线程的局部变量,google之看到下面这段解释。
Implements a thread-local storage, that is, a variable for which each thread has its own value. All threads share the same
made by one thread do not affect the other threads. The implementation supports
菜鸟斗胆来翻译下,ThreadLocal实现了一种线程的局部存储,ThreadLocal代表一种线程局部变量,对于这种变量来说ThreadLocal保证每个线程都有一个独立的值(value),所有线程都共有一个ThreadLocal对象,但是每个线程在访问这些变量的时候能得到不同的值,每个线程可以更改这些变量并且不会影响其他的线程。ThreadLocal支持NULL值。/*那位大牛有更好的翻译,分享下呗*/
其中ThreadLocal开放了两个接口:
public Tget():获取调用线程的局部变量
public void set(T value) :设置调用线程的局部变量
perpare()函数的这种处理方式保证在每个调用该函数的线程都有一个独立的Looper对象。
Handler:
Handler的作用是处理消息,他封装了消息的投递和对消息的处理。通过上面的图可以看到Handler类有一个Looper和MessageQueue成员变量,Handler类有4个不同的构造函数如下:
可以看到Handler类的成员变量MessageQueue mQueue都会指向Looper类的的MessageQueue,Handler为什么要这么做呢?
下面看看Handler类提供那些接口:
sendEmptyMessage
msg.target指明了这个msg将有谁来处理,在这里msg.target=this,Handler类除了封装消息添加外还封装了消息处理的接口。
Looper和Handler的联系:
本菜鸟不才不知道上面的分析有没有错误,有没有让很多初学者犯迷糊,下面我将分析以下Looper和Handler类的联系,希望可以把上面的知识点串联起来。
当调用Looper类的loop()函数的时候当前线程就进入到一个消息循环中去。看看这个loop()@Looper.java函数先.
其实这个Looper和Handler类中消息传递的机制还是很复杂到,用到了Linux中Pipe的相关知识,我太菜还不能做出更深的分析,以后有时间会好好学习下,在完善下这部份的知识。
好了,这篇blog就说到这里吧。下篇blog我会试着分析下,Handler和Looper的同步问题。届时会顺便学习一下HandlerThread类。
忘了说,如果哪位大牛发现我这篇blog有什么错误,敬请指正,免得误导了跟我一样的菜鸟
-------------------------------------------------------------------------------------------------------------------------------------------------------------
菜鸟我刚刚接触android源码的时候,当时连java语言都不太熟悉(菜鸟我一直是学C/C++),看到android某个应用的源码的时候,曾为这样的代码迷惑过。
private class MainHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case RESTART_PREVIEW: { restartPreview(); if (mJpegPictureCallbackTime != 0) { long now = System.currentTimeMillis(); mJpegCallbackFinishTime = now - mJpegPictureCallbackTime; Log.v(TAG, "mJpegCallbackFinishTime = " + mJpegCallbackFinishTime + "ms"); mJpegPictureCallbackTime = 0; } break; } ...
在同一个文件中不就是想调用restartPreview()函数吗?为什么不直接调用就好,还要发个消息然后在这个handleMessage()中来调用呢?初学菜鸟有没有同样的困惑?还是只有LZ一人太白痴了。
那么到底为什么要这么做呢。是为了异步,是为了多线程,将一些耗时的操作放到一个子线程里面去,大家应该知道如果在android的应用程序主线程,也就是UI线程里面如果执行耗时操作超过5s就会报ANR(application not respon)错误。所以,为了避免ANR,android应用程序将一些耗时操作放到一个独立的工作线程中去。
那么android是如何做到这点的呢?那个新的工作线程又是如何建立的?这就是android中Looper和Handler类的功劳。下面菜鸟来分析下首先看个类图:
从这个图看到Handler和HandlerThread类都有一个Looper成员变量。而且,Handler和Looper都有成员变量MessageQueue,下面我们看看Looper类和Handler类的作用。
Looper:
Looper类,来实现消息循环,内部有一个消息队列。看到Looper类的构造函数是private的,这样外部没法实例化Looper对象,Looper对象的构造只能通过Looper的内部接口,来看一段函数:
/** 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 final void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper()); } /** 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. * {@link #prepare()} */ public static final void prepareMainLooper() { prepare(); setMainLooper(myLooper()); if (Process.supportsProcesses()) { myLooper().mQueue.mQuitAllowed = false; } }其中函数prepareMainLooper()函数只在ActivityThread.java的main()函数中被调用过。这个prepareMainLooper()的作用保证每个调用线程都有一个Looper对象,这样应用程序的主线程就有一个Looper对象了,so应用程序主线程的消息循环建立了。
看看,perpare()函数中,有这样一句话 sThreadLocal.set(new Looper());其中sThreadLocal是一个ThreadLocal类型的变量。ThreadLocal表示这是一个线程的局部变量,google之看到下面这段解释。
Implements a thread-local storage, that is, a variable for which each thread has its own value. All threads share the same
ThreadLocalobject, but each sees a different value when accessing it, and changes
made by one thread do not affect the other threads. The implementation supports
nullvalues.
菜鸟斗胆来翻译下,ThreadLocal实现了一种线程的局部存储,ThreadLocal代表一种线程局部变量,对于这种变量来说ThreadLocal保证每个线程都有一个独立的值(value),所有线程都共有一个ThreadLocal对象,但是每个线程在访问这些变量的时候能得到不同的值,每个线程可以更改这些变量并且不会影响其他的线程。ThreadLocal支持NULL值。/*那位大牛有更好的翻译,分享下呗*/
其中ThreadLocal开放了两个接口:
public Tget():获取调用线程的局部变量
public void set(T value) :设置调用线程的局部变量
perpare()函数的这种处理方式保证在每个调用该函数的线程都有一个独立的Looper对象。
Handler:
Handler的作用是处理消息,他封装了消息的投递和对消息的处理。通过上面的图可以看到Handler类有一个Looper和MessageQueue成员变量,Handler类有4个不同的构造函数如下:
public Handler() { 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()); } } /*Handler默认的构造函数,使用当前线程(调用线程)的Looper对象给Handler成员变量mLooper赋值*/ mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } /*使用当前线程(调用线程)的消息队列给Handler的mQueue赋值*/ mQueue = mLooper.mQueue; mCallback = null; } /** * Constructor associates this handler with the queue for the * current thread and takes a callback interface in which you can handle * messages. */ public Handler(Callback callback) { 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; /*跟默认构造函数差不多,只不过带了一个callback*/ mCallback = callback; } /** * Use the provided queue instead of the default one. */ public Handler(Looper looper) { /*使用指定的Looper来给Handler类的mLooper赋值*/ mLooper = looper; mQueue = looper.mQueue; mCallback = null; } /** * Use the provided queue instead of the default one and take a callback * interface in which to handle messages. */ public Handler(Looper looper, Callback callback) { /*使用指定的Looper和CallBack来初始化Handler*/ mLooper = looper; mQueue = looper.mQueue; mCallback = callback; }
可以看到Handler类的成员变量MessageQueue mQueue都会指向Looper类的的MessageQueue,Handler为什么要这么做呢?
下面看看Handler类提供那些接口:
sendEmptyMessage
public final Message obtainMessage(int what) { return Message.obtain(this, what); } /** * 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); } } /** * Sends a Message containing only the what value. * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ public final boolean sendEmptyMessage(int what) { return sendEmptyMessageDelayed(what, 0); } /** * Remove any pending posts of messages with code 'what' that are in the * message queue. */ public final void removeMessages(int what) { mQueue.removeMessages(this, what, null, true); } /** *注意这个函数,函数将有子类实现 * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { }其中sendMessage函数都会调用到sendMessageAtTime函数,下面看下sendMessageAtTime(Message msg, long uptimeMillis)函数的实现
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { boolean sent = false; MessageQueue queue = mQueue; if (queue != null) { /*看到Message的target变量指向当前这个Handler类*/ msg.target = this; sent = queue.enqueueMessage(msg, uptimeMillis); } else { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); } return sent; }
msg.target指明了这个msg将有谁来处理,在这里msg.target=this,Handler类除了封装消息添加外还封装了消息处理的接口。
Looper和Handler的联系:
本菜鸟不才不知道上面的分析有没有错误,有没有让很多初学者犯迷糊,下面我将分析以下Looper和Handler类的联系,希望可以把上面的知识点串联起来。
当调用Looper类的loop()函数的时候当前线程就进入到一个消息循环中去。看看这个loop()@Looper.java函数先.
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static final void loop() { Looper me = myLooper(); 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(); while (true) { Message msg = queue.next(); // might block //if (!me.mRun) { // break; //} if (msg != null) { if (msg.target == null) { // No target is a magic identifier for the quit message. return; } if (me.mLogging!= null) me.mLogging.println( ">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what ); /*下面的函数将Looper类和Handler类联系起来*/ msg.target.dispatchMessage(msg); if (me.mLogging!= null) me.mLogging.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("Looper", "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(); } } }可以看到当前线程不断的从消息队列中取出消息,如果消息的target成员变量为null,就表示要退出消息循环了,否则的话就要调用这个target对象的dispatchMessage成员函数来处理这个消息,这个target对象的类型为Handler。
其实这个Looper和Handler类中消息传递的机制还是很复杂到,用到了Linux中Pipe的相关知识,我太菜还不能做出更深的分析,以后有时间会好好学习下,在完善下这部份的知识。
好了,这篇blog就说到这里吧。下篇blog我会试着分析下,Handler和Looper的同步问题。届时会顺便学习一下HandlerThread类。
忘了说,如果哪位大牛发现我这篇blog有什么错误,敬请指正,免得误导了跟我一样的菜鸟
相关文章推荐
- Android学习计划 & Android学习总结
- 移动应用瘦身学习总结 (Android&iOS)
- Java&Android学习总结(1)
- <转>二十六个月Android学习工作总结(2013-05-02更新)
- 【IOS 开发学习总结-OC-36】★文件 I/O——NSFileHandle&NSURL&NSBundle
- OC学习总结之KVC KVO 通知
- Android学习(33) -- 网络请求总结
- Android ButterKnife学习总结
- Android学习 6->浅谈界面显示与LayoutInflater
- Android Binder学习总结
- Android 学习资料总结40
- android WIFI学习总结
- [Kotlin&Anko开发Android入门学习笔记]-02Kotlin如何使用Android第三方库
- android 自定义控件学习之三 控件布局常用知识总结
- Android BroadcastReceiver学习总结
- Android之Widget学习总结
- [Android]Context泄露之谜:Handle & 内部类
- Android学习路线总结
- VC++6.0&&VS2008&MFC&API学习问题总结(二)(malloc/new/OnReceive)
- android智能指针学习总结