Android中的Handler信息传递机制

在介绍Handler之前有必要介绍一下:主线程(UI线程)

当应用启动,系统会创建一个主线程(mainthread。这个主线程负责向UI组件分发事件(包括绘制事件),也是在这个主线程里,你的应用和Android的UI组件(components from the android.widgetand android.viewpackages)发生交互。所以main thread也叫UI thread也即UI线程

系统不会为每个组件单独创建线程,在同一个进程里的UI组件都会在UI线程里实例化,系统对每一个组件的调用都从UI线程分发出去

  结果就是,响应系统回调的方法(比如响应用户动作的onKeyDown()和各种生命周期回调)永远都是在UI线程里运行。

 当App做一些比较重(intensive)的工作的时候,除非你合理地实现,否则单线程模型的performance会很poor。

  特别的是,如果所有的工作都在UI线程,做一些比较耗时的工作比如访问网络或者数据库查询,都会阻塞UI线程导致事件停止分发(包括绘制事件)。对于用户来说,应用看起来像是卡住了,更坏的情况是,如果UI线程blocked的时间太长(大约超过5秒),用户就会看到ANRapplication not responding)的对话框。

  另外,Andoid UI toolkit并不是线程安全的,所以你不能从非UI线程来操纵UI组件。你必须把所有的UI操作放在UI线程里,所以Android的单线程模型有两条原则:

  1.不要阻塞UI线程。

  2.不要在UI线程之外访问Android UI toolkit(主要是这两个包中的组件:android.widget and android.view)。


所以注意!!!

只有在UI线程中的对象才能操作UI线程中的对象,为了将非UI线程中的数据传送到UI线程,可以使用一个 Handler运行在UI线程中。

用一副图来简单表示上述:



正题开始Handler机制是怎么样的呢?


说到Handler就必须说到他的兄弟们:

Message,MessageQueue,Looper





Handler:作用是发送与处理信息,如果希望Handler正常工作,在当前线程中要有一个Looper对象

Looper:每个线程只能够有一个Looper,管理MessageQueue,不断地从中取出Message分发给对应的Handler处理。

Looper扮演着一个Handler和消息队列之间通讯桥梁的角色。程序组件首先通过Handler把消息传递给Looper,Looper把消息放入 队列。Looper也把消息队列里的消息广播给所有的Handler,Handler接受到消息后调用handleMessage进行处理。

Message:Handler接收与处理的消息对象

MessageQueue:消息队列,先进先出管理Message,在初始化Looper对象时会创建一个与之关联的MessageQueue;Message Queue是一个消息队列,用来存放通过Handler发布的消息。Android在第一次启动程序时会默认会为UI thread创建一个关联的消息队列,可以通过Looper.myQueue()得到当前线程的消息队列,用来管理程序的一些上层组件,activities,broadcast receivers 等等。


简单点说:

当我们的子线程想修改Activity中的UI组件时,我们可以新建一个Handler对象,通过这个对象向主线程发送消息,而我们发送的信息会先到主线程的MessageQueue进行等待,由Looper按先入先出顺序取出,再根据message对象的what属性分发给对应的Handler进行处理!



Handler的相关方法:

·                        void handleMessage(Messagemsg):处理消息的方法,通常是用于被重写!

·                        sendEmptyMessage(int what):发送空消息

·                        sendEmptyMessageDelayed(int what,longdelayMillis):指定延时多少毫秒后发送空信息

·                        sendMessage(Message msg):立即发送信息

·                        sendMessageDelayed(Message msglong delayMillis):指定延时多少毫秒后发送信息       注意:延迟的消息,不是延迟发送,而是延迟处理

·                        final boolean hasMessage(intwhat):检查消息队列中是否包含what属性为指定值的消息如果是参数为(int what,Object object):除了判断what属性,还需要判断Object属性是否为指定对象的消息

                          移除消息
               移除指定what标识的消息:removeMessages(int what)
               移除所有未执行的消息:  removeCallbacksAndMessages(null)


Handler的使用示例:

1Handler写在主线程中

在主线程中,因为系统已经初始化了一个Looper对象,所以我们直接创建Handler对象,就可以进行信息的发送与处理了!



01


public class HandlerTestActivity extends Activity {
02     private TextView tv;
03     private static final int UPDATE = 0;
04     private Handler handler = new Handler() {
05
06         @Override
07         public void handleMessage(Message msg) {
08             // TODO 接收消息并且去更新UI线程上的控件内容
09             if (msg.what == UPDATE) {
10                 // Bundle b = msg.getData();
11                 // tv.setText(b.getString("num"));
12                 tv.setText(String.valueOf(msg.obj));
13             }
14             super.handleMessage(msg);
15         }
16     };
17
18     /** Called when the activity is first created. */
19     @Override
20     public void onCreate(Bundle savedInstanceState) {
21         super.onCreate(savedInstanceState);
22         setContentView(R.layout.main);
23         tv = (TextView) findViewById(R.id.tv);
24
25         new Thread() {
26             @Override
27             public void run() {
28                 // TODO 子线程中通过handler发送消息给handler接收,由handler去更新TextView的值
29                 try {
30                     for (int i = 0; i < 100; i++) {
31                         Thread.sleep(500);
32                         Message msg = new Message();
33                         msg.what = UPDATE;
34                         // Bundle b = new Bundle();
35                         // b.putString("num", "更新后的值:" + i);
36                         // msg.setData(b);
37                         msg.obj = "更新后的值:" + i;
38                         handler.sendMessage(msg);
39                     }
40                 catch (InterruptedException e) {
41                     e.printStackTrace();
42                 }
43             }
44         }.start();
45     }
46
47 }


2Handler写在子线程中

如果是Handler写在了子线程中的话,我们就需要自己创建一个Looper对象了!创建的流程如下:

1 )直接调用Looper.prepare()方法即可为当前线程创建Looper对象,而它的构造器会创建配套的MessageQueue; 2 )创建Handler对象,重写handleMessage( )方法就可以处理来自于其他线程的信息了3 )调用Looper.loop()方法启动Looper

使用示例:输入一个数,计算后通过Toast输出在这个范围内的所有质数

实现代码: main.xml

<LinearLayout  
    xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:orientation="vertical">  
    <EditText  
        android:id="@+id/etNum"  
        android:inputType="number"  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        android:hint="请输入上限"/>  
    <Button  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        android:onClick="cal"  
        android:text="计算"/>    
</LinearLayout>  

MainActivity.java:

publicclassCalPrimeextendsActivity  
{  
    staticfinalString UPPER_NUM ="upper";  
    EditText etNum;  
    CalThread calThread;  
    // 定义一个线程类  
    classCalThreadextendsThread  
    {  
        publicHandler mHandler;  
  
        publicvoid run()  
        {  
            Looper.prepare();  
            mHandler =newHandler()  
            {  
                // 定义处理消息的方法  
                @Override  
                publicvoid handleMessage(Message msg)  
                {  
                    if(msg.what ==0x123)  
                    {  
                        int upper = msg.getData().getInt(UPPER_NUM);  
                        List<Integer> nums =newArrayList<Integer>();  
                        // 计算从2开始、到upper的所有质数  
                        outer:  
                        for(int i =2; i <= upper ; i++)  
                        {  
                            // i处于从2开始、到i的平方根的所有数  
                            for(int j =2; j <=Math.sqrt(i); j++)  
                            {  
                                // 如果可以整除,表明这个数不是质数  
                                if(i !=2&& i % j ==0)  
                                {  
                                    continue outer;  
                                }  
                            }  
                            nums.add(i);  
                        }  
                        // 使用Toast显示统计出来的所有质数  
                        Toast.makeText(CalPrime.this, nums.toString()  
                            ,Toast.LENGTH_LONG).show();  
                    }  
                }  
            };  
            Looper.loop();  
        }  
    }  
    @Override  
    publicvoid onCreate(Bundle savedInstanceState)  
    {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        etNum =(EditText)findViewById(R.id.etNum);  
        calThread =newCalThread();  
        // 启动新线程  
        calThread.start();  
    }  
    // 为按钮的点击事件提供事件处理函数  
    publicvoid cal(View source)  
    {  
        // 创建消息  
        Message msg =newMessage();  
        msg.what =0x123;  
        Bundle bundle =newBundle();  
        bundle.putInt(UPPER_NUM ,  
            Integer.parseInt(etNum.getText().toString()));  
        msg.setData(bundle);  
        // 向新线程中的Handler发送消息  
        calThread.mHandler.sendMessage(msg);  
    }  
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值