zoukankan      html  css  js  c++  java
  • Android--多线程之Handler

    转载,感谢http://www.cnblogs.com/plokmju/p/android_handler.html

    前言

      Android的消息传递机制是另外一种形式的“事件处理”,这种机制主要是为了解决Android应用中多线程的问题,在Android中不允许Activity新启动的线程访问该Activity里的UI组件,这样会导致新启动的线程无法改变UI组件的属性值。但实际开发中,很多地方需要在工作线程中改变UI组件的属性值,比如下载网络图片、动画等等。本篇博客主要介绍Handler是如何发送与处理线程上传递来的消息,并讲解Message的几种传递数据的方式,最后均会以小Demo来演示。

    Handler

      Handler,它直接继承自Object,一个Handler允许发送和处理Message或者Runnable对象,并且会关联到主线程的MessageQueue中。每个Handler具有一个单独的线程,并且关联到一个消息队列的线程,就是说一个Handler有一个固有的消息队列。当实例化一个Handler的时候,它就承载在一个线程和消息队列的线程,这个Handler可以把Message或Runnable压入到消息队列,并且从消息队列中取出Message或Runnable,进而操作它们。

      Handler主要有两个作用:

    • 在工作线程中发送消息。
    • 在UI线程中获取、处理消息。

      上面介绍到Handler可以把一个Message对象或者Runnable对象压入到消息队列中,进而在UI线程中获取Message或者执行Runnable对象,所以Handler把压入消息队列有两大体系,Post和sendMessage:

    • Post:Post允许把一个Runnable对象入队到消息队列中。它的方法有:post(Runnable)、postAtTime(Runnable,long)、postDelayed(Runnable,long)。
    • sendMessage:sendMessage允许把一个包含消息数据的Message对象压入到消息队列中。它的方法有:sendEmptyMessage(int)、sendMessage(Message)、sendMessageAtTime(Message,long)、sendMessageDelayed(Message,long)。

      从上面的各种方法可以看出,不管是post还是sendMessage都具有多种方法,它们可以设定Runnable对象和Message对象被入队到消息队列中,是立即执行还是延迟执行。

      

    Post

      对于Handler的Post方式来说,它会传递一个Runnable对象到消息队列中,在这个Runnable对象中,重写run()方法。一般在这个run()方法中写入需要在UI线程上的操作。

      在Handler中,关于Post方式的方法有:

    • boolean post(Runnable r):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,立即执行。
    • boolean postAtTime(Runnable r,long uptimeMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,在特定的时间执行。
    • boolean postDelayed(Runnable r,long delayMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,延迟delayMills秒执行
    • void removeCallbacks(Runnable r):从消息队列中移除一个Runnable对象。

       下面通过一个Demo,讲解如何通过Handler的post方式在新启动的线程中修改UI组件的属性:

    复制代码
     1 package com.bgxt.datatimepickerdemo;
     2 
     3 import android.app.Activity;
     4 import android.os.Bundle;
     5 import android.os.Handler;
     6 import android.view.View;
     7 import android.widget.Button;
     8 import android.widget.TextView;
     9 
    10 public class HandlerPostActivity1 extends Activity {
    11     private Button btnMes1,btnMes2;
    12     private TextView tvMessage;
    13     // 声明一个Handler对象
    14     private static Handler handler=new Handler();
    15     
    16     @Override
    17     protected void onCreate(Bundle savedInstanceState) {
    18         super.onCreate(savedInstanceState);
    19         setContentView(R.layout.message_activity);        
    20         
    21         btnMes1=(Button)findViewById(R.id.btnMes1);
    22         btnMes2=(Button)findViewById(R.id.btnMes2);
    23         tvMessage=(TextView)findViewById(R.id.tvMessage);
    24         btnMes1.setOnClickListener(new View.OnClickListener() {
    25             
    26             @Override
    27             public void onClick(View v) {
    28                 // 新启动一个子线程
    29                 new Thread(new Runnable() {                    
    30                     @Override
    31                     public void run() {
    32                         // tvMessage.setText("...");
    33                         // 以上操作会报错,无法再子线程中访问UI组件,UI组件的属性必须在UI线程中访问
    34                         // 使用post方式修改UI组件tvMessage的Text属性
    35                         handler.post(new Runnable() {                    
    36                             @Override
    37                             public void run() {
    38                                 tvMessage.setText("使用Handler.post在工作线程中发送一段执行到消息队列中,在主线程中执行。");                        
    39                             }
    40                         });                                
    41                     }
    42                 }).start();
    43             }
    44         });
    45         
    46         btnMes2.setOnClickListener(new View.OnClickListener() {
    47             
    48             @Override
    49             public void onClick(View v) {
    50                 new Thread(new Runnable() {                    
    51                     @Override
    52                     public void run() {
    53                         // 使用postDelayed方式修改UI组件tvMessage的Text属性值
    54                         // 并且延迟3S执行
    55                         handler.postDelayed(new Runnable() {
    56                             
    57                             @Override
    58                             public void run() {
    59                                 tvMessage.setText("使用Handler.postDelayed在工作线程中发送一段执行到消息队列中,在主线程中延迟3S执行。");    
    60                                 
    61                             }
    62                         }, 3000);                        
    63                     }
    64                 }).start();
    65                 
    66             }
    67         });
    68     }
    69 }
    复制代码

      效果展示:

      有一点值得注意的是,对于Post方式而言,它其中Runnable对象的run()方法的代码,均执行在UI线程上,所以对于这段代码而言,不能执行在UI线程上的操作,一样无法使用post方式执行,比如说访问网络,下面提供一个例子,使用post方式从互联网上获取一张图片,并且显示在ImageView中。

    复制代码
     1 package com.bgxt.datatimepickerdemo;
     2 
     3 import org.apache.http.HttpResponse;
     4 import org.apache.http.client.HttpClient;
     5 import org.apache.http.client.methods.HttpGet;
     6 import org.apache.http.impl.client.DefaultHttpClient;
     7 import org.apache.http.util.EntityUtils;
     8 
     9 import android.app.Activity;
    10 import android.app.ProgressDialog;
    11 import android.graphics.Bitmap;
    12 import android.graphics.BitmapFactory;
    13 import android.os.Bundle;
    14 import android.os.Handler;
    15 import android.view.View;
    16 import android.widget.Button;
    17 import android.widget.ImageView;
    18 
    19 public class HandlerPostActivity2 extends Activity {
    20     private Button btnDown;
    21     private ImageView ivImage;
    22     private static String image_path = "http://ww4.sinaimg.cn/bmiddle/786013a5jw1e7akotp4bcj20c80i3aao.jpg";
    23     private ProgressDialog dialog;
    24     // 一个静态的Handler,Handler建议声明为静态的
    25     private static  Handler handler=new Handler();
    26     @Override
    27     protected void onCreate(Bundle savedInstanceState) {
    28         super.onCreate(savedInstanceState);
    29         setContentView(R.layout.asynctask_activity);
    30         
    31         btnDown = (Button) findViewById(R.id.btnDown);
    32         ivImage = (ImageView) findViewById(R.id.ivSinaImage);
    33 
    34         dialog = new ProgressDialog(this);
    35         dialog.setTitle("提示");
    36         dialog.setMessage("正在下载,请稍后...");
    37         dialog.setCancelable(false);
    38         
    39         btnDown.setOnClickListener(new View.OnClickListener() {            
    40             @Override
    41             public void onClick(View v) {
    42                 // 开启一个子线程,用于下载图片
    43                 new Thread(new MyThread()).start();
    44                 // 显示对话框
    45                 dialog.show();
    46             }
    47         });
    48     }
    49     
    50     public class MyThread implements Runnable {
    51 
    52         @Override
    53         public void run() {
    54             // 下载一个图片
    55             HttpClient httpClient = new DefaultHttpClient();
    56             HttpGet httpGet = new HttpGet(image_path);
    57             HttpResponse httpResponse = null;
    58             try {
    59                 httpResponse = httpClient.execute(httpGet);
    60                 if (httpResponse.getStatusLine().getStatusCode() == 200) {
    61                     byte[] data = EntityUtils.toByteArray(httpResponse
    62                             .getEntity());
    63                     // 得到一个Bitmap对象,并且为了使其在post内部可以访问,必须声明为final
    64                     final Bitmap bmp=BitmapFactory.decodeByteArray(data, 0, data.length);
    65                     handler.post(new Runnable() {                        
    66                         @Override
    67                         public void run() {
    68                             // 在Post中操作UI组件ImageView
    69                             ivImage.setImageBitmap(bmp);
    70                         }
    71                     });
    72                     // 隐藏对话框
    73                     dialog.dismiss();
    74                 }
    75             } catch (Exception e) {
    76                 e.printStackTrace();
    77             }
    78         }
    79 
    80     }
    81 }
    复制代码

       效果展示:

    Message

      Handler如果使用sendMessage的方式把消息入队到消息队列中,需要传递一个Message对象,而在Handler中,需要重写handleMessage()方法,用于获取工作线程传递过来的消息,此方法运行在UI线程上。下面先介绍一下Message。

      Message是一个final类,所以不可被继承。Message封装了线程中传递的消息,如果对于一般的数据,Message提供了getData()和setData()方法来获取与设置数据,其中操作的数据是一个Bundle对象,这个Bundle对象提供一系列的getXxx()和setXxx()方法用于传递基本数据类型的键值对,对于基本数据类型,使用起来很简单,这里不再详细讲解。而对于复杂的数据类型,如一个对象的传递就要相对复杂一些。在Bundle中提供了两个方法,专门用来传递对象的,但是这两个方法也有相应的限制,需要实现特定的接口,当然,一些Android自带的类,其实已经实现了这两个接口中的某一个,可以直接使用。方法如下:

    • putParcelable(String key,Parcelable value):需要传递的对象类实现Parcelable接口。
    • pubSerializable(String key,Serializable value):需要传递的对象类实现Serializable接口。

      还有另外一种方式在Message中传递对象,那就是使用Message自带的obj属性传值,它是一个Object类型,所以可以传递任意类型的对象,Message自带的有如下几个属性:

    • int arg1:参数一,用于传递不复杂的数据,复杂数据使用setData()传递。
    • int arg2:参数二,用于传递不复杂的数据,复杂数据使用setData()传递。
    • Object obj:传递一个任意的对象。
    • int what:定义的消息码,一般用于设定消息的标志。

       对于Message对象,一般并不推荐直接使用它的构造方法得到,而是建议通过使用Message.obtain()这个静态的方法或者Handler.obtainMessage()获取。Message.obtain()会从消息池中获取一个Message对象,如果消息池中是空的,才会使用构造方法实例化一个新Message,这样有利于消息资源的利用。并不需要担心消息池中的消息过多,它是有上限的,上限为10个。Handler.obtainMessage()具有多个重载方法,如果查看源码,会发现其实Handler.obtainMessage()在内部也是调用的Message.obtain()。  

      既然Message是在线程间传递消息,那么先以一个Demo讲解一下Message的使用,还是常规的从互联网上下载一张图片的Demo,下载后使用ImageView控件展示:

    复制代码
     1 package com.bgxt.datatimepickerdemo;
     2 
     3 import org.apache.http.HttpResponse;
     4 import org.apache.http.client.HttpClient;
     5 import org.apache.http.client.methods.HttpGet;
     6 import org.apache.http.impl.client.DefaultHttpClient;
     7 import org.apache.http.util.EntityUtils;
     8 
     9 import android.app.Activity;
    10 import android.app.ProgressDialog;
    11 import android.graphics.Bitmap;
    12 import android.graphics.BitmapFactory;
    13 import android.os.Bundle;
    14 import android.os.Handler;
    15 import android.os.Message;
    16 import android.view.View;
    17 import android.widget.Button;
    18 import android.widget.ImageView;
    19 
    20 public class HandlerMessageActivity1 extends Activity {
    21     private Button btnDown;
    22     private ImageView ivImage;
    23     private static String image_path = "http://ww4.sinaimg.cn/bmiddle/786013a5jw1e7akotp4bcj20c80i3aao.jpg";
    24     private ProgressDialog dialog;
    25     private static int IS_FINISH = 1;
    26 
    27     @Override
    28     protected void onCreate(Bundle savedInstanceState) {
    29         super.onCreate(savedInstanceState);
    30         setContentView(R.layout.asynctask_activity);
    31 
    32         btnDown = (Button) findViewById(R.id.btnDown);
    33         ivImage = (ImageView) findViewById(R.id.ivSinaImage);
    34 
    35         dialog = new ProgressDialog(this);
    36         dialog.setTitle("提示信息");
    37         dialog.setMessage("正在下载,请稍后...");
    38         dialog.setCancelable(false);
    39 
    40         btnDown.setOnClickListener(new View.OnClickListener() {
    41             @Override
    42             public void onClick(View v) {
    43                     new Thread(new MyThread()).start();
    44                     dialog.show();
    45             }
    46         });
    47     }
    48 
    49     private  Handler handler = new Handler() {
    50         // 在Handler中获取消息,重写handleMessage()方法
    51         @Override
    52         public void handleMessage(Message msg) {            
    53             // 判断消息码是否为1
    54             if(msg.what==IS_FINISH){
    55                 byte[] data=(byte[])msg.obj;
    56                 Bitmap bmp=BitmapFactory.decodeByteArray(data, 0, data.length);
    57                 ivImage.setImageBitmap(bmp);
    58                 dialog.dismiss();
    59             }
    60         }
    61     };
    62 
    63     public class MyThread implements Runnable {
    64 
    65         @Override
    66         public void run() {
    67             HttpClient httpClient = new DefaultHttpClient();
    68             HttpGet httpGet = new HttpGet(image_path);
    69             HttpResponse httpResponse = null;
    70             try {
    71                 httpResponse = httpClient.execute(httpGet);
    72                 if (httpResponse.getStatusLine().getStatusCode() == 200) {
    73                     byte[] data = EntityUtils.toByteArray(httpResponse
    74                             .getEntity());
    75                     // 获取一个Message对象,设置what为1
    76                     Message msg = Message.obtain();
    77                     msg.obj = data;
    78                     msg.what = IS_FINISH;
    79                     // 发送这个消息到消息队列中
    80                     handler.sendMessage(msg);
    81                 }
    82             } catch (Exception e) {
    83                 e.printStackTrace();
    84             }
    85         }
    86     }
    87 }
    复制代码

      展示效果:

      Message.obtain()方法具有多个重载方法,大致可以分为为两类,一类是无需传递Handler对象,对于这类的方法,当填充好消息后,需要调用Handler.sendMessage()方法来发送消息到消息队列中。第二类需要传递一个Handler对象,这类方法可以直接使用Message.sendToTarget()方法发送消息到消息队列中,这是因为在Message对象中有一个私有的Handler类型的属性Target,当时obtain方法传递进一个Handler对象的时候,会给Target属性赋值,当调用sendToTarget()方法的时候,实际在它内部还是调用的Target.sendMessage()方法。

      在Handler中,也定义了一些发送空消息的方法,如:sendEmptyMessage(int what)、sendEmptyMessageDelayed(int what,long delayMillis),看似这些方法没有使用Message就可以发送一个消息,但是如果查看源码就会发现,其实内部也是从Message.obtain()方法中获取一个Message对象,然后给属性赋值,最后使用sendMessage()发送消息到消息队列中。

      Handler中,与Message发送消息相关的方法有:

    • Message obtainMessage():获取一个Message对象。
    • boolean sendMessage():发送一个Message对象到消息队列中,并在UI线程取到消息后,立即执行。
    • boolean sendMessageDelayed():发送一个Message对象到消息队列中,在UI线程取到消息后,延迟执行。
    • boolean sendEmptyMessage(int what):发送一个空的Message对象到队列中,并在UI线程取到消息后,立即执行。
    • boolean sendEmptyMessageDelayed(int what,long delayMillis):发送一个空Message对象到消息队列中,在UI线程取到消息后,延迟执行。
    • void removeMessage():从消息队列中移除一个未响应的消息。

      下面通过一个小Demo演示一下各种发送Message的方式:

    复制代码
      1 package com.bgxt.datatimepickerdemo;
      2 
      3 import android.app.Activity;
      4 import android.os.Bundle;
      5 import android.os.Handler;
      6 import android.os.Message;
      7 import android.view.View;
      8 import android.widget.Button;
      9 import android.widget.TextView;
     10 
     11 public class HandlerMessageActivity2 extends Activity {
     12     private Button btn1, btn2, btn3, btn4,btn5;
     13     private static TextView tvMes;
     14     private static Handler handler = new Handler() {
     15         @Override
     16         public void handleMessage(android.os.Message msg) {
     17             if (msg.what == 3||msg.what==5) {
     18                 tvMes.setText("what=" + msg.what + ",这是一个空消息");
     19             } else {
     20                 tvMes.setText("what=" + msg.what + "," + msg.obj.toString());
     21             }
     22 
     23         };
     24     };
     25 
     26     @Override
     27     protected void onCreate(Bundle savedInstanceState) {
     28         // TODO Auto-generated method stub
     29         super.onCreate(savedInstanceState);
     30         setContentView(R.layout.message_activity2);
     31         tvMes = (TextView) findViewById(R.id.tvMes);
     32         btn1 = (Button) findViewById(R.id.btnMessage1);
     33         btn2 = (Button) findViewById(R.id.btnMessage2);
     34         btn3 = (Button) findViewById(R.id.btnMessage3);
     35         btn4 = (Button) findViewById(R.id.btnMessage4);
     36         btn5 = (Button) findViewById(R.id.btnMessage5);
     37 
     38         btn1.setOnClickListener(new View.OnClickListener() {
     39             @Override
     40             public void onClick(View v) {
     41                 // 使用Message.Obtain+Hander.sendMessage()发送消息
     42                 new Thread(new Runnable() {
     43                     @Override
     44                     public void run() {
     45                         Message msg = Message.obtain();
     46                         msg.what = 1;
     47                         msg.obj = "使用Message.Obtain+Hander.sendMessage()发送消息";
     48                         handler.sendMessage(msg);
     49                     }
     50                 }).start();
     51             }
     52         });
     53 
     54         btn2.setOnClickListener(new View.OnClickListener() {
     55 
     56             @Override
     57             public void onClick(View v) {
     58                 // 使用Message.sendToTarget发送消息
     59                 new Thread(new Runnable() {
     60                     @Override
     61                     public void run() {
     62                         Message msg = Message.obtain(handler);
     63                         msg.what = 2;
     64                         msg.obj = "使用Message.sendToTarget发送消息";
     65                         msg.sendToTarget();
     66                     }
     67                 }).start();
     68             }
     69         });
     70 
     71         btn3.setOnClickListener(new View.OnClickListener() {
     72             // 发送一个延迟消息
     73             @Override
     74             public void onClick(View v) {
     75                 new Thread(new Runnable() {
     76                     @Override
     77                     public void run() {
     78                         handler.sendEmptyMessage(3);
     79                     }
     80                 }).start();
     81             }
     82         });
     83 
     84         btn4.setOnClickListener(new View.OnClickListener() {
     85 
     86             @Override
     87             public void onClick(View v) {
     88                 new Thread(new Runnable() {
     89                     @Override
     90                     public void run() {
     91                         Message msg = Message.obtain();
     92                         msg.what =4;
     93                         msg.obj = "使用Message.Obtain+Hander.sendMessage()发送延迟消息";
     94                         handler.sendMessageDelayed(msg, 3000);
     95                     }
     96                 }).start();
     97             }
     98         });
     99         
    100         btn5.setOnClickListener(new View.OnClickListener() {
    101             // 发送一个延迟的空消息
    102             @Override
    103             public void onClick(View v) {
    104                 new Thread(new Runnable() {
    105                     @Override
    106                     public void run() {
    107                         handler.sendEmptyMessageDelayed(5, 3000);
    108                     }
    109                 }).start();
    110             }
    111         });
    112     }
    113 }
    复制代码

      效果展示:

      源码下载

      

    作者:承香墨影
    更多内容,请阅读本人新书:《Android深入浅出》
    欢迎转载,但还请尊重劳动果实,保留此段声明并注明原文链接。
  • 相关阅读:
    iOS 苹果开发证书失效的解决方案(Failed to locate or generate matching signing assets)
    iOS NSArray数组过滤
    App Store2016年最新审核规则
    iOS 根据字符串数目,自定义Label等控件的高度
    iOS 证书Bug The identity used to sign the executable is no longer valid 解决方案
    Entity FrameWork 增删查改的本质
    EF容器---代理类对象
    Entity FrameWork 延迟加载本质(二)
    Entity FrameWork 延迟加载的本质(一)
    Entity FrameWork 增删查改
  • 原文地址:https://www.cnblogs.com/vactor/p/5014212.html
Copyright © 2011-2022 走看看