android平板上的GridView视图缓存优化

guizhongyun 2012-04-12

本文来自http://blog.csdn.net/hellogv/ ,引用必须注明出处!

      最近在做android平板上的开发,其中涉及到高分辨率之下使用GridView的性能问题。在Android手机软件开发中,如果在ListView或者GridView上使用大数量Item,很多人都会想到ViewHolder......没错,ViewHolder非常适合用在ListView或者每行小于4个Item的GridView。但是如果是高分辨率的设备(android平板甚至android电视),每行包含4个以上Item的话,即使用了ViewHolder也依然卡。

      如下图,每行9个Item,而且每个Item的图片都是从网络动态下载的,这时就比较考验GridView视图的优化了。

android平板上的GridView视图缓存优化

      本文提出的优化方法是:在getView()构建一个View列表(List<View>),把最近构建的View存起来,回退时直接从View列表中读取,而不是动态构建。使用这种方法有2个好处:

1.快速读取过去的Item;

2.直接保存View而不是Bitmap,避免了ImageView.setImageBitmaps()带来的延时。

当然坏处就是浪费内存,所以要设定一个上限,超过了就删掉最老的Item。先来看看这种方法与ViewHolder的性能对比:

android平板上的GridView视图缓存优化

100个Item往下滚到的三组数据对比,如上图:

“CacheAdapter缓存50个Item”跟ViewHolderAdapter的速度很接近,由于CacheAdapter有缓存,所以会有1~2次快速读取Item(10~20个)的情况,而ViewHolder的每次读取Item速度比较平均。

“CacheAdapter 缓存75个Item”只在第一次往下滚动时消耗较长时间,第二次用了缓存的Item,所以速度快了很多。

android平板上的GridView视图缓存优化

100个Item往上滚到的三组数据对比,如上图:

“CacheAdapter 缓存50个Item”比ViewHolderAdapter的速度略快,“CacheAdapter 缓存75个Item”依然是最快的。总结:“CacheAdapter 缓存50个Item”速度与HolderView略快,读取最近的Item速度最快,缓存的Item越多速度越快。“CacheAdapter 缓存75个Item”占用内存最少,这是由于一部分图片下载失败,保存的Item的图片为空,实际上是缓存越多Item占用的内存越多。

PS:这里用到异步读取网络图片,成功下载的就占用较多内存,下载失败就占用较少内存,所以内存占用情况并不是一个时刻的绝对值,占用内存只用于参考.....

本文程序源码可以到http://www.rayfile.com/zh-cn/files/5ebf5666-958a-11e0-99ec-0015c55db73d/这里下载。

CacheAdapter.java是实现缓存Item的自定义Adapter,源码如下:

/**
 


 * 使用列表缓存过去的Item
 


 * @author hellogv
 


 * 
 


 */
  



public
 
class
 CacheAdapter 
extends
 BaseAdapter {  



  


    public
 
class
 Item {  



        public
 String itemImageURL;  



        public
 String itemTitle;  



        public
 Item(String itemImageURL, String itemTitle) {  



            this
.itemImageURL = itemImageURL;  



            this
.itemTitle = itemTitle;  



        }  


    }  


  


    private
 Context mContext;  



    private
 ArrayList<Item> mItems = 
new
 ArrayList<Item>();  



    LayoutInflater inflater;  


    public
 CacheAdapter(Context c) {  



        mContext = c;  


        inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  


    }  


  


    public
 
void
 addItem(String itemImageURL, String itemTitle) {  



        mItems.add(new
 Item(itemImageURL, itemTitle));  



    }  


  


    public
 
int
 getCount() {  



        return
 mItems.size();  



    }  


  


    public
 Item getItem(
int
 position) {  



        return
 mItems.get(position);  



    }  


  


    public
 
long
 getItemId(
int
 position) {  



        return
 position;  



    }  


      


    List<Integer> lstPosition=new
 ArrayList<Integer>();  



    List<View> lstView=new
 ArrayList<View>();  



      


    List<Integer> lstTimes= new
 ArrayList<Integer>();  



    long
 startTime=
0
;  



    public
 View getView(
int
 position, View convertView, ViewGroup parent) {  



        startTime=System.nanoTime();  


          


        if
 (lstPosition.contains(position) == 
false
) {  



            if
(lstPosition.size()>
75
)
//这里设置缓存的Item数量
  



            {  


                lstPosition.remove(0
);
//删除第一项
  



                lstView.remove(0
);
//删除第一项
  



            }  


            convertView = inflater.inflate(R.layout.item, null
);  



            TextView text = (TextView) convertView.findViewById(R.id.itemText);  


            ImageView icon = (ImageView) convertView.findViewById(R.id.itemImage);  


            text.setText(mItems.get(position).itemTitle);  


            new
 AsyncLoadImage().execute(
new
 Object[] { icon,mItems.get(position).itemImageURL });  



              


            lstPosition.add(position);//添加最新项
  



            lstView.add(convertView);//添加最新项
  



        } else
  



        {  


            convertView = lstView.get(lstPosition.indexOf(position));  


        }  


          


        int
 endTime=(
int
) (System.nanoTime()-startTime);  



        lstTimes.add(endTime);  


        if
(lstTimes.size()==
10
)  



        {  


            int
 total=
0
;  



            for
(
int
 i=
0
;i<lstTimes.size();i++)  



                total=total+lstTimes.get(i);  


      


            Log.e("10个所花的时间:"
 +total/
1000
 +
" μs"
,  



                    "所用内存:"
+Runtime.getRuntime().totalMemory()/
1024
 +
" KB"
);  



            lstTimes.clear();  


        }  


          


        return
 convertView;  



    }  


  


    /**
 


     * 异步读取网络图片
 


     * @author hellogv
 


     */
  



    class
 AsyncLoadImage 
extends
 AsyncTask<Object, Object, Void> {  



        @Override
  



        protected
 Void doInBackground(Object... params) {  



  


            try
 {  



                ImageView imageView=(ImageView) params[0
];  



                String url=(String) params[1
];  



                Bitmap bitmap = getBitmapByUrl(url);  


                publishProgress(new
 Object[] {imageView, bitmap});  



            } catch
 (MalformedURLException e) {  



                Log.e("error"
,e.getMessage());  



                e.printStackTrace();  


            } catch
 (IOException e) {  



                Log.e("error"
,e.getMessage());  



                e.printStackTrace();  


            }  


            return
 
null
;  



        }  


  


        protected
 
void
 onProgressUpdate(Object... progress) {  



            ImageView imageView = (ImageView) progress[0
];  



            imageView.setImageBitmap((Bitmap) progress[1
]);           



        }  


    }  


  


    static
 
public
 Bitmap getBitmapByUrl(String urlString)  



            throws
 MalformedURLException, IOException {  



        URL url = new
 URL(urlString);  



        URLConnection connection = url.openConnection();  


        connection.setConnectTimeout(25000
);  



        connection.setReadTimeout(90000
);  



        Bitmap bitmap = BitmapFactory.decodeStream(connection.getInputStream());  


        return
 bitmap;  



    }  


}  

其中if(lstPosition.size()>75)是设置缓存的Item数量的关键地方,这里缓存75个Item。

ViewHolderAdapter.java是实现ViewHolder加载Item的自定义Adapter,源码如下:

/**
 


 * 使用ViewHolder加载Item
 


 * @author hellogv
 


 * 
 


 */
  



public
 
class
 ViewHolderAdapter 
extends
 BaseAdapter {  



  


    public
 
class
 Item {  



        public
 String itemImageURL;  



        public
 String itemTitle;  



  


        public
 Item(String itemImageURL, String itemTitle) {  



            this
.itemImageURL = itemImageURL;  



            this
.itemTitle = itemTitle;  



        }  


    }  


  


    private
 Context mContext;  



    private
 ArrayList<Item> mItems = 
new
 ArrayList<Item>();  



    LayoutInflater inflater;  


    public
 ViewHolderAdapter(Context c) {  



        mContext = c;  


        inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  


    }  


  


    public
 
void
 addItem(String itemImageURL, String itemTitle) {  



        mItems.add(new
 Item(itemImageURL, itemTitle));  



    }  


      


    public
 
int
 getCount() {  



        return
 mItems.size();  



    }  


  


    public
 Item getItem(
int
 position) {  



        return
 mItems.get(position);  



    }  


  


    public
 
long
 getItemId(
int
 position) {  



        return
 position;  



    }  


      


    static
 
class
 ViewHolder {  



        TextView text;  


        ImageView icon;  


    }  


      


    List<Integer> lstTimes= new
 ArrayList<Integer>();  



    long
 startTime=
0
;  



    public
 View getView(
int
 position, View convertView, ViewGroup parent) {  



        startTime=System.nanoTime();  


          


        ViewHolder holder;  


  


        if
 (convertView == 
null
) {  



            convertView = inflater.inflate(R.layout.item, null
);  



            holder = new
 ViewHolder();  



            holder.text = (TextView) convertView.findViewById(R.id.itemText);  


            holder.icon = (ImageView) convertView.findViewById(R.id.itemImage);  


            convertView.setTag(holder);  


        } else
 {  



            holder = (ViewHolder) convertView.getTag();  


        }  


        holder.text.setText(mItems.get(position).itemTitle);  


        new
 AsyncLoadImage().execute(
new
 Object[]{holder.icon,mItems.get(position).itemImageURL });  



          


        int
 endTime=(
int
) (System.nanoTime()-startTime);  



        lstTimes.add(endTime);  


        if
(lstTimes.size()==
10
)  



        {  


            int
 total=
0
;  



            for
(
int
 i=
0
;i<lstTimes.size();i++)  



                total=total+lstTimes.get(i);  


      


            Log.e("10个所花的时间:"
 +total/
1000
 +
" μs"
,  



                    "所用内存:"
+Runtime.getRuntime().totalMemory()/
1024
 +
" KB"
);  



            lstTimes.clear();  


        }  


          


        return
 convertView;  



    }  


  


    /**
 


     * 异步读取网络图片
 


     * @author hellogv
 


     */
  



    class
 AsyncLoadImage 
extends
 AsyncTask<Object, Object, Void> {  



        @Override
  



        protected
 Void doInBackground(Object... params) {  



  


            try
 {  



                ImageView imageView=(ImageView) params[0
];  



                String url=(String) params[1
];  



                Bitmap bitmap = CacheAdapter.getBitmapByUrl(url);  


                publishProgress(new
 Object[] {imageView, bitmap});  



            } catch
 (MalformedURLException e) {  



                e.printStackTrace();  


            } catch
 (IOException e) {  



                e.printStackTrace();  


            }  


            return
 
null
;  



        }  


  


        protected
 
void
 onProgressUpdate(Object... progress) {  



            ImageView imageView = (ImageView) progress[0
];  



            imageView.setImageBitmap((Bitmap) progress[1
]);  



        }  


    }  


  


}  

testPerformance.java是主程序,通过注释符就可以分别测试CacheAdapter与ViewHolderAdapter的性能,源码如下:

public
 
class
 testPerformance 
extends
 Activity {  



    /** Called when the activity is first created. */
  



    @Override
  



    public
 
void
 onCreate(Bundle savedInstanceState) {  



        super
.onCreate(savedInstanceState);  



        setContentView(R.layout.main);  


        this
.setTitle(
"android平板上的GridView视图缓存优化-----hellogv"
);  



        GridView gridview = (GridView) findViewById(R.id.gridview);  


        CacheAdapter adapter=new
 CacheAdapter(
this
);  



        // ViewHolderAdapter adapter=new ViewHolderAdapter(this);
  



         


        gridview.setAdapter(adapter);  


        String urlImage=""
;
//请自己选择网络上的静态图片
  



          


        for
(
int
 i=
0
;i<
100
;i++)  



        {  


            adapter.addItem(urlImage, "第"
+i+
"项"
);  



        }  


          


    }  


}  

相关推荐

sgafdsg / 0评论 2011-01-24