Looper、Handler和Thread的关系

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放在主线程中去执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值