Handler和postDelayed方法和removeCallbacks方法的使用

6.4  Handle对象的使用

对象Handle的主要作用是可以发送和处理消息队列,在Android中模仿了Windows操作系统中的Message原理来实现组件间的解耦,它可以接受子线程发送的Message对象,并用此Message对象中封装的数据在主线程中更新UI界面。

需要注意的是,在UI线程中启动Handler对象时,Handler与调用者Activity处于同一线程,也就是通常所说的UI线程。如果Handler里面做耗时的动作,UI线程会阻塞,另外由于Android的UI线程不是安全的,并且这些操作必须在UI线程中执行,如果不是在UI线程中操作View对象则系统报出异常。每个Handler实例都会绑定到创建它的线程中(一般是位于主线程)。

在Android中进行与UI通信的开发时,经常会使用Handler对象来控制UI程序的界面,它的作用可以理解为与其他线程协同工作,接收其他线程的消息并通过接收到的消息更新UI界面。

现在有这么一种情况,在一个UI界面上有一个按钮,当单击这个按钮的时候会进行网络连接,并把网络上的数据取下来显示到UI界面中一个TextView里,这时出现一个问题,就是如果这个网络连接的延迟过大,或根本连接不上,可能用时数秒甚至更长,那么程序的界面将处于一种假死状态,这样的效果很明显不符合体验性好的软件标准,这时理论上可以创建一个线程,在线程中取得网络上的数据,但下一步出现了问题!在用户自定义的线程中将取到的数据去更新UI则会报出异常,这个情况在第二章已经介绍过此实验,因为Android是单线程模型,不允许程序员在自定义的线程类中直接操作UI界面,为了解决这个问题,Android开发了Handler对象,由它来负责与子线程进行通信,从而让子线程与主线程之间建立起协作的桥梁,当然也就可以传递数据(大多使用Message对象传递),使Android的UI更新问题得到解决。

6.4.1  Handler对象的初步使用(1)

本示例就模拟从网络下载数据再显示到UI界面上的效果。新建名称为handler1的Android项目,文件Main.java的核心代码如下:

 
  
  1. public class Main extends Activity {  
  2.     private Button button1;  
  3.  
  4.     private Handler handler = new Handler() {  
  5.         @Override  
  6.         public void handleMessage(Message msg) {  
  7.             super.handleMessage(msg);  
  8.             Log.v("!", "Activity print status="  
  9.                     + msg.getData().getString("status") + " thread name="  
  10.                     + Thread.currentThread().getName());  
  11.         }  
  12.     };  
  13.  
  14.     @Override  
  15.     public void onCreate(Bundle savedInstanceState) {  
  16.         super.onCreate(savedInstanceState);  
  17.         setContentView(R.layout.main);  
  18.  
  19.         Log.v("!", "Activity Thread name=" + Thread.currentThread().getName());  
  20.  
  21.         button1 = (Button) this.findViewById(R.id.button1);  
  22.         button1.setOnClickListener(new OnClickListener() {  
  23.             public void onClick(View arg0) {  
  24.                 GhyThread ghyThreadRef = new GhyThread(handler);  
  25.                 ghyThreadRef.start();  
  26.             }  
  27.         });  
  28.  
  29.     }  
  30. }  
自定义线程类GhyThread.java的核心代码如下:
 
  
  1. public class GhyThread extends Thread {  
  2.  
  3.     public GhyThread(Handler handler) {  
  4.         super();  
  5.         this.handler = handler;  
  6.     }  
  7.  
  8.     private Handler handler;  
  9.  
  10.     @Override  
  11.     public void run() {  
  12.         super.run();  
  13.         try {  
  14.             int i = 0;  
  15.             while (i < 10) {  
  16.                 i++;  
  17.                 Log.v("!", "GhyThread threadName="  
  18.                         + this.currentThread().getName() + " i=" + i);  
  19.                 Thread.sleep(1000);  
  20.             }  
  21.             Bundle bundle = new Bundle();  
  22.             bundle.putString("status", "end");  
  23.  
  24.             Message message = new Message();  
  25.             message.setData(bundle);  
  26.             handler.sendMessage(message);  
  27.         } catch (InterruptedException e) {  
  28.             // TODO Auto-generated catch block  
  29.             e.printStackTrace();  
  30.         }  
  31.     }  
  32. }  

程序运行后的效果如图6.37所示。

Handler和postDelayed方法和removeCallbacks方法的使用_第1张图片 
图6.37  运行效果

6.4.1  Handler对象的初步使用(2)

从图6.37中可以看到,方法handleMessage()是运行在main主线程中的,也就是Handler被绑定到了主线程中。

为了进一步演示Handler绑定到主线程中的情况,新建一个名称为HandlerBindUIThread项目,Activity文件Main.java的代码如下:

 
  
  1. public class Main extends Activity {  
  2.  
  3.     private Runnable run = new Runnable() {  
  4.         public void run() {  
  5.             try {  
  6.                 Log.v("!", "run thread is=" + Thread.currentThread().getId()  
  7.                         + " thread name=" + Thread.currentThread().getName());  
  8.                 Thread.sleep(10000);  
  9.             } catch (InterruptedException e) {  
  10.                 // TODO Auto-generated catch block  
  11.                 e.printStackTrace();  
  12.             }  
  13.         }  
  14.     };  
  15.  
  16.     @Override  
  17.     public void onCreate(Bundle savedInstanceState) {  
  18.         super.onCreate(savedInstanceState);  
  19.         Log.v("!", "onCreate thread is=" + Thread.currentThread().getId()  
  20.                 + " thread name=" + Thread.currentThread().getName());  
  21.         Log.v("!", "begin");  
  22.         long beginTime = System.currentTimeMillis();  
  23.         Handler hanlder = new Handler();  
  24.         hanlder.post(run);  
  25.         setContentView(R.layout.main);  
  26.         long endTime = System.currentTimeMillis();  
  27.         Log.v("!", "耗时:" + (endTime - beginTime) / 1000);  
  28.     }  
  29. }  
程序运行的结果如图6.38所示。
 
图6.38  程序运行结果
这是打印出来的结果,真正的运行流程是先打印出图6.38所示的日志信息,然后项目挂起10秒钟后再显示出界面,从图6.38中还可以看到都是在线程名称为main中运行,即属于同步的方式运行,具有"阻塞"的特点,有没有办法实现异步方式运行呢?也就是新开启一个线程运行,并且不耽误Activity界面的显示。这只要将Main.java的代码更改为如下形式就可以实现这种要求。
 
  
  1. public class Main extends Activity {  
  2.  
  3.     private Runnable run = new Runnable() {  
  4.         public void run() {  
  5.             try {  
  6.                 Log.v("!", "run thread is=" + Thread.currentThread().getId()  
  7.                         + " thread name=" + Thread.currentThread().getName());  
  8.                 Thread.sleep(10000);  
  9.                 Log.v("!", "run end!");  
  10.             } catch (InterruptedException e) {  
  11.                 // TODO Auto-generated catch block  
  12.                 e.printStackTrace();  
  13.             }  
  14.         }  
  15.     };  
  16.  
  17.     @Override  
  18.     public void onCreate(Bundle savedInstanceState) {  
  19.         super.onCreate(savedInstanceState);  
  20.         Log.v("!", "onCreate thread is=" + Thread.currentThread().getId()  
  21.                 + " thread name=" + Thread.currentThread().getName());  
  22.         Log.v("!", "begin");  
  23.         long beginTime = System.currentTimeMillis();  
  24.         // Handler hanlder = new Handler();  
  25.         // hanlder.post(run);  
  26.         Thread thread = new Thread(run);  
  27.         thread.start();  
  28.         setContentView(R.layout.main);  
  29.         long endTime = System.currentTimeMillis();  
  30.         Log.v("!", "耗时:" + (endTime - beginTime) / 1000);  
  31.     }  
  32. }  

6.4.1  Handler对象的初步使用(3)

程序运行后界面也优先显示了出来,如图6.39所示。

Handler和postDelayed方法和removeCallbacks方法的使用_第2张图片 
(点击查看大图)图6.39  先显示界面
10秒后打印出了结束日志,结果如图6.40所示。
Handler和postDelayed方法和removeCallbacks方法的使用_第3张图片 
图6.40  打印结束日志

对象Handler的初步使用主要体现在构造函数的方法上,这些函数的功能说明如下。

(1)public Handler():无参的构造函数,将创建好的Handler实例绑定到代码所在的线程的消息队列上,因此一定要确定该线程开启了消息队列,否则程序将发生错误,使用这个构造函数创建的Handler实例需要重写Hanler类的handleMessage()方法,以便在之后的消息处理时调用。

(2)public Handler(Callback callback):接口Callback是Handler内部定义的一个接口,因此想要使用这个构造函数创建Handler对象,需要自定义一个类实现Callback接口,并重写接口中定义的handleMessage()方法,这个构造函数其实与无参的构造函数类似,也要确保代码所在的线程开启了消息队列,不同的是在之后处理消息时,将调用接口Callback的handleMessage()方法,而不是Handler对象的handleMssage()方法。

(3)public Handler(Looper looper):表示创建一个Handler实例并将其绑定在Looper所在的线程上,此时looper不能为null,一般也需要重写Hanler类的handleMessage()方法。

(4)public Handler(Looper looper,Callback callback):与(2)和(3)功能相结合。

还有几个知识点需要留意:

(1)调用Handler类中以send开头的方法可以将Message对象压入消息队列中,调用Handler类中以post开头的方法可以将一个Runnable对象包装在一个Message对象中,然后再压入消息队列,此时入队的Message其Callback字段不为null,值就是这个Runnable对象。

(2)调用Message对象的sendToTarget()方法可以将其本身(Message)压入与其target字段(即handler对象)所关联的消息队列中。

6.4.2  postDelayed方法和removeCallbacks方法的使用(1)

方法postDelayed的作用是延迟多少毫秒后开始运行,而removeCallbacks方法是删除指定的Runnable对象,使线程对象停止运行。

方法声明如下:

 
  
  1. public final boolean postDelayed (Runnable r, long delayMillis) 

其中参数Runnable r在Handler对象所运行的线程中执行。

创建名称为handler2的Android项目,Main.java的核心代码如下:

 
  
  1. public class Main extends Activity {  
  2.     private Button button1;  
  3.     private Button button2;  
  4.  
  5.     private Handler handler = new Handler();  
  6.  
  7.     private int count = 0;  
  8.  
  9.     private Runnable runnableRef = new Runnable() {  
  10.         public void run() {  
  11.             Log.v("2", Thread.currentThread().getName());  
  12.             count++;  
  13.             Log.v("!", "count=" + count);  
  14.             handler.postDelayed(runnableRef, 1000);  
  15.         }  
  16.     };  
  17.  
  18.     @Override  
  19.     public void onCreate(Bundle savedInstanceState) {  
  20.         super.onCreate(savedInstanceState);  
  21.         setContentView(R.layout.main);  
  22.  
  23.         Log.v("1", Thread.currentThread().getName());  
  24.  
  25.         button1 = (Button) this.findViewById(R.id.button1);  
  26.         button2 = (Button) this.findViewById(R.id.button2);  
  27.  
  28.         button1.setOnClickListener(new OnClickListener() {  
  29.             public void onClick(View arg0) {  
  30.                 Thread thread = new Thread(runnableRef);  
  31.                 thread.start();  
  32.                 Log.v("!!!!!!!!!!!!!", "end");  
  33.             }  
  34.         });  
  35.  
  36.         button2.setOnClickListener(new OnClickListener() {  
  37.             public void onClick(View arg0) {  
  38.                 handler.removeCallbacks(runnableRef);  
  39.             }  
  40.         });  
  41.  
  42.     }  
  43. }  
程序运行后单击button1按钮开始循环,count累加1,运行结果如图6.41所示。
Handler和postDelayed方法和removeCallbacks方法的使用_第4张图片 
图6.41  循环加1效果


从打印结果可以发现,使用代码:

 
  
  1. handler.postDelayed(runnableRef, 1000) 

上述代码运行的Runnable并没有新建一个线程,而是运行在main线程里。

当单击button2按钮时,停止这种累加1的功能。

关于循环执行某一个任务还可以使用Java SE自带的类来进行处理,新建名称为TimerTest项目,文件Main.java的代码如下:

 
  
  1. public class Main extends Activity {  
  2.     private int count = 0;  
  3.  
  4.     @Override  
  5.     public void onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.         setContentView(R.layout.main);  
  8.  
  9.         TimerTask task = new TimerTask() {  
  10.             @Override  
  11.             public void run() {  
  12.                 Log.v("!", "" + (++count));  
  13.             }  
  14.         };  
  15.         Timer timer = new Timer();  
  16.         timer.schedule(task, 1000, 1000);  
  17.     }  
  18. }  
打印的效果如图6.42所示。
Handler和postDelayed方法和removeCallbacks方法的使用_第5张图片 
图6.42  Timer循环执行某一任务
如果想在TimerTask中控制View控件,还需要用Handler对象以发送消息Message的方式来处理View的更新。

6.4.3  post方法的使用

方法post是将Message对象放入消息队列中,以待后面执行消息队列中的任务。

方法声明如下:

 
  
  1. public final boolean post (Runnable r) 

其中参数Runnable r在Handler对象所运行的线程中执行。

新建名称为handler3的Android项目,文件Main.java的代码如下:

 
  
  1. public class Main extends Activity {  
  2.     private Button button1;  
  3.     private int count = 0;  
  4.  
  5.     private Handler handler = new Handler() {  
  6.         @Override  
  7.         public void handleMessage(Message msg) {  
  8.             super.handleMessage(msg);  
  9.             Log.v("!", "count=" + msg.getData().getString("count") + "  3"  
  10.                     + Thread.currentThread().getName());  
  11.  
  12.         }  
  13.     };  
  14.  
  15.     @Override  
  16.     public void onCreate(Bundle savedInstanceState) {  
  17.         super.onCreate(savedInstanceState);  
  18.         setContentView(R.layout.main);  
  19.  
  20.         Log.v("1", "" + Thread.currentThread().getName());  
  21.  
  22.         button1 = (Button) this.findViewById(R.id.button1);  
  23.         button1.setOnClickListener(new OnClickListener() {  
  24.             public void onClick(View arg0) {  
  25.                 count = 0;  
  26.                 handler.post(new Runnable() {  
  27.                     public void run() {  
  28.                         count++;  
  29.                         while (count < 10) {  
  30.                             try {  
  31.                                 Log.v("2", ""  
  32.                                         + Thread.currentThread().getName());  
  33.  
  34.                                 Bundle bundle = new Bundle();  
  35.                                 bundle.putString("count", "" + count);  
  36.  
  37.                                 Message message = new Message();  
  38.                                 message.setData(bundle);  
  39.  
  40.                                 handler.sendMessage(message);  
  41.                                 count++;  
  42.                                 Thread.sleep(200);  
  43.                             } catch (InterruptedException e) {  
  44.                                 // TODO Auto-generated catch block  
  45.                                 e.printStackTrace();  
  46.                             }  
  47.                         }  
  48.                     }  
  49.                 });  
  50.  
  51.                 for (int i = 0; i < 10; i++) {  
  52.                     try {  
  53.                         Log.v("!", "i=" + (i + 1));  
  54.                         Thread.sleep(1000);  
  55.                     } catch (InterruptedException e) {  
  56.                         // TODO Auto-generated catch block  
  57.                         e.printStackTrace();  
  58.                     }  
  59.                 }  
  60.  
  61.             }  
  62.         });  
  63.     }  
  64. }  
程序运行后单击Button按钮出现如图6.43所示的效果。
Handler和postDelayed方法和removeCallbacks方法的使用_第6张图片 
图6.43  运行效果
从本示例可以发现,Handler的post()方法对线程的处理也不是真正创建一个新的线程,而是直接调用了线程的run方法。

6.4.4  postAtTime方法的使用

方法postAtTime的作用是实现隔几秒后自动执行,本示例代码在项目handler4中。

 
  
  1. handler.postAtTime(new Runnable() {  
  2.                     public void run() {  
  3.                         count++;  
  4.                         while (count < 10) {  
  5.                             Bundle bundle = new Bundle();  
  6.                             bundle.putString("count", "" + count);  
  7.  
  8.                             Message message = new Message();  
  9.                             message.setData(bundle);  
  10.  
  11.                             handler.sendMessage(message);  
  12.  
  13.                             count++;  
  14.                         }  
  15.                     }  
  16.                 }, SystemClock.uptimeMillis() + 5000);  
本示例实现的效果是隔5秒后执行。

6.4.5  在线程对象的run方法中实例化Handler对象的注意事项(1)

在有些情况下,需要在线程中创建Handler对象然后发送消息。

创建名称为threadUseHandler的Android项目,创建自定义Handler对象GhyHandler.java,代码如下:

 
  
  1. package exthandler;  
  2.  
  3. import android.os.Handler;  
  4. import android.os.Message;  
  5. import android.util.Log;  
  6.  
  7. public class GhyHandler extends Handler {  
  8.     @Override  
  9.     public void handleMessage(Message msg) {  
  10.         super.handleMessage(msg);  
  11.         Log.v("!", "usernameusername=" + msg.getData().getString("username"));  
  12.     }  
  13. }  
创建自定义线程类GhyThread.java,代码如下:
 
  
  1. package extthread;  
  2.  
  3. import android.os.Bundle;  
  4. import android.os.Message;  
  5. import exthandler.GhyHandler;  
  6.  
  7. public class GhyThread extends Thread {  
  8.  
  9.     @Override  
  10.     public void run() {  
  11.         super.run();  
  12.  
  13.         GhyHandler handler = new GhyHandler();  
  14.         Message message = handler.obtainMessage();  
  15.         Bundle bundle = new Bundle();  
  16.         bundle.putString("username", "gaohongyan");  
  17.         message.setData(bundle);  
  18.         handler.sendMessage(message);  
  19.  
  20.     }  
  21. }  
文件Main.java的代码如下:
 
  
  1. package threadUseHandler.test.run;  
  2.  
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import extthread.GhyThread;  
  6.  
  7. public class Main extends Activity {  
  8.     @Override  
  9.     public void onCreate(Bundle savedInstanceState) {  
  10.         super.onCreate(savedInstanceState);  
  11.         setContentView(R.layout.main);  
  12.  
  13.         GhyThread ghyThreadRef = new GhyThread();  
  14.         ghyThreadRef.start();  
  15.  
  16.     }  
  17. }  

6.4.5  在线程对象的run方法中实例化Handler对象的注意事项(2)

程序运行后出现错误如下:

 
  
  1. java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() 

出错的原因是当前的线程GhyThread.java并没有创建Looper对象,一个线程可以产生一个Looper对象,由Looper对象来管理线程里的Message Queue(消息队列),Message Queue按顺序处理队列中的Message对象,每一个线程里可含有一个Looper对象以及一个MessageQueue。

Handler在创建的时候可以指定Looper,这样通过Handler的sendMessage()方法发送出去的消息就会添加到指定Looper里面的MessageQueue里面去,但在不指定Looper的情况下,Handler绑定的是创建它的线程的Looper,如果这个线程的Looper不存在,程序将抛出"java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()"的异常,这就是上面代码出错的原因。

介绍到这儿,有必要对一些知识点进行一下总结:

(1)Message消息,可以理解为线程间通信的数据单元,通过将数据放入Message对象中以便达到线程间的通信。例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含最新数据信息的Message给UI线程。

(2)Message Queue消息用来存放通过Handler发布的消息,按照先进先出执行。队列中的每一个Message都有一个when字段,这个字段用来决定Message应该何时处理,消息队列中的每一个Message根据when字段的大小由小到大排列,排在最前面的消息会首先得到处理,因此可以说消息队列并不是一个严格的先进先出的队列。

Message对象的target字段表示关联了哪个线程的消息队列,这个消息就会被压入哪个线程的消息队列中,Message类用于表示消息。Message对象可以通过arg1、arg2、obj字段和setData()携带数据,此外还具有很多字段。when字段决定Message应该何时处理,target字段用来表示将由哪个Handler对象处理这个消息,next 字段表示在消息队列中排在这个Message之后的下一个Message,callback字段如果不为null,表示这个Message包装了一个runnable对象,what字段表示code,即这个消息具体是什么类型的消息。每个what都在其handler的namespace中,只需要确保将由同一个handler处理的消息的what属性不重复就可以。

(3)Handler是Message的主要处理者,负责将Message添加到消息队列以及对消息队列中的Message进行处理。

(4)Looper循环器扮演Message Queue和Handler之间桥梁的角色,循环取出Message Queue里面的Message,并交付给相应的Handler进行处理。Looper类主要用来创建消息队列,每个线程最多只能有一个消息队列,在Android中UI线程默认具有消息队列,但非UI线程在默认情况下是不具备消息队列的,比如自定义的线程类。如果需要在非UI线程中开启消息队列,需要调用Looper.prepare()方法,该方法在执行过程中会创建一个Looper对象,而在源代码中的Looper构造函数中会创建一个MessageQueue实例,此后再为该线程绑定一个Handler实例,再调用Looper.loop()方法,就可以不断地从消息队列中取出消息和处理消息了。Looper.myLoop()方法可以得到线程的Looper对象,如果为null,说明此时该线程尚未开启消息队列。通过Loop.getMainLooper()可以获得当前进程的主线程的Looper对象。

如果想让该线程具有消息队列和消息循环,需要在线程中首先调用Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环,这样该线程就具有了消息处理机制,可以在Handler对象中进行消息处理。

下面来看看其实现方法,更改GhyThread.java的代码如下:

 
  
  1. public class GhyThread extends Thread {  
  2.  
  3.     @Override  
  4.     public void run() {  
  5.         super.run();  
  6.  
  7.         Looper.prepare();//准备创建1个Looper对象  
  8.  
  9.         GhyHandler handler = new GhyHandler();  
  10.         Message message = handler.obtainMessage();  
  11.         Bundle bundle = new Bundle();  
  12.         bundle.putString("username", "gaohongyan");  
  13.         message.setData(bundle);  
  14.         handler.sendMessage(message);  
  15.  
  16.         Looper.loop();//执行消息队列中的Message对象  
  17.  
  18.     }  
  19. }  
程序运行后正确地取出了username的值,如图6.44所示。
 
图6.44  成功打印username的值

6.4.6  以异步方式打开网络图片(1)

创建持有PNG图标资源的Web项目pngProject,布署到tomcat中,项目文件结构如图6.45所示。

Handler和postDelayed方法和removeCallbacks方法的使用_第7张图片 
图6.45  持有png图标的web项目
创建Android客户端应用程序项目synchronizedOpenNetPNG,由于是以异步方式访问远程PNG图片资源,所以创建自定义线程类OpenNetPNGThread.java,该类主要的功能就是通过远程PNG图片的URL返回Bitmap位图资源,核心代码如下:
 
  
  1. public class OpenNetPNGThread extends Thread {  
  2.  
  3.     private String pngPath;  
  4.     private Handler handler;  
  5.     private int imageViewId;  
  6.  
  7.     public OpenNetPNGThread(Handler handler, String pngPath, int imageViewId) {  
  8.         super();  
  9.         this.pngPath = pngPath;  
  10.         this.handler = handler;  
  11.         this.imageViewId = imageViewId;  
  12.     }  
  13.  
  14.     @Override  
  15.     public void run() {  
  16.         super.run();  
  17.         try {  
  18.             Log.v("!", "启动线程" + Thread.currentThread().getId() + "  "  
  19.                     + Thread.currentThread().getName());  
  20.             URL url = new URL(pngPath);  
  21.             URLConnection connection = url.openConnection();  
  22.             InputStream isRef = connection.getInputStream();  
  23.             Bitmap bitmap = BitmapFactory.decodeStream(isRef);  
  24.  
  25.             Bundle bundle = new Bundle();  
  26.             bundle.putInt("imageViewId", imageViewId);  
  27.             bundle.putParcelable("bitmap", bitmap);  
  28.  
  29.             Message message = handler.obtainMessage();  
  30.             message.setData(bundle);  
  31.             handler.sendMessage(message);  
  32.  
  33.         } catch (MalformedURLException e) {  
  34.             // TODO Auto-generated catch block  
  35.             e.printStackTrace();  
  36.         } catch (IOException e) {  
  37.             // TODO Auto-generated catch block  
  38.             e.printStackTrace();  
  39.         }  
  40.  
  41.     }  
  42. }  

6.4.6  以异步方式打开网络图片(2)

创建自定义Handler对象PNGHandler.java,该类主要的作用是从Message中取出Bitmap资源来对ImageView进行更新,核心代码如下:

 
  
  1. public class PNGHandler extends Handler {  
  2.  
  3.     private Context context;  
  4.  
  5.     public PNGHandler(Context context) {  
  6.         super();  
  7.         this.context = context;  
  8.     }  
  9.  
  10.     @Override  
  11.     public void handleMessage(Message msg) {  
  12.         super.handleMessage(msg);  
  13.  
  14.         Bundle bundle = msg.getData();  
  15.         Bitmap bitmap = bundle.getParcelable("bitmap");  
  16.         int imageViewId = bundle.getInt("imageViewId");  
  17.  
  18.         ImageView findImageView = (ImageView) ((Activity) context)  
  19.                 .findViewById(imageViewId);  
  20.         findImageView.setImageBitmap(bitmap);  
  21.  
  22.     }  
  23. }  

项目的核心Activity对象Main.java文件的主要代码如下:

 
  
  1. public class Main extends Activity {  
  2.  
  3.     private PNGHandler[] handler = new PNGHandler[5];  
  4.     private String[] pngFileName = new String[5];  
  5.  
  6.     private ImageView imageView1;  
  7.     private ImageView imageView2;  
  8.     private ImageView imageView3;  
  9.     private ImageView imageView4;  
  10.     private ImageView imageView5;  
  11.  
  12.     private ImageView[] imageViewArray = new ImageView[5];  
  13.  
  14.     @Override  
  15.     public void onCreate(Bundle savedInstanceState) {  
  16.         super.onCreate(savedInstanceState);  
  17.         setContentView(R.layout.main);  
  18.  
  19.         imageView1 = (ImageView) this.findViewById(R.id.imageView1);  
  20.         imageView2 = (ImageView) this.findViewById(R.id.imageView2);  
  21.         imageView3 = (ImageView) this.findViewById(R.id.imageView3);  
  22.         imageView4 = (ImageView) this.findViewById(R.id.imageView4);  
  23.         imageView5 = (ImageView) this.findViewById(R.id.imageView5);  
  24.  
  25.         imageViewArray[0] = imageView1;  
  26.         imageViewArray[1] = imageView2;  
  27.         imageViewArray[2] = imageView3;  
  28.         imageViewArray[3] = imageView4;  
  29.         imageViewArray[4] = imageView5;  
  30.  
  31.         pngFileName[0] = "http://10.0.2.2:8081/pngProject/a.png";  
  32.         pngFileName[1] = "http://10.0.2.2:8081/pngProject/b.png";  
  33.         pngFileName[2] = "http://10.0.2.2:8081/pngProject/c.png";  
  34.         pngFileName[3] = "http://10.0.2.2:8081/pngProject/d.png";  
  35.         pngFileName[4] = "http://10.0.2.2:8081/pngProject/e.png";  
  36.  
  37.         for (int i = 0; i < handler.length; i++) {  
  38.             handler[i] = new PNGHandler(this);  
  39.         }  
  40.         for (int i = 0; i < handler.length; i++) {  
  41.             OpenNetPNGThread mythread = new OpenNetPNGThread(handler[i],  
  42.                     pngFileName[i], imageViewArray[i].getId());  
  43.             mythread.start();  
  44.         }  
  45.  
  46.     }  
  47. }  

6.4.6  以异步方式打开网络图片(3)

程序运行后的结果如图6.46所示。控件ImageView显示出5张图片资源,如图6.47所示。

Handler和postDelayed方法和removeCallbacks方法的使用_第8张图片 
图6.46  运行结果
Handler和postDelayed方法和removeCallbacks方法的使用_第9张图片 
图6.47  5张PNG资源显示在ImageView控件中


你可能感兴趣的:(多线程)