天天看點

json解析,異步下載下傳(listview僅滑動時加載)Demo總結

異步加載的練習demo

主要涉及知識點:

1.解析json格式資料,主要包括圖檔,文本

2.使用asyntask異步方式從網絡下載下傳圖檔

3.baseadapter的“優雅”使用

4.使用lru緩存算法

5.改進加載:僅在listview滑動停止後才加載可見項,滑動中不加載

涉及到的知識點如上,這裡做一個小結,僅對一些代碼片段分析

1、異步加載

主要有倆個原因

【2】耗時操作阻塞ui線程(網絡下載下傳等 )

常用的倆種方式

【1】多線程/線程池

【2】asyntask (其實它底層也是線程池 核心線程數5,最大線程數128)

2、json數組解析

采用的是某網站的提供的接口(http://www.imooc.com/api/teacher?type=4&num=30)傳回的json資料如下

json解析,異步下載下傳(listview僅滑動時加載)Demo總結

<span style="font-family:microsoft yahei;">/* 

 * json資料bean,這裡擷取三個屬性 

 * 圖檔 

 * 标題 

 * 内容 

 */  

public class newsbean {  

    public string newsiconurl;// 圖檔的網址  

    public string newstitle;  

    public string newscontent;  

}</span>  

下面就開始解析json,并将解析的資料放到list<newsbean>,代碼片段在mainactivity.java中

     * 将url對應的json格式資料轉化為所封裝的newsbean 

     */  

    // 擷取json傳回格式資料  

    private list<newsbean> getjsondata(string url) {  

        list<newsbean> newsbeanlist = new arraylist<newsbean>();  

        try {  

            string jsonstring = readstream(new url(url).openstream());// 直接根據url擷取網絡資料傳回inputstream類型  

            jsonobject jsonobject;  

            newsbean newsbean;  

            // log.d("xsf", jsonstring); //列印測試  

            // 将json資料放入jsonobject中,然後通過jsonarray擷取所需要的資料集合  

            try {  

                jsonobject = new jsonobject(jsonstring);  

                jsonarray jsonarray = jsonobject.getjsonarray("data");  

                // 通過for循環取出jsonarray每個值,放到newsbean的集合中去  

                for (int i = 0; i < jsonarray.length(); i++) {  

                    jsonobject = jsonarray.getjsonobject(i);  

                    newsbean = new newsbean();  

                    newsbean.newsiconurl = jsonobject.getstring("picsmall");// 擷取小圖檔  

                    newsbean.newstitle = jsonobject.getstring("name");// 擷取title  

                    newsbean.newscontent = jsonobject.getstring("description");// 擷取内容  

                    newsbeanlist.add(newsbean);  

                }  

            } catch (jsonexception e) {  

                e.printstacktrace();  

            }  

        } catch (ioexception e) {  

            e.printstacktrace();  

        }  

        return newsbeanlist;  

    }  

</span>  

整個邏輯在 代碼注釋中很清楚,通過url下載下傳資料位string,先擷取最外一級jsonobj,然後擷取内部jsonarray數組,在for循環中子jsonobj,通過getstring擷取每個子jsonobj中的标簽對應的内容。(結合前面的json圖來看)最後統一放到newslist中,作為後面listview擴充卡apapter的資料源

這裡還涉及到java io流的操string jsonstring = readstream(new url(url).openstream());,主要是吧new url(url).openstream()得到的位元組流蹭蹭封裝成buffer(核心依然是裝飾者模式),然後拼接成string形式傳回,代碼片段在mainactivity.java中

<span style="font-family:microsoft yahei;">// 位元組流轉字元流,讀取函數,解析網頁傳回的數組  

    private string readstream(inputstream is) {  

        inputstreamreader isr;  

        string result = "";  

            string line = "";  

            isr = new inputstreamreader(is, "utf-8");// 位元組流封裝成字元流并且指定為utf-8格式  

            bufferedreader br = new bufferedreader(isr);// 将字元流通過buffer形式讀取出來  

            while ((line = br.readline()) != null) {  

                result += line;  

        } catch (unsupportedencodingexception e) {  

        return result;  

    }</span>  

3、asyntask異步加載

先來看看asynctask的定義:

public abstract class asynctask<params, progress, result> {  }三種泛型類型分别代表“啟動任務執行的輸入參數”、“背景任務執行的進度”、“背景計算結果的類型”。

在特定場合下,并不是所有類型都被使用,如果沒有被使用,可以用java.lang.void類型代替。一個異步任務的執行一般包括以下幾個步驟:

【1】.execute(params... params),執行一個異步任務,需要我們在代碼中調用此方法,觸發異步任務的執行。(在ui線程中執行,不可混淆)

以下幾個是asyntask繼承時可以重寫的函數

【2】.onpreexecute(),在execute(params... params)被調用後立即執行,一般用來在執行背景任務前對ui做一些标記。

【3】.doinbackground(params... params),在onpreexecute()完成後立即執行,用于執行較為費時的操作,此方法将接收輸入參數和傳回計算結果。在執行過程中可以調用publishprogress(progress... values)來更新進度資訊。

【4】.onprogressupdate(progress... values),在調用publishprogress(progress... values)時,此方法被執行,直接将進度資訊更新到ui元件上。

【5】.onpostexecute(result result),當背景操作結束時,此方法将會被調用,計算結果将做為參數傳遞到此方法中,直接将結果顯示到ui元件上。

在使用的時候,有幾點需要格外注意:

【1】.異步任務的執行個體必須在ui線程中建立。

【2】.execute(params... params)方法必須在ui線程中調用。

【3】.不要手動調用onpreexecute(),doinbackground(params... params),onprogressupdate(progress... values),onpostexecute(result result)這幾個方法。

【4】.不能在doinbackground(params... params)中更改ui元件的資訊。

【5】.一個任務執行個體隻能執行一次,如果執行第二次将會抛出異常。

在mainactivity.java中,通過該方式進行異步加載json資料

詳細見代碼注釋,邏輯比較簡單

在主線程中通過execute啟動asyntask

<code><code></code></code>

@override  

    protected void oncreate(bundle savedinstancestate) {  

        super.oncreate(savedinstancestate);  

        setcontentview(r.layout.activity_main);  

        // 擷取listview控件  

        mlistview = (listview) findviewbyid(r.id.tv_main);  

        // 在主線程中啟動asyntask  

        new newsasyntask().execute(url);  

    // 實作網絡的異步通路  

    /* 

     * asyntask 輸入三個參數 params,這裡需要傳入url網址,是以為string類型 progress 這裡不需要傳回進度顯示 

     * 是以為void result 為 json解析之後的資料bean的集合 

    class newsasyntask extends asynctask&lt;string, void, list&lt;newsbean&gt;&gt; {  

        // 通過擷取newsbean集合得到資料傳遞到adapter中,這樣可以顯示出每個json資料  

        @override  

        protected list&lt;newsbean&gt; doinbackground(string... params) {  

            return getjsondata(params[0]);// 這裡的參數隻有一個url網址  

        // 将生成的nesbean設定給listview  

        protected void onpostexecute(list&lt;newsbean&gt; newsbeans) {  

            super.onpostexecute(newsbeans);  

            newsadapter adapter = new newsadapter(mainactivity.this, newsbeans,  

                    mlistview);  

            mlistview.setadapter(adapter);  

4 baseadapter的“優雅”使用

主要包含以下幾點

1、自定義adpter繼承baseadpter;

2、定義變量:list&lt;newsbean&gt;;layoutinflater;

3、重寫構造函數newsadpter(context context, list&lt;newsbean&gt; data)。

4、文藝方式重寫getview()方法。

5、自定義類viewholder,映射相關的view對象

(擴充卡是架起資料到界面顯示的一座橋梁,普通listview的核心就是在擴充卡上下功夫)

在getview中通過viewholder儲存資料,結合settag來給viewholder打标簽,來解決listview滑動圖檔加載錯位的問題,代碼片段在newsadapter.java中

    public view getview(int position, view convertview, viewgroup parent) {  

        // viewholder方式  

        viewholder viewholder = null;  

        if (convertview == null) {  

            viewholder = new viewholder();  

            convertview = minflater.inflate(r.layout.item_layout, null);  

            // 對viewholder元素進行初始化  

            viewholder.ivicon = (imageview) convertview  

                    .findviewbyid(r.id.tv_icon);  

            viewholder.tvtitle = (textview) convertview  

                    .findviewbyid(r.id.tv_title);  

            viewholder.tvcontent = (textview) convertview  

                    .findviewbyid(r.id.tv_content);  

            convertview.settag(viewholder);  

        } else {  

            viewholder = (viewholder) convertview.gettag();  

        viewholder.ivicon.setimageresource(r.drawable.ic_launcher);  

        /* 

         * 防止listview加載圖檔出現錯位,非常重要 以下倆行代碼很重要,是以在imageloader擷取圖檔需要進行判斷 

         */  

        string url = mlist.get(position).newsiconurl;  

        viewholder.ivicon.settag(url);// 将圖檔和對應的url進行綁定  

        // 使用多線程方式加載實際圖檔  

         * new imageloadr().showimagebythread(viewholder.ivicon, url); 

        // 使用asynctask加載實際圖檔  

        // new imageloadr().showimagebyasynctask(viewholder.ivicon, url);  

        mimageloader.showimagebyasynctask(viewholder.ivicon, url);  

        viewholder.tvtitle.settext(mlist.get(position).newstitle);  

        viewholder.tvcontent.settext(mlist.get(position).newscontent);  

        return convertview;  

    class viewholder {  

        public textview tvtitle, tvcontent;  

        public imageview ivicon;  

這裡通過

string url = mlist.get(position).newsiconurl;  

con.settag(url);// 将圖檔和對應的url進行綁定  

然後調用asyntask異步方式,開始加載圖檔,關于加載圖檔這裡采用了lru算法,下面會分析。

5、優化的listview圖檔加載

通常在listview網絡加載圖檔時,我們通常會做這樣的處理: 僅在listview滑動停止後加載圖檔或者文字,這樣可以減少卡頓

實作邏輯:在listview的adapetr中實作onscrolllistener接口,需要重寫倆個函數

public void onscrollstatechanged(abslistview view, int scrollstate) 滾動狀态改變觸發,在這裡可以判斷滾動狀态進而确定是否需要加載

public void onscroll(abslistview view, int firstvisibleitem, int visibleitemcount, int totalitemcount)  一直都會觸發,可以記錄此時滾動的在螢幕中起始位置,便于加載處理,代碼片段在newsadapter.java中

    public void onscrollstatechanged(abslistview view, int scrollstate) {  

        // listview滑動狀态切換的時候才調用  

        // 判斷listview滾動狀态  

        if (scrollstate == scroll_state_idle) {  

            // 滾動停止狀态,加載可見項  

            mimageloader.loadimages(mstart, mend);  

            // 停止任務  

            mimageloader.cancelalltask();  

    @override  

    public void onscroll(abslistview view, int firstvisibleitem,  

            int visibleitemcount, int totalitemcount) {  

        // 整個滑動過程都會調用,不斷擷取目前可見項目和最後一個可見項目  

        mstart = firstvisibleitem;  

        mend = firstvisibleitem + visibleitemcount;  

        if (mfirrstin &amp;&amp; visibleitemcount &gt; 0) {  

            // 手動加載第一屏  

            mfirrstin = false;  

6、 圖檔下載下傳 核心代碼 imageloader.java

有以下幾個看點

對于從網絡上擷取圖檔這種需求,我們都要使用cache來将我們的圖檔緩存起來,尤其是對于listview這種,不能每次我們滑動listview就重新從網上下載下傳圖檔,這樣會很浪費資源而且浪費手機的流量。在android中,已經為我們提供了一個用于緩存的類lrucache。我們可以使用這個類來實作我們對于圖檔資源的緩存。(lrucache是将圖檔緩存在記憶體中,而還有個第三方的類disklrucache來将圖檔緩存到手機的disk上,而我們大型的app,一般都是将lrucache和disklrucache結合起來使用,形成一個memory hierarchy。)

【1】需要預設緩存占sd卡的大小 代碼片段在imageloader.java中

【2】添加到緩存  (可以通過url和bitmap的鍵值對方式關聯)

【3】從緩存擷取圖檔

lru本質就是linkhashmap,是以具備put get操作,lru這裡就不擴充開了,代碼片段如下

public imageloadr(listview listview) {  

        mlistview = listview;  

        mtask = new hashset&lt;imageloadr.imageloaderasyntask&gt;();  

        // 擷取最大可使用記憶體  

        int maxmemory = (int) runtime.getruntime().maxmemory();  

        // 設定所需緩存大小  

        int cachesize = maxmemory / 4;  

        mcaches = new lrucache&lt;string, bitmap&gt;(cachesize) {  

            @override  

            protected int sizeof(string key, bitmap value) {  

                // 在每次存入緩存時候調用,告訴系統傳入對象的大小  

                return value.getbytecount();  

        };  

    // 增加到緩存  

    public void addbitmaptocache(string url, bitmap bitmap) {  

        if (getbitmapfromcache(url) == null) {  

            // 判斷目前是否存在url所指定的圖檔  

            mcaches.put(url, bitmap);  

    // 從緩存中擷取資料  

    public bitmap getbitmapfromcache(string url) {  

        return mcaches.get(url);  

6.2、加載圖檔

使用了loadimages(start,end)該函數主要用來加載目前顯示listview從start到end的圖檔用來配合listview僅在活動停止後加載,假設此時滑動停止螢幕listview在12-20行之間則隻加載該區間的圖檔文本

原理:

【1】将start,end作為for循環,由于在newsadapter.java中已經記錄了所有的urls,因而string url = newsadapter.urls[i]   并且i在[start,end]之間,這樣就将url和start,end對應起來

【2】這樣同樣使用asyntask來加載圖檔,這裡使用一個asyntask集合來管理,當開始下載下傳時加入集合,下載下傳完成回調時在onpostexecute中将該asyntask從中remove掉

// 用來加載從start到end的所有圖檔  

public void loadimages(int start, int end) {  

    for (int i = start; i &lt; end; i++) {  

        string url = newsadapter.urls[i];// 擷取到了從start開始到end所有rul  

        bitmap bitmap = getbitmapfromcache(url);  

        if (bitmap == null) {  

            // 緩存中沒有該圖檔則直接加載  

            // new imageloaderasyntask(, url).execute(url);  

            imageloaderasyntask task = new imageloaderasyntask(url);  

            task.execute(url);  

            mtask.add(task);// 将該task儲存到目前活動task集合中  

            /* 

             * // 緩存中有該圖檔直接加載 // imageview.setimagebitmap(bitmap); 

             */  

            // 通過tag找到imageview  

            imageview imageview = (imageview) mlistview  

                    .findviewwithtag(url);  

            imageview.setimagebitmap(bitmap);  

}  

private class imageloaderasyntask extends asynctask&lt;string, void, bitmap&gt; {  

    // private imageview  

    // mimageview;不再需要,可以通過listview的findviewwithtag(url)找到imageview  

    // 防止listview圖檔加載錯位做的處理  

    private string murl;  

     * public imageloaderasyntask(imageview imageview, string url) { 

     * mimageview = imageview; murl = url; } 

    public imageloaderasyntask(string url) {  

        murl = url;  

    protected bitmap doinbackground(string... params) {  

        string url = params[0];  

        // 從網絡中擷取圖檔  

        bitmap bitmap = getbitmapfromurl(url);  

        if (bitmap != null) {  

            addbitmaptocache(url, bitmap);  

        return bitmap;  

    protected void onpostexecute(bitmap bitmap) {  

        super.onpostexecute(bitmap);  

        // 設定圖檔時增加判斷防止listview加載圖檔錯位  

         * if (mimageview.gettag().equals(murl)) { 

         * mimageview.setimagebitmap(bitmap); } 

        imageview imageview = (imageview) mlistview.findviewwithtag(murl);  

        if (imageview != null &amp;&amp; bitmap != null) {  

        // 設定完bitmap之後表明該task已經失去作用,需要從集合中移除  

        mtask.remove(this);  

}  

轉載:http://blog.csdn.net/xsf50717/article/details/49024075