Android異步加載訪問網絡圖片-解析json

來自:http://www.imooc.com/video/7871
推薦大家去學習這個視頻,講解的很不錯。
慕課網提供了一個json網址可以用來學習:http://www.imooc.com/api/teacher?type=4&num=30。我們的任務就是建立一個listview,將json提供的一些參數,主要是name,picSmall,description顯示出來,效果圖如下:
這裏寫圖片描述
主要思路如下:listview中圖片的加載,程序中使用了兩種方式,一種是使用Thread類,一種是使用AsyncTask,爲了提高listview滑動的效率,並節省流量,使用了LruCache類,更改加載圖片的處理邏輯爲:當滑動listview時不加載圖片,停止滑動listview時加載界面可視範圍內的圖片。具體程序如下:
1 NewsBean.java listview每一項的封裝類

package com.example.imoocnews;

public class NewsBean {
    private String newsIconUrl;//圖片的網址即picSmall
    private String newsTitle;//圖片的標題即json中的name屬性
    private String newsContent;//圖片的內容即json中的description

    public NewsBean(String newsIconUrl, String newsTitle, String newsContent)
    {
        this.newsIconUrl = newsIconUrl;
        this.newsTitle = newsTitle;
        this.newsContent = newsContent;
    }
    public String getNewsIconUrl() {
        return newsIconUrl;
    }
    public void setNewsIconUrl(String newsIconUrl) {
        this.newsIconUrl = newsIconUrl;
    }
    public String getNewsTitle() {
        return newsTitle;
    }
    public void setNewsTitle(String newsTitle) {
        this.newsTitle = newsTitle;
    }
    public String getNewsContent() {
        return newsContent;
    }
    public void setNewsContent(String newsContent) {
        this.newsContent = newsContent;
    }


}

2 適配器NewsAdapter.java

package com.example.imoocnews;

import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.AbsListView;

/**
 * listview的適配器,包括上下文對象和數據源
 * 提高listview的效率:當listview滾動時不去加載可見項圖片,停止滾動後再開始加載
 */
public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener{

    private LayoutInflater mInflater;
    private List<NewsBean> mList;
    private ImageLoader mImageLoader;
    private int mStart, mEnd;//listview屏幕可視範圍內的第一條item和最後一個item
    public static String URLS[];//設置一個數組,用來保存所有圖片的URL
    private boolean mFirstIn;//判斷是否是第一次啓動程序

    public NewsAdapter(Context context, List<NewsBean> data, ListView listView) {
        mInflater = LayoutInflater.from(context);
        this.mList = data;
        mImageLoader = new ImageLoader(listView);//在這裏初始化,能夠保證只有一個imageloader的實例,即只有一個LruCache的實例
        URLS = new String[data.size()];
        for (int i = 0; i < data.size(); i++) {
            URLS[i] = data.get(i).getNewsIconUrl();//將data中的每一個URL賦值給數組
        }
        listView.setOnScrollListener(this);
        mFirstIn = true;//寫在構造函數中,第一次調用newsAdapter時表示第一次啓動程序,顯示listview
    }

    @Override
    public int getCount() {     
        return mList.size();
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return mList.get(position);
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.item, null);
            holder = new ViewHolder();
            holder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);
            holder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);
            holder.tvContent = (TextView) convertView.findViewById(R.id.tv_content);
            convertView.setTag(holder);
        }else {
            holder = (ViewHolder) convertView.getTag();
        }
        //holder.ivIcon.setImageResource(R.drawable.ic_launcher);
        String url = mList.get(position).getNewsIconUrl();
        holder.ivIcon.setTag(url);//給ImageView設置標誌,即對應的圖片網址 
        //利用thread類實現異步加載圖片,我們這裏將其註釋,使用AsyncTask的方式
        //new ImageLoader().showImageByThread(holder.ivIcon, mList.get(position).getNewsIconUrl());
        mImageLoader.showImages(holder.ivIcon, url);

        holder.tvTitle.setText(mList.get(position).getNewsTitle());
        holder.tvContent.setText(mList.get(position).getNewsContent());
        return convertView;
    }
    class ViewHolder
    {
        public ImageView ivIcon;
        public TextView tvTitle, tvContent;
    }
    /* 
     * 當listview滑動的時候調用
     */
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        mStart = firstVisibleItem;
        mEnd = mStart + visibleItemCount;
        //只在第一次加載的時候調用
        if (mFirstIn && visibleItemCount >0) {//表示第一次加載listview並且已經繪製了可見範圍內的item
            mImageLoader.loadImages(mStart, mEnd);
            mFirstIn = false;//加載圖片後即設爲false
        }
    }

    /* 
     * 當listview滑動狀態變化時調用
     */
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (scrollState == SCROLL_STATE_IDLE) {//listview停止滾動
            //加載可見項
            mImageLoader.loadImages(mStart, mEnd);

        }else {
            //停止加載任務
            mImageLoader.cancelAllTasks();
        }

    }

}

3 訪問圖片的實現ImageLoader.java

package com.example.imoocnews;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.LruCache;
import android.widget.ImageView;
import android.widget.ListView;

public class ImageLoader {
    private ImageView mImageView;
    private String url;
    //當圖片加載過後就將圖片緩存到本地,下次便不用重新聯網獲取,直接從本地緩存獲取即可,一個圖片即string url --> bitmap
    private LruCache<String, Bitmap> mCache;
    private ListView mListView;
    private Set<NewsAsyncTask> mTasks;//從start到end範圍每次執行加載圖片任務的集合

    public ImageLoader(ListView listView)
    {
        mListView = listView;
        mTasks = new HashSet<ImageLoader.NewsAsyncTask>();
        int maxMemory = (int) Runtime.getRuntime().maxMemory();//獲取最大可用內存
        int cacheSize = maxMemory/4;//設置緩存的大小
        mCache = new LruCache<String, Bitmap>(cacheSize){
            @Override
            protected int sizeOf(String key, Bitmap value) {
                // 每次將圖片存入緩存時返回圖片的大小
                return value.getByteCount();
            }
        };
    }
    /**
     * 將已聯網獲取成功的圖片加入到緩存中
     * @param bitmap
     */
    public void addBitmapToCache(String url, Bitmap bitmap)
    {
        //在將圖片緩存到本地之前要判斷這個圖片是否已經緩存過了
        if (getBitmapFromCache(url) == null) {
            mCache.put(url, bitmap);
        }
    }
    /**
     * 通過URL從緩存中取出相應的圖片
     */
    public Bitmap getBitmapFromCache(String url)
    {
        return mCache.get(url);
    }

    private Handler mHandler = new Handler(){
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //通過tag使得ImageView和它對應的URL綁定,這樣在上下滑動listview時ImageView顯示的圖片就始終是正確的
            //否則,由於listview的緩存機制,ImageView會先顯示出上次加載成功時的圖片,然後再顯示正確的圖片
            if (mImageView.getTag().equals(url)) {
                mImageView.setImageBitmap((Bitmap) msg.obj);//使用handler在主線程中更新UI,並將URL對應的圖片設置給控件imageview
            }

        }
    };

    /**
     * 通過使用Thread的方式從網絡上獲取圖片
     */
    public void showImageByThread(ImageView imageView, final String iconUrl)
    {
        mImageView = imageView;
        url = iconUrl;
        new Thread(){
            @Override
            public void run() {
                // 在新的進程中實現圖片的加載
                super.run();
                //從url中獲得bitmap,將bitmap發送給主線程
                Bitmap bitmap = getBitmapFromUrl(iconUrl);
                Message message = Message.obtain();
                message.obj = bitmap;
                mHandler.sendMessage(message);
            }
        }.start();
    }
    public Bitmap getBitmapFromUrl(String urlString)
    {
        InputStream is = null;
        Bitmap bitmap;
        try {
            URL url = new URL(urlString);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            is = new BufferedInputStream(connection.getInputStream());
            bitmap = BitmapFactory.decodeStream(is);
            connection.disconnect();
            //Thread.sleep(1000);
            return bitmap;
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally
        {
            try {
                is.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 加載listview可見範圍內的所有圖片
     * 
     */
    public void loadImages(int start, int end)
    {
        for (int i = start; i < end; i++) {
            String url = NewsAdapter.URLS[i];
            //看是否能從緩存中取出對應的圖片
            Bitmap bitmap = getBitmapFromCache(url);
            //如果緩存中沒有,就要對每個url執行異步加載任務去獲取圖片
            if (bitmap == null) {
                NewsAsyncTask task = new NewsAsyncTask(url);
                task.execute(url);
                mTasks.add(task);

            }else {
                //如果緩存中存在此圖片,直接將其設置給對應的imageview即可
                //因爲我們之前給imageview設置的tag就是URL,可以利用findViewWithTag直接在這裏獲取到
                ImageView imageView = (ImageView) mListView.findViewWithTag(url);
                imageView.setImageBitmap(bitmap);
            }
        }
    }
    /**
     * 取消所有正在進行的圖片加載任務
     */
    public void cancelAllTasks()
    {
        if (mTasks != null) {
            for(NewsAsyncTask task : mTasks)
            {
                task.cancel(false);
            }
        }
    }

    public void showImages(ImageView imageView, String iconUrl)
    {
        //是否能從緩存中取出對應的圖片
        Bitmap bitmap = getBitmapFromCache(iconUrl);
        if (bitmap == null) {
            imageView.setImageResource(R.drawable.ic_launcher);//顯示默認圖片
        }else {
            //如果緩存中存在此圖片,直接將其設置給對應的imageview即可
            imageView.setImageBitmap(bitmap);
        }

    }
    /**
     * 使用AsyncTask實現圖片的異步加載
     */
    class NewsAsyncTask extends AsyncTask<String, Void, Bitmap>
    {
        //private ImageView mImageView;
        private String mUrl;
        public NewsAsyncTask(String url) {
        //  mImageView = image;
            mUrl = url;
        }
        @Override
        protected Bitmap doInBackground(String... params) {
            String url = params[0];
            Bitmap bitmap = getBitmapFromUrl(url);//從網絡上得到圖片
            if (bitmap != null) {
                addBitmapToCache(url, bitmap);//獲取圖片成功將圖片存入緩存中
            }
            return bitmap;
        }
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            // 後臺獲取圖片的任務完成時調用此方法
            super.onPostExecute(bitmap);
            //給imageview設置圖片
            ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl);
            if (imageView != null && bitmap != null) {
                imageView.setImageBitmap(bitmap);
            }
            mTasks.remove(this);
        }
    }

}

4 MainActivity.java

package com.example.imoocnews;

import java.io.BufferedReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ListView;

public class MainActivity extends Activity {

    private ListView mListView;
    private static String jsonURL = "http://www.imooc.com/api/teacher?type=4&num=30";//json數據網址

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView = (ListView) findViewById(R.id.lv_main);
        new NewsAsyncTask().execute(jsonURL);
    }
    /**
     * 將URL網址上的json數據轉化爲我們所需的newsbean對象
     * @return
     */
    private List<NewsBean> getJsonData(String url)
    {
        List<NewsBean> newsBeanList = new ArrayList<NewsBean>();//保存解析出來的所有的數據
        try {
            //獲取到json字符串
            String jsonString = readStream(new URL(url).openStream());//和url.openConnection().getInputStream()一樣
            //Log.d("MainActivity", jsonString);
            //將獲取到的json字符串變爲jsonObject對象,打開網址可以看出data節點是一個jsonArray,array裏面存放了一個個的jsonObject
            NewsBean newsBean;
            JSONObject jsonObject;
            String newsUrl = null;
            String newsTitle = null;
            String newsContent = null;
            jsonObject = new JSONObject(jsonString);
            JSONArray jsonArray = jsonObject.getJSONArray("data");
            for (int i = 0; i < jsonArray.length(); i++) {
                jsonObject = jsonArray.getJSONObject(i);
                newsUrl = jsonObject.getString("picSmall");//圖片網址
                newsTitle = jsonObject.getString("name");//title
                newsContent = jsonObject.getString("description");//content
                newsBean = new NewsBean(newsUrl, newsTitle, newsContent);
                newsBeanList.add(newsBean);
            }
        } catch (MalformedURLException e) {         
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (JSONException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return newsBeanList;
    }

    /**
     * 解析網絡返回的數據
     */
    private String readStream(InputStream is)
    {
        InputStreamReader isReader;
        String result = "";
        String line = "";
        try {
            isReader = new InputStreamReader(is, "utf-8");//將字節流轉化爲字符流
            BufferedReader buffReader = new BufferedReader(isReader);//從字符輸入流中讀取文本,緩衝各個字符,從而實現字符、數組和行的高效讀取
            while ((line = buffReader.readLine()) != null) {
                result += line;
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return result;
    }
    /**
     * 構造一個AsyncTask,傳入String類型的URL,返回一個NewsBean對象,每一個對象就是
     * listview中的每一行數據,包括一個icon,title,content
     */
    class NewsAsyncTask extends AsyncTask<String, Void, List<NewsBean>>
    {

        @Override
        protected List<NewsBean> doInBackground(String... params) {         
            return getJsonData(params[0]);
        }

        @Override
        protected void onPostExecute(List<NewsBean> result) {
            super.onPostExecute(result);
            // 訪問網絡並解析json成功後返回結果,即我們設置的List<NewsBean>
            NewsAdapter adapter = new NewsAdapter(MainActivity.this, result, mListView);
            mListView.setAdapter(adapter);
        }

    }

}

源碼在這裏:http://download.csdn.net/detail/hnyzwtf/9418993

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章