【转】 AyncTask 实战 模拟GridView 动态更新效果

5点多就在睡梦中惊醒,睡不着,于是醒着也是醒着,还不如跟进下项目,于是网上看到这样一篇文章,刚好解决现在的问题,贴出来。

 

原文:http://www.ophonesdn.com/article/show/80

 

OPhone 线程模型:

      首先先要普及一下OPhone的线程模型: 当一个OPhone得应用运行后, 就会有一个UI的main线程启动, 这是一个非常重要的线程,它负责把事件分派到相应的控件,其中就包括屏幕绘图事件,它同样是用户与OPhone控件交互的线程。比如,当你在屏幕上的 EditText上输入文字,UI线程会把这个事件分发给刚输入文字的EditText,紧接会向事件队列发送一个更新(invalidate)请求。 UI线程会把这个请求移出事件队列并通知EditText在屏幕上重新绘制自身。

      这种单线线程模型就会使得OPhone的应用程序性能低下, 如果在这个单线程里执行一些耗时的操作, 比如访问数据库, 或是从网络端下载图片, 就会会阻塞整个用户界面。 比如如下操作:

view plain copy to clipboard print ?
  1. public   void  onClick( View v ) {    
  2.          // 这里有可能是非常耗时的操作   
  3.             Bitmap b = loadImageFromNetwork();    
  4.             mImageView.setImageBitmap( b );    
  5.      }  


     在这种情况下你会发现, 界面僵死在那里并且OPhone在系统5秒中后没有反应,会显示如下错误:

      有的同学可能会采取以下的办法:

view plain copy to clipboard print ?
  1. public   void  onClick( View v ) {    
  2.             new  Thread( new  Runnable() {  
  3.                 public   void  run() {  
  4.                     Bitmap b = loadImageFromNetwork();    
  5.                         mImageView.setImageBitmap( b );    
  6.                 }  
  7.             }).start();  
  8.              
  9.      }  

    但这样会发生一些很难察觉的错误, 因为我们知道UI线程不是线程安全的。

当然有很多种方法来处理这个问题:
OPhone提供了几种在其他线程中访问UI线程的方法。
• Activity.runOnUiThread( Runnable )
• View.post( Runnable )
• View.postDelayed( Runnable, long )
• Hanlder

 

view plain copy to clipboard print ?
  1. public   void  onClick( View v ) {    
  2.     new  Thread(  new  Runnable() {    
  3.             public   void  run() {    
  4.                      final  Bitmap b = loadImageFromNetwork();    
  5.                      mImageView.post( new  Runnable() {    
  6.                      mImageView.setImageBitmap( b );    
  7. });    
  8.           }    
  9.     }).start();    

        这种方法比较繁琐, 同时当你需要实现一些很复杂的操作并需要频繁地更新UI时这会变得更糟糕。为了解决这个问题,OPhone 1.5提供了一个工具类:AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单。
我这里不详细介绍AsyncTask的用法了, 大家可以参考詹建飞写的“联网应用开发中的线程管理与界面更新”里面有详细介绍。 或者是参考这篇OPhone Developers Blog: Painless threading。 当然你也可以参考sdk的文档, 里面写的很详细。


          GridView动态更新效果实验:
  下面我就用AsyncTask来完成从网络动态下载图片,并且更新在我的GridView上的小实验。

        现在说一下我这个小实验的需求, 目标, 实现。

 需求: 从网络上下载各种图片, 并且动态显示在我的GridView上。
目标:展示AsyncTask的常见功能和动态显示的效果。
实现:利用AsyncTask和listener模式完成

下面是整个实验的代码实现, 我会一步步的展示给大家看:

1. 创建一个GridView Layout.

 

view plain copy to clipboard print ?
  1. <LinearLayout xmlns:OPhone= "http://schemas.OPhone.com/apk/res/OPhone"   
  2.     OPhone:orientation="vertical"  OPhone:layout_width= "fill_parent"   
  3.     OPhone:layout_height="fill_parent" >  
  4.     <GridView OPhone:id="@+id/showPhotoGridView"   
  5.         OPhone:layout_width="fill_parent"   
  6.             OPhone:layout_height="0dip"   
  7.             OPhone:layout_weight="1.0"   
  8.         OPhone:numColumns="3"  OPhone:verticalSpacing= "10dp"   
  9.         OPhone:horizontalSpacing="10dp"  OPhone:columnWidth= "90dp"   
  10.         OPhone:stretchMode="columnWidth"  OPhone:gravity= "center"  />  
  11. </LinearLayout>  
  12.    

2. 模拟从网上下载图片的Downloader程序:主要是模拟从网上下载图片, 当然你可以选择你想要下载的数量。 然后通过网络编程来获取URL信息。获得图片的bitmap。

 

view plain copy to clipboard print ?
  1. class  DownLoader {  
  2.     int  downloadNo =  0 ;  
  3.     private   static  DownLoader downloader =  new  DownLoader();  
  4.     private   static  String[] myImageURL =  null ;  
  5.     private  List<Photo> photos =  new  ArrayList<Photo>();  
  6.   
  7.     public   static  DownLoader getInstance() {  
  8.         initImageURL();  
  9.         return  downloader;  
  10.     }  
  11.   
  12.     /**  
  13.      * 这里模拟从网上下载100张图片  
  14.      */   
  15.     static   void  initImageURL() {  
  16.         int  no =  0 ;  
  17.         myImageURL = new  String[ 100 ];  
  18.         for  ( int  i =  0 ; i < myImageURL.length; i++) {  
  19.             myImageURL[i] = "http://cp.a8.com/image/128X128GIF/8"  + no +  ".gif" ;  
  20.             no++;  
  21.             if  (no %  10  ==  0 ) {  
  22.                 no = 0 ;  
  23.             }  
  24.         }  
  25.     }  
  26.   
  27.     /**  
  28.      * 返回一个Photo的List 注意这里注册了PhotoDownloadListener来监听每一张图片。   
  29.      * 如果有图片下载完了就更新的界面  
  30.      * @param listener 监听器  
  31.      * @return  
  32.      */   
  33.     public  List<Photo> downLoadImage(PhotoDownloadListener listener) {  
  34.         List<String> urls = Arrays.asList(myImageURL);  
  35.         List<Photo> photos = new  ArrayList<Photo>();  
  36.         URL aryURI = null ;  
  37.         URLConnection conn = null ;  
  38.         InputStream is = null ;  
  39.         Bitmap bm = null ;  
  40.         Photo photo = null ;  
  41.         for  (String url : urls) {  
  42.             try  {  
  43.                 aryURI = new  URL(url);  
  44.                 conn = aryURI.openConnection();  
  45.                 is = conn.getInputStream();  
  46.                 bm = BitmapFactory.decodeStream(is);  
  47.                 photo = new  Photo(bm);  
  48.                 // update listener   
  49.                 listener.onPhotoDownloadListener(photo);  
  50.                 photos.add(photo);  
  51.                 Log.i("downlaod no: " , ++downloadNo +  "" );  
  52.             } catch  (Exception e) {  
  53.                 throw   new  RuntimeException(e);  
  54.             } finally  {  
  55.                 try  {  
  56.                     if  (is !=  null )  
  57.                     is.close();  
  58.                 } catch  (IOException e) {  
  59.                     e.printStackTrace();  
  60.                 }  
  61.             }  
  62.         }  
  63.         return  photos;  
  64.     }  
  65. }  


3. 通过ImageAdapter使得Gridview获得图片位置和现实内容。 Adapter是OPhone中用来更新或者说用来配合那些特殊的View, 例如ListView, GridView来显示里面的内容的。里面有个notifyDataSetChanged()的方法可以告诉Adapter来更新自己。在Doc中描述如 下:Notifies the attached View that the underlying data has been changed and it should refresh itself.

 

view plain copy to clipboard print ?
  1. class  ImageAdapter  extends  BaseAdapter {  
  2.   
  3.     private  Context mContext;  
  4.     private  List<Photo> photos =  new  ArrayList<Photo>();  
  5.   
  6.     public  ImageAdapter(Context context) {  
  7.         this .mContext = context;  
  8.     }  
  9.   
  10.     public   void  addPhoto(Photo photo) {  
  11.         photos.add(photo);  
  12.     }  
  13.       
  14. //通过这个api来动态通知adapter需要更新界面   
  15.     @Override   
  16.     public   void  notifyDataSetChanged() {  
  17.         super .notifyDataSetChanged();  
  18.     }  
  19.       
  20.   
  21.     @Override   
  22.     public   int  getCount() {  
  23.         return  photos.size();  
  24.     }  
  25.   
  26.     @Override   
  27.     public  Object getItem( int  position) {  
  28.         return  photos.get(position);  
  29.     }  
  30.   
  31.     @Override   
  32.     public   long  getItemId( int  position) {  
  33.         return  position;  
  34.     }  
  35.   
  36.     @Override   
  37.     public  View getView( int  position, View convertView, ViewGroup parent) {  
  38.         if  (convertView ==  null ) {  
  39.             convertView = new  ImageView(mContext);;  
  40.         }   
  41.         ((ImageView)convertView).setImageBitmap(photos.get(position).getBm());  
  42.         return  convertView;  
  43.     }  
  44.   
  45. }  


4. 普通的pojo类, 注意里面应用了(onPhotoDownloadListener)Listener模式来通知Adapter, 一个图片download完了。 你可以更新了。当然这个是在后面得重点类AsyncTask中完成。 这里只是定义一个接口。

 

view plain copy to clipboard print ?
  1. class  Photo {  
  2.     private  Bitmap bm;  
  3.   
  4.     public  Photo(Bitmap bm) {  
  5.         this .bm = bm;  
  6.     }  
  7.   
  8.     public  Bitmap getBm() {  
  9.         return  bm;  
  10.     }  
  11.   
  12.     public   void  setBm(Bitmap bm) {  
  13.         this .bm = bm;  
  14.     }  
  15.   
  16.     interface  PhotoDownloadListener {  
  17.         public   void  onPhotoDownloadListener(Photo photo);  
  18.     }  
  19. }  


5. 利用AsyncTask进行多线程的下载图片, 并且利用adapter进行更新。
其中当有一个图片下载完了以后, Downloader就会调用onPhotoDownloadListener(),
DownloadPhotosTask看都有photo下载完了以后就调用publishProgress这个方法。 从而触发onProgressUpdate, 在这个方法里面去通知adapter有data更新了。

 

view plain copy to clipboard print ?
  1. class  DownloadPhotosTask  extends  AsyncTask<Void, Photo, Void>  implements   
  2.             PhotoDownloadListener {  
  3.   
  4.         @Override   
  5.         protected  Void doInBackground(Void... params) {  
  6.             DownLoader.getInstance().downLoadImage(this );  
  7.             return   null ;  
  8.         }  
  9.   
  10.         @Override   
  11.         public   void  onPhotoDownloadListener(Photo photo) {  
  12.             if  (photo !=  null  && !isCancelled()) {  
  13.                 //通过这个调用onProgressUpdate   
  14.                 publishProgress(photo);  
  15.             }  
  16.         }  
  17.   
  18.         @Override   
  19.         public   void  onProgressUpdate(Photo... photos) {  
  20.             for  (Photo photo : photos) {  
  21.                 imageAdapter.addPhoto(photo);  
  22.             }  
  23.             // 这个是通知adapter有新的photo update.   
  24.             imageAdapter.notifyDataSetChanged();  
  25.         }  
  26.   
  27.     }  


6. 最后 onCreate() 方法启动我们整个实验

 

view plain copy to clipboard print ?
  1. /** Called when the activity is first created. */   
  2.     @Override   
  3.     public   void  onCreate(Bundle savedInstanceState) {  
  4.         super .onCreate(savedInstanceState);  
  5.         setContentView(R.layout.main);  
  6.   
  7.         gridView = (GridView) findViewById(R.id.showPhotoGridView);  
  8.         button = (Button) findViewById(R.id.button);  
  9.           
  10.         imageAdapter = new  ImageAdapter( this );  
  11.         gridView.setAdapter(imageAdapter);  
  12.         button.setOnClickListener(new  View.OnClickListener() {  
  13.             @Override   
  14.             public   void  onClick(View v) {  
  15.                 button.setEnabled(false );  
  16.                 downloadPhotosTask = (DownloadPhotosTask) new  DownloadPhotosTask()  
  17.                         .execute();  
  18.             }  
  19.         });  
  20.     }  

        效果图如下:下面所有的图都可以动态更新。 有兴趣的朋友可以用传统的方式试试, 就可以知道一个好的UI用起来是多么的舒服。

 最后, 我会把这个小程序的所有相关的source code放在附件里面供大家参考。 Code里面同时包括了动态更新ListView的代码。 大家可以自己试试写一个动态跟新ListView的小程序。

总结:
希望通过这个实例可以帮助大家进一步的了解OPhone的AsyncTask的用法(有点类似于Java Swing 里面的SwingWorker.) 和如何编写出人机更为互动的界面。我会把全部源代码打包放在后面。 由于是第一篇教程,肯能存在较多的问题, 如果大家有所疑惑欢迎提出意见和问题。 我会及时更新, 完善我们的教程。

你可能感兴趣的:(【转】 AyncTask 实战 模拟GridView 动态更新效果)