http://blog.csdn.net/hellogv/article/details/6541286
最近在做android平闆上的開發,其中涉及到高分辨率之下使用gridview的性能問題。在android手機軟體開發中,如果在listview或者gridview上使用大數量item,很多人都會想到viewholder......沒錯,viewholder非常适合用在listview或者每行小于4個item的gridview。但是如果是高分辨率的裝置(android平闆甚至android電視),每行包含4個以上item的話,即使用了viewholder也依然卡。
如下圖,每行9個item,而且每個item的圖檔都是從網絡動态下載下傳的,這時就比較考驗gridview視圖的優化了。
本文提出的優化方法是:在getview()建構一個view清單(list<view>),把最近建構的view存起來,回退時直接從view清單中讀取,而不是動态建構。使用這種方法有2個好處:
1.快速讀取過去的item;
2.直接儲存view而不是bitmap,避免了imageview.setimagebitmaps()帶來的延時。
當然壞處就是浪費記憶體,是以要設定一個上限,超過了就删掉最老的item。
先來看看這種方法與viewholder的性能對比:
100個item往下滾到的三組資料對比,如上圖:
“cacheadapter 緩存50個item”跟viewholderadapter的速度很接近,由于cacheadapter有緩存,是以會有1~2次快速讀取item(10~20個)的情況,而viewholder的每次讀取item速度比較平均。
“cacheadapter 緩存75個item”隻在第一次往下滾動時消耗較長時間,第二次用了緩存的item,是以速度快了很多。
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,源碼如下:
[java] view
plaincopyprint?
/**
* 使用清單緩存過去的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) {
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
public class viewholderadapter extends baseadapter {
public viewholderadapter(context c) {
static class viewholder {
textview text;
imageview icon;
viewholder holder;
if (convertview == 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 });
bitmap bitmap = cacheadapter.getbitmapbyurl(url);
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+"項");