您的位置:首页 > 移动开发 > Android开发

Android Loop&Handle学习总结

2012-01-08 00:21 309 查看
       昨晚偷懒,这篇博客只写了一个标题,今天早晨一看,还有15的阅读量。实在是对不起那些同学.......换了是我,也会BS这样的LZ吧!sorry 啦

-------------------------------------------------------------------------------------------------------------------------------------------------------------

       菜鸟我刚刚接触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
ThreadLocal
object, but each sees a different value when accessing it, and changes
made by one thread do not affect the other threads. The implementation supports
null
values.

       菜鸟斗胆来翻译下,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有什么错误,敬请指正,免得误导了跟我一样的菜鸟
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息