在 android 中,不可以在子线程中更新 UI 的操作,否则会报错或者异常信息。
在这种情况下,我们会使用 Handler(在 UI 线程创建该对象) 接收子线程的消息更新 UI.
可以看出,这是子线程通知主线程,而主线程没有直接通知子线程,那麽我们如何做到这一点?这样有什么好处?
好处,很明显。主线程中如果有些耗时的操作,我们可以让子线程去做,这时,主线程就可以通知子线程了!
实现这种操作的方式有很多,今天主要想说说 HandlerThread 与 Handler 的配合使用方法。
在主线程中,可以创建 HandlerThread 对象:
- HandlerThreadmHandlerThread=newHandlerThread("sub_thread");
- mHandlerThread.start();
这样,就开启了一个子线程,该线程的名字就是 sub_thread.
然后,我们实现一个继承自 Handler 的 MyHandler 类:
- classMyHandlerextendsHandler{
- publicMyHandler(){
- super();
- }
- publicMyHandler(Looperlooper){
- super(looper);
- }
- @Override
- publicvoidhandleMessage(Messagemsg){
- //super.handleMessage(msg);
- if(msg.what==MSG_WHAT){
- Bundlebundle=msg.getData();
- Stringinfo=bundle.getString("main");
- Log.d("mark","我接受任务:"+info);
- }
- }
- }
最后,使用 Message 来传递消息,MyHandler 会根据情况处理 Message.
完整代码:
- packagemark.zhang;
- importandroid.app.Activity;
- importandroid.os.Bundle;
- importandroid.os.Handler;
- importandroid.os.HandlerThread;
- importandroid.os.Looper;
- importandroid.os.Message;
- importandroid.util.Log;
- publicclassHandlerThreadActivityextendsActivity{
- privatestaticfinalintMSG_WHAT=1;
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- //创建对象,并启动该线程
- HandlerThreadmHandlerThread=newHandlerThread("sub_thread");
- mHandlerThread.start();
- //获取Looper对象
- MyHandlermHandler=newMyHandler(mHandlerThread.getLooper());
- //创建Bundle对象,传递数据
- Bundlebundle=newBundle();
- bundle.putString("main","我这边事情太多,麻烦你帮忙处理一下!");
- //发送消息
- Messagemsg=newMessage();
- msg.what=MSG_WHAT;
- msg.setData(bundle);
- msg.setTarget(mHandler);
- msg.sendToTarget();
- Log.d("mark","UI----"+"threadName:"+Thread.currentThread().getName()+",threadId:"+Thread.currentThread().getId());
- }
- /**
- *该Handler调用handleMessage方法运行在子线程
- *
- *@authormark
- */
- classMyHandlerextendsHandler{
- publicMyHandler(){
- super();
- }
- /*这个构造方法必须有*/
- publicMyHandler(Looperlooper){
- super(looper);
- }
- @Override
- publicvoidhandleMessage(Messagemsg){
- if(msg.what==MSG_WHAT){
- Bundlebundle=msg.getData();
- //接收消息
- Stringinfo=bundle.getString("main");
- Log.d("mark","handleMessage---"+"threadName:"+Thread.currentThread().getName()+",threadId:"+Thread.currentThread().getId());
- Log.d("mark","我接受任务:"+info);
- }
- }
- }
- }
这里,我们思考一个问题,代码:
- //获取Looper对象
- MyHandlermHandler=newMyHandler(mHandlerThread.getLooper());
获取 Looper 对象,使用的是 HandlerThread 对象的 getLooper 对象。为什么不直接这样:
- //获取Looper对象
- MyHandlermHandler=newMyHandler(getMainLooper());
如果这样做的话,打印信息如下:
- D/mark(21852):UI----threadName:main,threadId:1
- D/mark(21852):handleMessage---threadName:main,threadId:1
- D/mark(21852):我接受任务:我这边事情太多,麻烦你帮忙处理一下!
可见,此时的 handleMessage 是在主线程中运行的。每个主线程默认有一个 Looper,也就是说在主线程中,下面代码是等效的:
- MyHandlermHandler=newMyHandler(getMainLooper());
- MyHandlermHandler=newMyHandler();
但是子线程需要手动获取如:
- MyHandlermHandler=newMyHandler(mHandlerThread.getLooper());
实现主线程、子线程双向通信可以在各自的线程中分别是用 Handler 来传递信息和处理消息。