论坛首页 移动开发技术论坛

android中Handler,Looper,Message的疑问

浏览 9959 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-08-04   最后修改:2011-08-04

 

最近做的项目中涉及到线程间通信,我先把我的项目说下:

    Activity启动后点击一个界面按钮后会开启一个服务(暂定为padService),在padService中会启动一个线程(暂定为Thread-3)发起Socket连接。我们项目中使用mina作为socket通信框架,用过mina的同志们应该熟悉,Thread-3只是负责监听,具体的消息处理是另外的线程。在我们的IoHandler中处理消息,现在的问题是,我需要在IoHander的sessionOpened方法中给Activity一个消息去更新UI界面,这个就涉及到不同线程间的通信了。

   网上搜索后,在android中线程间通信使用Handler,Looper,Message这几个对象(不熟悉这些概念的同志们请自己查下)。

   这是网上的一个使用例子:

 

public class Activity2 extends Activity implements OnClickListener{

       Button button = null;

       TextView text = null;

       MyHandler mHandler = null;

       Thread thread ;

       @Override

       protected void onCreate(Bundle savedInstanceState) {

              super.onCreate(savedInstanceState);

              setContentView(R.layout.activity1);         

              button = (Button)findViewById(R.id.btn);

              button.setOnClickListener(this);

              text = (TextView)findViewById(R.id.content);

       }

       public void onClick(View v) {

              switch (v.getId()) {

              case R.id.btn:

                     thread = new MyThread();

                     thread.start();

                     break;

              }             

       }      

       private class MyHandler extends Handler{              

              public MyHandler(Looper looper){

                     super(looper);

              }

              @Override

              public void handleMessage(Message msg) {//处理消息

                     text.setText(msg.obj.toString());

              }             

       }

       private class MyThread extends Thread{

              @Override

              public void run() {

                     Looper curLooper = Looper.myLooper();

                     Looper mainLooper = Looper.getMainLooper();

                     String msg ;

                     if(curLooper==null){

                            mHandler = new MyHandler(mainLooper);

                            msg = "curLooper is null";

                     }else{

                            mHandler = new MyHandler(curLooper);

                            msg = "This is curLooper";

                     }

                     mHandler.removeMessages(0);

                     Message m = mHandler.obtainMessage(1, 1, 1, msg);

                     mHandler.sendMessage(m);

              }             

       }

}

 

 

这个没有问题,基本上三个对象的使用也很清楚,myHandler虽然是由子线程new出来的,但主线程持有引用,在我们的项目中不能用,因为我们几个线程属于不同的类,我尝试用下面的方法解决:

 

    在IoHandler中new一个android的handler,参数为主线程的Looper:

 

 

new Handler(Looper.getMainLooper()).sendMessage(msg);

IoHandler所在的线程给主线程发送消息(looper是主线程的,消息也就放在主线程的消息队列里了)

但是在主线程的handleMessage方法中得不到消息,尝试失败。

 

    那么怎么办呢,让IoHandler持有主线程的handler引用,具体做法有两种方式:

    1.  参数传递,把主线程的handler通过参数传递的形式传到IoHandler中。

    2. 静态变量,把主线程的handler申明为公共静态变量

 

public static Handler mainHandler;

 这样在Iohandler中使用

 

welcomeActivity.mainHandler.sendMessage(msg);

 这两种方式在主线程的  handleMessage的方法中都可以得到IoHandler发送的消息。

 本人使用的是静态变量解决的,因为有好几个来实现通信,参数传递太麻烦。

 

 那为什么我的第一种尝试是失败的呢,我是把消息放到主线程的消息队列了啊,这就要看android的一些实现机制了。

 通过网络和android的api,本人的理解如下:

 Looper是MessageQueue和Handler沟通的桥梁,Handler通过Looper把消息放入消息队列(MessageQueue),你想把消息发给谁,就把谁的looper作为参数传给Handler

 

newHandler(Looper looper);

  Looper把消息放入消息队列,并广播消息,这个不太好理解,我举例如下:

 

   主线程的Handler我们这样定义:Handler mainHandler = new Handler();  如果Handler没有参数,默认为当前线程的Looper

   子线程的Handler我们这样定义: Handler subHandler = newHandler(Looper.getMainLooper()); 参数为主线程的Looper

 这样两个线程都会把消息放入主线程的消息队列里了。

   现在mainHandler.sendMessage(), 消息进入主线程的消息队列,Looper广播消息,其实就是调用mainHandler的dispatchMessage方法,所有持有mianHandler引用的类都可以收到消息,注意啊,现在subHandler并不能接受到消息,因为Looper并没有调用subHandler的dispatchMessage方法,所以应该这样理解广播,A发送消息,那么A的Looper就调用A的dispatchMessage方法,别的B,C, D虽然也是A的Looper,但没有A的引用,所以B,C,D是接受不到消息的,如果B, C,D持有A的引用,但B,C,D不用A的Looper,那么也是接受不到消息的。这点在开发时要特别注意。

 

  以上是我在使用Looper, Handler ,Message中的一些问题,可能有理解错的地方,请大大们指出来。

  我的疑惑是难道子线程必须持有主线程的引用才可以给主线程发送消息吗?要知道我们的子线程并不一定和主线程一个类,可能在别的类中,这个引用传递实在太麻烦了,期望有更好的解决方式。

   发表时间:2011-08-08  
<span style="font-size: medium;">new Handler(Looper.getMainLooper()).sendMessage(msg);</span>
我觉得这样你又创建了一个handler,跟前面声明的handler不一样,这个handler只能处理它里面handleMessage方法里面的语句。
0 请登录后投票
   发表时间:2011-08-08  
是滴,这个问题我文章里面已经解释的很清楚了,那个Handler发送消息,那么Handler所属的Looper就广播那个Handler的消息,所以只有那个Handler才能在HandlerMessage中收到消息,我现在的问题就是如果Handler采用参数传递太麻烦,很多类的方法都得改。如果是静态变量,一定要保证Handler实例化,因为我们的handler要操作UI,所以都是界面的内部类,只能在界面中实例化。
0 请登录后投票
   发表时间:2011-08-08  
Thread thread = new Thread(handler)
这样很麻烦吗?
0 请登录后投票
   发表时间:2011-08-08  
zhouYunan2010 写道
Thread thread = new Thread(handler)
这样很麻烦吗?

你这个肯定是不麻烦,但如果这个线程不是你自己new的呢,比如说mina的消息收发线程。而且你这个是一个类中完成,要是代码分散在几个类中,那么这样是不是就麻烦了。
0 请登录后投票
   发表时间:2011-08-08  
Handler处理消息对象是与Message绑定的。
是哪个handler对象发送的消息。此handler对象就被存储到message的target属性当中,
与message绑定到一起,并存储到looper对象的MessageQueue。当Looper对象执行loop方法时。死循环遍历MessageQueue,即有消息存在就发送出来,并由此message绑定的handler进行处理。调用handler的dispatchMessage方法,然后调用到handleMessage方法。
这是我的理解。

0 请登录后投票
   发表时间:2011-08-09  
是滴,这样对这几个对象的关系就更深入了,消息都在MessageQueue中,Looper循环遍历时,只能通过Message的Target属性得到是那个Handler发送的消息,就调用那个Handler的dispatchMessage进行消息的广播,这样Handler的handlerMessage才能收到消息。有一点要注意,如果你不知道怎么分发消息,那么你的Handler类就不要覆写dispatchMessage方法,哪怕只是打印下,如果你是在要打印看看过程,那么覆写时调下super(),否则消息可发送不出去滴,你滴handlerMessage就收不到消息了
0 请登录后投票
   发表时间:2011-08-16  
handler都得自己继承之后重写  handleMessage()来用
然后在handleMessage中加入处理逻辑。
直接new Handler实例是没有用的。
如果要想系统的处理UI事件的handler处理消息,要拿到那个handler,再之上调用 sendMessage才行。

0 请登录后投票
   发表时间:2012-03-24   最后修改:2012-03-24
经测试,单独分成两份java文件,在子线程的java文件中,这样生成handler也是可以更新UI线程界面的:
Handler subHandler = new Handler(Looper.getMainLooper())
        {
        	public void handleMessage(Message msg) { 
	        	  Log.d("System.out", "MyThread:: handler所在线程的id:"
	  					+ Thread.currentThread().getId());
	        	  HandlerTest5.mybutton1.setText("new text");
        	}
        };

此处可以看出来打印出来的线程ID就是UI线程的ID,并且更新UI成功!
测试环境:android2.2+eclipse
0 请登录后投票
论坛首页 移动开发技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics