天天看點

android平闆上的GridView視圖緩存優化

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視圖的優化了。

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,源碼如下:

[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+"項");  

繼續閱讀