您的位置:首页 > 运维架构

Looper、Handler和Thread的关系

2014-12-30 18:45 399 查看
1.如何为Thread创建消息循环

Threads by default do not have a message loop associated with them

Thread默认是没有绑定消息循环的,主线程是个例外,稍后讨论主线程。

to create one, call {@link #prepare} in the thread that is to run the loop,

and then {@link #loop} to have it process messages until the loop is stopped.

创建消息循环的第一步是new Looper(),但是由于只有一个构造方法且是private,所以应调用prepare()来new出来

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}

    private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }
sThreadLocal中保存了Looper的实例,能保证prepare在同一个Thread中只能被调用一次。

Looper的构造方法也值得一看:

它需要创建一个消息队列,同时需要保存当前Thread到成员变量mThread中。

所以,Looper、MessageQueue和Thread是一对一的关系:

一个Thread最多只能有一个Looper实例(也可以没有,主线程默认有一个,非主线程默认没有);

当你调用Looper.prepare()时,它就会创建一个Looper实例,同时与当前Thread进行绑定。

一个Looper有且只有一个MessageQueue。

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

public Thread getThread() {
return mThread;
}

public static MessageQueue myQueue() {
return myLooper().mQueue;
}

/** @hide */
public MessageQueue getQueue() {
return mQueue;
}


创建消息循环的第二步是调用loop()方法使Thread进入消息循环。

在一个while循环中,不断地从消息队列中取出Message,交给此Message绑定的Hanler却分发处理。

这个循环退出只有一种方式:某个Message的target为空。

这只有调用quit方法时才会发生,其它情况下,每个Message都会与某个Handler实例绑定。

public static void loop() {
Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
MessageQueue queue = me.mQueue;

while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}

msg.target.dispatchMessage(msg);
msg.recycle();
}
}
}

    public void quit() {
        Message msg = Message.obtain();
        // NOTE: By enqueueing directly into the message queue, the
        // message is left with a null target.  This is how we know it is
        // a quit message.
        mQueue.enqueueMessage(msg, 0);
    }


下面是一个很好的示例代码:

*  class LooperThread extends Thread {
*      public Handler mHandler;
*
*      public void run() {
*          Looper.prepare();
*
*          mHandler = new Handler() {
*              public void handleMessage(Message msg) {
*                  // process incoming messages here
*              }
*          };
*
*          Looper.loop();
*      }
*  }


2.消息的处理

msg.target.dispatchMessage(msg);

每个Message的target都指向一个Handler实例,所以处理消息是有Handler的dispatchMessage方法来处理

public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
当调用Hanlder的postXXX方法时,都会传递过来一个Runnable对象,这个Runnable对象被封装为callback,在消息队列循环轮到此消息后,handleCallback直接调用此Runnable的run方法。

mCallback是在Hanlder的构造方法中传递进来的,很少见到如此使用它。

最后则是每个Handler实例都要实现的handleMessage方法。

其实,某个Message最终是在哪个线程中被处理,与这个Message中target所指向的Handler有关,进一步,是与这个Handler绑定的Looper有关,更进一步,是这个Looper绑定的Thread。

看Handler的构造方法:

public Handler() {
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 = null;
}

    public Handler(Looper looper) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = null;
    }


这里主要关注与之绑定的Looper,无参数的构造方法中,会通过Looper.myLooper()方法拿到Looper实例,这个之前提到过,它会返回当前线程的Looper(同样地,这个线程应该有Looper,所以要么是主线程,则一定会有;要么是其它线程,那么一定要调用Looper.prepare()之后了,否则会有异常抛出);另一种则是在构造Handler时传入Looper,因此这种情形下,此Looper绑定哪个线程,则handlerMessage则在哪个线程中执行。

看个简单例子:

public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

HandlerThread ht = new HandlerThread("thread_test");
ht.start();

Handler handler = new Handler(ht.getLooper()){
public void handleMessage(Message msg) {
System.out.println("Handler.handleMessage: getId = " + Thread.currentThread().getId());
System.out.println("Handler.handleMessage: getName = " + Thread.currentThread().getName());
}
};

System.out.println("Activity.onCreate: getId = " + Thread.currentThread().getId());
System.out.println("Activity.onCreate: getName = " + Thread.currentThread().getName());

handler.sendEmptyMessage(0);
}
}

I/System.out( 2737): Activity.onCreate: getId = 1
I/System.out( 2737): Activity.onCreate: getName = main
I/System.out( 2737): Handler.handleMessage: getId = 174
I/System.out( 2737): Handler.handleMessage: getName = thread_test
通过运行程序后输出的log可以确定,handleMessage方法是在thread_test这个新线程中执行的,而不是在主线程中,UI线程name为main。

也不难猜测,如果只是在主线程中通过new Handler方法,那么其实还是在主线程中执行handleMessage。

所以,如果希望通过Handler执行某些耗时工作的话,一定要新创建一个线程,在新线程中构造消息循环,然后把Looper对象交给Handler。不这样的话,始终还是会在主线程执行耗时操作。

当有此类需求时,HandlerThread是个不错的类,可以帮我们更容易实现。使用方法如上。

系统服务PackageManagerService就是这么处理的。

3.关于主线程

每个应用程序都运行在单独的进程中,进程名称就是包名。每个应用程序都有一个ActivityThread实例,它就运行在该进程的主线程中。

我们既然说主线程默认就有一个消息循环,那么就看它是如何实现的,有和不同:

public static void main(String[] args) {
Process.setArgV0("<pre-initialized>");

Looper.prepareMainLooper();

if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}

ActivityThread thread = new ActivityThread();
thread.attach(false);

Looper.loop();

throw new RuntimeException("Main thread loop unexpectedly exited");
}
其实与我们之前提到的没多大差异:Looper.prepareMainLooper();   然后是Looper.loop();

所以,主线程之所以有消息循环,是因为系统已为每个应用程序创建了消息循环。但它的特别在于调用的是prepareMainLooper而非prepare。

public static void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
myLooper().mQueue.mQuitAllowed = false;
}

private static Looper mMainLooper = null;  // guarded by Looper.class
private synchronized static void setMainLooper(Looper looper) {
    mMainLooper = looper;
}
public synchronized static Looper getMainLooper() {
    return mMainLooper;
}
可以看到它与prepare相比,只是多了两步:一是把当前创建的Looper保存到成员变量mMainLooper中,二是设置对应的消息队列不允许quit。

public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

System.out.println("Activity.onCreate: getId = " + Thread.currentThread().getId());
System.out.println("Activity.onCreate: getName = " + Thread.currentThread().getName());

new Thread() {
public void run() {

System.out.println("new Thread: getId = " + Thread.currentThread().getId());
System.out.println("new Thread: getName = " + Thread.currentThread().getName());

Looper.prepare();
Handler mHandler = new Handler() {
//Handler mHandler = new Handler(Looper.getMainLooper()) {
public void handleMessage(Message msg) {
System.out.println("new Thread handleMessage: getId = " + Thread.currentThread().getId());
System.out.println("new Thread handleMessage: getName = " + Thread.currentThread().getName());
}
};

mHandler.sendEmptyMessage(0);
Looper.loop();
}
}.start();
}
}

Handler mHandler = new Handler(Looper.getMainLooper())

I/System.out( 3162): Activity.onCreate: getId = 1

I/System.out( 3162): Activity.onCreate: getName = main

I/System.out( 3162): new Thread: getId = 204

I/System.out( 3162): new Thread: getName = Thread-204

I/System.out( 3162): new Thread handleMessage: getId = 1

I/System.out( 3162): new Thread handleMessage: getName = main

Handler mHandler = new Handler()

I/System.out( 3265): Activity.onCreate: getId = 1

I/System.out( 3265): Activity.onCreate: getName = main

I/System.out( 3265): new Thread: getId = 210

I/System.out( 3265): new Thread: getName = Thread-210

I/System.out( 3265): new Thread handleMessage: getId = 210

I/System.out( 3265): new Thread handleMessage: getName = Thread-210

*/



从这个例子可以看出,新线程中的handleMessage是在新线程中执行,我们可以通过getMainLooper方法,把handleMessage放在主线程中去执行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: