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

Android消息机制学习

2011-10-17 22:23 323 查看
百度面试,被问到了这个问题。其实是我挖的“坑”,但是还没有弄得很透彻。不知道面试官怎么想的了。

今天回来,好好整理了下。应该差不多了,恩,以后的“坑”。

第一篇文献:

深入剖析Android消息机制

http://www.cnblogs.com/coolszy/archive/2011/04/25/2026662.html

四个概念:

1.Message

消息对象,顾名思义就是记录消息信息的类。这个类有几个比较重要的字段:

a.arg1和arg2:我们可以使用两个字段用来存放我们需要传递的整型值,在Service中,我们可以用来存放Service的ID。

b.obj:该字段是Object类型,我们可以让该字段传递某个多项到消息的接受者中。

c.what:这个字段可以说是消息的标志,在消息处理中,我们可以根据这个字段的不同的值进行不同的处理,类似于我们在处理Button事件时,通过switch(v.getId())判断是点击了哪个按钮。

在使用Message时,我们可以通过new Message()创建一个Message实例,但是Android更推荐我们通过Message.obtain()或者Handler.obtainMessage()获取Message对象。这并不一定是直接创建一个新的实例,而是先从消息池中看有没有可用的Message实例,存在则直接取出并返回这个实例。反之如果消息池中没有可用的Message实例,则根据给定的参数new一个新Message对象。通过分析源码可得知,Android系统默认情况下在消息池中实例化10个Message对象。

2.MessageQueue

倘若我们的线程中存在Looper对象,则我们可以通过Looper.myLooper()获取,此外我们还可以通过Looper.getMainLooper()获取当前应用系统中主线程的Looper对象。在这个地方有一点需要注意,假如Looper对象位于应用程序主线程中,则Looper.myLooper()和Looper.getMainLooper()获取的是同一个对象。

3.looper

looper作用就是就是管理messagequeue。

4.Handler

消息的处理者。通过Handler对象我们可以封装Message对象,然后通过sendMessage(msg)把Message对象添加到MessageQueue中;当MessageQueue循环到该Message时,就会调用该Message对象对应的handler对象的handleMessage()方法对其进行处理。由于是在handleMessage()方法中处理消息,因此我们应该编写一个类继承自Handler,然后在handleMessage()处理我们需要的操作。

消息就是消息,处理还是在自己线程里面处理的。
消息就是可以开始了,可以结束了的信号灯。

线程之间相互发消息,就可以合作了。

第二代码:
我的项目里面,进度条的实现:

Handler handler=new Handler();
private OnClickListener playlis=new OnClickListener(){

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
handler.post(start);
//调用handler播放

}

};
Runnable start=new Runnable(){

@Override
public void run() {
// TODO Auto-generated method stub
//            mp.reset();
//            mp =MediaPlayer.create(main.this,Uri.parse

("/sdcard/test.mp3"));
mp.start();
handler.post(updatesb);
//用一个handler更新SeekBar
}

};
Runnable updatesb =new Runnable(){

@Override
public void run() {
// TODO Auto-generated method stub
sb.setProgress(mp.getCurrentPosition());
handler.postDelayed(updatesb, 1000);
//每秒钟更新一次
}

};


这里,handler不断把runnable对象post到UI线程里面,UI线程再来处理。其实,这里并没有新起一个线程。

真正的多线程

1.UI线程肯定是有的,凡是涉及到UI操作的动作,必须放在UI主线程里面去实现。因为UI线程不是安全的。

2.耗时操作不能放在UI线程里面去,因为耗时操作在UI线程中处理,会使得程序的体验性非常差劲。(你试着想象一下,你按下一个button按钮,程序就死在那里3秒钟,什么都不能做。)所以,耗时操作新起一个线程,这时候,android系统来使用多线程进行处理。

3.线程之间如何交互?这里就要使用android的消息机制了。线程A给线程B发消息,发来的消息放在线程B的messagequeue里面。

具体地可以参见下面这个问题:
http://zhidao.baidu.com/question/275665210.html?pt=monline_ik
耗时操作在新起的一个线程中去做;(此时就是多线程,线程的调度有系统来完成。)

更新UI的操作在主线程中完成,但不是耗时操作(post runnble对象到主UI线程中,等到UI线程来做;)
http://zhidao.baidu.com/question/275665210.html?pt=monline_ik
看下面这个问题就明白了:

android handler调用post方法还是阻塞

2011-6-4 16:19

提问者:贩卖孤独 | 浏览次数:284次

sureButton.setOnClickListener(new Button.OnClickListener(){

            public void onClick(View view){

                myHandler.post(new Runnable() {

                    public void run() {

                        getWeatherInfo();//耗时操作

                        analyzing();//耗时操作

                        setWeather();//更新UI

                    }

                });

            }

        });

为什么这样写还是会阻塞掉。点了sureButton按钮之后,按钮会一直处于按下状态,

直到三个操作完成,才弹出来。。。

定义一个线程。

  class MyThread extends Thread{

         Handler   mHandler;

         Boolean  boo;

         public MyThread(Handler handler){

               mHandler = handler;

         }

         public void setBoo(boolean b) {boo = b; }

         publid void run(){

           if(boo){

                  getWeatherInfo();//耗时操作

     analyzing();//耗时操作

     mHandler.post(new Runnable() {

             public void run() {

                                                    setWeather();//更新UI

                                                 }

                                        );//更新UI

                   boo = true;

              }

       }

}

在处理单击事件时:

sureButton.setOnClickListener(new Button.OnClickListener(){

            public void onClick(View view){

                                                          setBoo(true);

                      myThread.start();

            }

        });

注意这里,setWeather是更新界面的操作,所以被放进了runnbale对象,然后被handler.post

到了UI线程,注意,由UI线程处理。

耗时的getWeatherInfo()和analyzing()放在了另外一个线程中处理。这样,就不会出现ANR

(application not responsible)的问题了。

UI线程是不安全的,意味着如果要更新UI,必须在UI线程中操作。

其实进度条的刷新显示并没有多线程,而是不断地延时1s将runnable对象通过handler调入主

线程的messagequeue中。按照下面的说法就是:
http://yzw2007.iteye.com/blog/724641
这是android提供的一种机制,handler对象将通过post方法,将里面的Runnable对象放到UI执行队列中,UI消费这个队列,调用Runnable的run方法。这里并不生成新的线程。项目中其实用到就是每间1s中将runnable对象扔进主线程里面,让主线程去做这件事情(更新界面:setProgress())。至于消息机制,还暂时没有用到:
发送消息实际上就是相当于发一个通知一样,只是通知作用。只有收到相应的通知,才会做自己的事情。

5.最后一个例子:

模拟下载文件(一个耗时操作),下载完毕后,通知主线程。

http://android.yaohuiji.com/archives/770
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息