Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android_ListView圖片下載三級緩存處理

Android_ListView圖片下載三級緩存處理

編輯:關於Android編程

這次練習的是圖片的三級緩存處理。主要是避免下載圖片過大過多導致的內存洩露的問題,我們要將下載的圖片進行緩存。緩存主要分三級,是首先存儲再強引用LruCache中,存不下之後存在軟引用SoftRerences中,同時也會存在本地的SD卡中。由於LruCache和SoftRerences是存在運行時內存中,雖然容量下,但是讀取快,而且程序一旦退出,數據就會丟失。同時本地內存卡中可以存入大量數據,但是讀取慢,但是退出程序後數據任然存在本地,不會丟失。所以結合雙方不同的特性我們將其結合起來使用,所以進行圖片的三級緩存處理。

步驟:首先會從內存和本地中取出圖片,有的話可以直接顯示,如果沒有則就要通過網絡下載圖片,並存入到內存和本地中。

取出的順序,先從LruCache中讀取,如果有直接顯示,沒有的話從SoftRerences中找,有的話顯示,沒有就去本地SD卡中尋找,有的話顯示,如果也沒有那麼就只能通過網絡下載圖片。下載圖片後存入順序,先存如到LruCache和本地中,如果LruCache存滿,則將系統自動刪除的圖片存入到SoftReferences中。

LruCache:

1.再創建對象時可以指定存儲容量一般為當前運行時內存的1/8或者1/16。

2.當存滿後會自動刪除之前數據並存入新數據。

3.自身是一個相當於map集合可以存儲n組鍵值對。

SoftRerences:

1.沒有最大存儲大小,存儲大小可以根據當前設備調整。

2.如果內存也存儲滿了,也會自動刪除數據,保證能獲取新的數據。

3.本身只能存儲一個對象。兩者配合使用可以存儲更多數據。

接下來介紹以下將網絡上的數據和圖片下載後顯示到listview中的緩存操作,詳細見代碼。

主要分為:

1.MyMainActivity主函數類主要用於初始化控件,網絡下載數據初始化數據源並設置listview適配器。

2.AsyncTaskOfJson異步任務解析JSON數據類 主要通過url下載數據後json解析得到數據源。

3.AsyncTaskOfBitmap異步任務下載Bitmap圖片類主要是通過網址下載bitmap圖片並通過接口回調返回。

4.MyInterface接口定義接口方法,通過調用接口方法回傳數據。

5.DataBean對象類用於存儲對應json解析的數據存儲類。

6.HttpURLUtils類網絡請求下載工具類主要結合異步任務類進行下載操作。

7.MyBaseAdapter自定義適配器類主要是listview適配器對網絡下載的數據圖片進行緩存處理,讀取操作。

 

1.MyMainActivity主函數類

package com.bane.mymain;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.ListView;
import com.bane.adapter.MyBaseAdapter;
import com.bane.asynctaskjson.AsyncTaskOfJson;
import com.bane.bean.DataBean;
import com.bane.myinterface.InterfaceData;
import com.example.administrator.android_picturesave.R;

import java.util.ArrayList;
import java.util.List;

//主函數類初始化控件,設置適配器和開啟網絡下載數據的任務
public class MyMainActivity extends AppCompatActivity {


    //tag標簽
    private static final String TAG = "===";

    //創建一個list集合存儲下載的數據
    private List listData = new ArrayList<>();

    //網址數據為JSON
    private String path = "http://lib.wap.zol.com.cn/ipj/docList/?v=6.0&class_id=0&page=4&vs=and412&retina=1";

    //聲明適配器引用
    private MyBaseAdapter adapter ;

    //聲明listview引用
    private ListView listview;

    //初始化控件方法
    private void assignViews() {
        listview = (ListView) findViewById(R.id.listview);
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my_main);

        //初始化控件
        assignViews();

        //開啟網絡下載異步任務
        startAsyncTask();

    }

    private void startAsyncTask() {

        //創建異步任務下載數據
        new AsyncTaskOfJson(new InterfaceData(){

            @Override
            public void getDataOfJson(List data) {
                if(data!=null){
                    //通過接口回調的到數據之後加入都list集合中存儲
                    listData.addAll(data);

                    Log.i(TAG, "getDataOfJson: "+listData.size());
                    //數據回傳回來之後進行適配器的初始化
                    initAdapter();

                }
            }
        }).execute(path);

    }

    private void initAdapter() {

        //創建適配器對象
        adapter = new MyBaseAdapter(this,listData);

        //設置適配器
        listview.setAdapter(adapter);

        //刷新適配器
        adapter.notifyDataSetChanged();
    }

}
2.AsyncTaskOfJson異步任務解析JSON數據類

 

 

package com.bane.asynctaskjson;

import android.os.AsyncTask;
import android.util.Log;

import com.bane.bean.DataBean;
import com.bane.httputils.HttpURLUtils;
import com.bane.myinterface.InterfaceData;

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

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Bane on 2016/8/7 0007.
 * 異步任務類
 * 用於下載path對應網址的數據得到相應的文字和圖片對應的網址
 * (詳細解析見上篇博文代碼)
 */
public class AsyncTaskOfJson extends AsyncTask>{

    private static final String TAG = "===";

    private InterfaceData interfaceData;

    private List list = new ArrayList<>();

    public AsyncTaskOfJson(InterfaceData interfaceData){

        this.interfaceData = interfaceData;

    }

    @Override
    protected List doInBackground(String... params) {

        Log.i(TAG, "doInBackground:params "+params[0]);

        String json = HttpURLUtils.getDataOfString(params[0]);

        Log.i(TAG, "doInBackground:json "+json);

        try {
            JSONObject ob = new JSONObject(json);

            JSONArray array = ob.getJSONArray("list");

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

                JSONObject obj = array.getJSONObject(i);
                String stitle = obj.getString("stitle");
                String imgsrc2 = obj.getString("imgsrc2");

                Log.i(TAG, "doInBackground:stitle, imgsrc2"+stitle+","+imgsrc2);

                DataBean dataBean = new DataBean(stitle,imgsrc2);
                list.add(dataBean);

            }

            Log.i(TAG, "doInBackground: list.size()"+list.size());

            return list;

        } catch (JSONException e) {
            Log.i(TAG, "doInBackground: JSONException=================");
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPostExecute(List result) {
        super.onPostExecute(result);

        if(result!=null){

            interfaceData.getDataOfJson(result);
        }
    }
}


3.AsyncTaskOfBitmap異步任務下載Bitmap圖片類

 

 

package com.bane.asynctaskbitmap;


import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;

import com.bane.httputils.HttpURLUtils;
import com.bane.myinterface.InterfaceBitmap;

/**
 * Created by Bane on 2016/8/7 0007.
 * 異步任務類
 * 用於下載網址對應的圖片 並用接口回調返回bitmap對象
 * (詳細解析見上篇博文代碼)
 */
public class AsyncTaskOfBitmap extends AsyncTask{

    private InterfaceBitmap interfaceBitmap;
    private String url;

    public AsyncTaskOfBitmap(InterfaceBitmap interfaceBitmap){

        this.interfaceBitmap = interfaceBitmap;

    }

    @Override
    protected Bitmap doInBackground(String... params) {

        url = params[0];

        byte[] buf = HttpURLUtils.getDataOfByte(url);

        Bitmap bitmap = BitmapFactory.decodeByteArray(buf,0,buf.length);

        return bitmap;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {

        if(bitmap != null){

            interfaceBitmap.getDataOfBitmap(url,bitmap);
        }
    }
}


 

4.MyInterface接口

 

package com.bane.myinterface;

import android.graphics.Bitmap;

/**
 * Created by Bane on 2016/8/7 0007.
 * 將得到的bitmap對象和對應網址回傳
 */
public interface InterfaceBitmap {


    public void getDataOfBitmap(String url,Bitmap bitmap);
}
package com.bane.myinterface;

import com.bane.bean.DataBean;

import java.util.List;

/**
 * Created by Bane on 2016/8/7 0007.
 * JSON解析數據回傳接口
 */
public interface InterfaceData {

    public void getDataOfJson(List list);
}



 

5.DataBean對象類

 

package com.bane.bean;

/**
 * Created by Bane on 2016/8/7 0007.
 * bean類  根據JSON數據類型定義的類 存儲對應數據
 */
public class DataBean {


    private String stitle;
    private String imgsrc2;

    public DataBean(String stitle, String imgsrc2) {
        this.stitle = stitle;
        this.imgsrc2 = imgsrc2;
    }

    public String getStitle() {
        return stitle;
    }

    public String getImgsrc2() {
        return imgsrc2;
    }

    public void setStitle(String stitle) {
        this.stitle = stitle;
    }

    public void setImgsrc2(String imgsrc2) {
        this.imgsrc2 = imgsrc2;
    }

    @Override
    public String toString() {
        return "DataBean{" +
                "stitle='" + stitle + '\'' +
                ", imgsrc2='" + imgsrc2 + '\'' +
                '}';
    }
}


 

6.HttpURLUtils類網絡請求下載工具類

 

package com.bane.httputils;

import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * Created by Bane on 2016/8/7 0007.
 * 封裝網絡請求工具類用於可以調用類中靜態方法
 * 分別返回網址對應的String數據和byte[]類型數據
 * (詳細解析見上篇博文代碼)
 */
public class HttpURLUtils {

    private static final String TAG = "===";

    public static String getDataOfString(String path){
        StringBuffer sb =null;

        try {
            URL url = new URL(path);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            conn.setRequestMethod("GET");
            conn.setConnectTimeout(100000);
            conn.connect();

            if(conn.getResponseCode() == HttpURLConnection.HTTP_OK){

                InputStream is = conn.getInputStream();

                byte[] buf = new byte[1024];

                int len = 0;

                sb = new StringBuffer();

                while ((len = is.read(buf))!=-1){

                    String info = new String(buf,0,len);
                    sb.append(info);

                }

//                Log.i(TAG, "getDataOfString: "+sb.toString());

                return sb.toString();
            }

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }


    public static byte[] getDataOfByte(String path){

        ByteArrayOutputStream baos = null;
        try {
            URL url = new URL(path);

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            conn.setRequestMethod("GET");
            conn.setConnectTimeout(100000);
            conn.connect();

            if(conn.getResponseCode() == HttpURLConnection.HTTP_OK){

                InputStream is = conn.getInputStream();

                byte[] buf = new byte[1024];

                int len = 0;

                baos = new ByteArrayOutputStream();


                while ((len = is.read(buf))!=-1){

                    baos.write(buf,0,len);
                    baos.flush();

                }

//                Log.i(TAG, "getDataOfString: "+baos.toByteArray());

                return baos.toByteArray();

            }

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}

 

7.MyBaseAdapter自定義適配器類

 

package com.bane.adapter;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.util.Log;
import android.util.LruCache;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.bane.asynctaskbitmap.AsyncTaskOfBitmap;
import com.bane.bean.DataBean;
import com.bane.myinterface.InterfaceBitmap;
import com.example.administrator.android_picturesave.R;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * Created by Administrator on 2016/8/7 0007.
 *
 * 適配器類
 * 用於處理網絡上下載的圖片的緩存的操作
 * 主要是分三級緩存對網絡上的數據進行處理
 * 以防止圖片過大導致的內存洩露
 */
public class MyBaseAdapter  extends BaseAdapter{

    //log標簽
    private static final String TAG = "===";
    //創建一個list集合用於接受構造函數傳來的list數據的集合
    private List list = new ArrayList<>();

    //用於接受傳來的context上下文對象
    private Context context;

    //首先定義好要存儲圖片的SD卡的路徑方便引用
    private String path = Environment.getExternalStorageDirectory().getAbsolutePath()+"/zcr_xwz";

    //聲明強引用LruCache引用 范型為String此鍵用來存儲圖片對應網址作為鍵,bitmap對象為值
    private LruCache lrucache;

    //創建一個HashMap對象 同理范型為String此鍵用來存儲圖片對應網址作為鍵,值為軟引用並存儲bitmap對象
    private HashMap> map = new HashMap<>();

    //聲明一個線程池用來同時下載多張圖片
    private Executor executor;

    //構造方法傳參 並初始化聲明的引用
    public MyBaseAdapter(Context context, List list) {

        this.context = context;
        this.list = list;

        //創建一個容量為3的線程池
        executor = Executors.newFixedThreadPool(3);

        //定義出強引用的最大大小為當前運行時內存的八分之一
        int maxSize = (int) (Runtime.getRuntime().maxMemory()/8);

        //創建強引用lrucache對象並指明大小和重寫方法
        lrucache = new LruCache(maxSize){


            /**
             * 當LruCache刪除圖片時就會運行此方法
             * @param evicted 是否為系統自動刪除
             * @param key      被刪除的鍵
             * @param oldValue 被刪除的值
             * @param newValue 新加入數據
             * @return
             */
            @Override
            protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
                super.entryRemoved(evicted, key, oldValue, newValue);

                //判斷是否被系統自動刪除
                if(evicted) {

                    //將刪除的圖片存入軟引用中
                    SoftReference bitmapSoftReference = new SoftReference(oldValue);
                    map.put(key,bitmapSoftReference);
                    Log.i(TAG, "saveBitmap: 往SoftReference中存數據!!!!");
                }
            }

            /**
             *  參數代表要新加入LruCache的鍵值對
             * @param key 加入數據的鍵
             * @param value 加入數據的值
             * @return  一般返回新加入的數據(圖片)的大小
             */
            @Override
            protected int sizeOf(String key, Bitmap value) {

                return value.getByteCount();
            }
        };
    }
    //重寫適配器中的方法返回對應list集合中的數據
    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int position) {
        return list.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        //聲明ViewHolder的應用為空
        ViewHolder mHolder =null;


        if(convertView==null){
            //判斷如果該position下的view不存在則自主創建一個
            mHolder = new ViewHolder();

            //加載寫好的適配器的布局文件
            convertView = LayoutInflater.from(context).inflate(R.layout.listview_layout,parent,false);

            //通過convertView初始化控件對象
            mHolder.textView = (TextView) convertView.findViewById(R.id.textview);
            mHolder.imageView = (ImageView) convertView.findViewById(R.id.imageview);

            //將創建的mHolder對象作為tag唯一標識便於復用
            convertView.setTag(mHolder);

        }else{
            //一旦判斷view對象存在 進行復用通過tag找到對應控件對象不再創建新的對象
            mHolder = (ViewHolder) convertView.getTag();

        }

        //此時將對應position下的控件設置屬性值

        //文字為得到的list中的數據
        mHolder.textView.setText(list.get(position).getStitle());

        //將每個imageview對應所放的圖片的網址作為tag標識
        mHolder.imageView.setTag(list.get(position).getImgsrc2());

        //在數據下載回來之前要先默認將圖片設置為小機器人圖片
        mHolder.imageView.setImageResource(R.mipmap.ic_launcher);

        //通過getBitmap方法獲取圖片 傳入參數是相應的網址tag唯一標示取得bitmap對象
        Bitmap bitmap = getBitmap(list.get(position).getImgsrc2());

        if(bitmap ==null){
            //若取得的bitmap對象為空則圖片從未下載過進行loadBitmap下載圖片
            //傳入的參數是當前的position和復用的mHolder對象
            loadBitmap(position,mHolder);

        }else{
            //若能從強或者軟引用或者sd卡中取得圖片則已經下載過直接將取得的圖片設置
            mHolder.imageView.setImageBitmap(bitmap);
        }

        //返回加載了布局文件的view對象
        return convertView;
    }

    //創建一個ViewHolder類用來listview中每個position下的view的復用
    class ViewHolder{

        private ImageView imageView;
        private TextView textView;

    }


    //下載圖片的方法,將下載的圖片設置再imageview上顯示
    public void loadBitmap(int position , final ViewHolder holder){

        //開啟異步任務下載圖片
        new AsyncTaskOfBitmap(new InterfaceBitmap() {
            @Override
            public void getDataOfBitmap(String url, Bitmap bitmap) {

                //通過接口對調回傳bitmap對象和對應網址

                //為防止復用導致的切換圖片現象
                //將每次復用的holder做出判斷是否是網址對應的imageview如是則下載之後設置
                if(holder.imageView.getTag().toString().equals(url)&&holder.imageView.getTag()!=null){

                    Log.i(TAG, "getDataOfBitmap: 下載圖片");
                    //將下載好的bitmap設置再imageview上
                    holder.imageView.setImageBitmap(bitmap);

                    //將下載好的圖片通過對應網址作為鍵保存
                    saveBitmap(url,bitmap);
                }
            }

            //在線程池中進行3張圖片同時下載並通過list數據集合和position得到相對應的網址
        }).executeOnExecutor(executor,list.get(position).getImgsrc2());


    }


    //存儲圖片的方法將圖片網址作為鍵bitmap對象為值
    public void saveBitmap(String url,Bitmap bitmap){
        //首先是存入到強引用lrucache中,lrucache本身可以存儲多組鍵值對
        lrucache.put(url,bitmap);

        Log.i(TAG, "saveBitmap: 往lruCache中存儲數據!!!!");

        //由於網址對應的 / 會造成多級文件夾所以替換為 _
        String urlName = url.replace("/","_");

        //創建文件夾
        File file = new File(path);

        if(!file.exists()){
            file.mkdirs();
        }


        try {
            //之後也要將下載好的圖片通過io流存入到本地的內存卡中
            //以便於當程序關閉之後再次開啟時能從本地讀取圖片
            FileOutputStream fos = new FileOutputStream(file+"/"+urlName);

            //將bitmap對象壓縮存入對應路徑的內存卡中
            bitmap.compress(Bitmap.CompressFormat.PNG,100,fos);
            Log.i(TAG, "saveBitmap: 往內存卡中存儲數據!!!!");


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    //從緩存中獲取bitmap對象的方法
    public Bitmap getBitmap(String url){

        //首先是通過對應網址為鍵從運行時內存中找
        Bitmap result =  lrucache.get(url);
        //若有會返回

        if(result ==null){
            //若為空再從軟引用中找
            SoftReference soft = map.get(url);
            if(soft !=null){
                //若存在則result獲取對應bitmap對象
                result = soft.get();
                Log.i(TAG, "getBitmap:SoftReference中取出數據: ");
            }else {
                //若沒有 再從本地內存卡中讀取
                result = BitmapFactory.decodeFile(path+"/"+url.replace("/","_"));
                if(result!=null) {
                    Log.i(TAG, "getBitmap:從內存卡中取出數據");
                }

                //若都沒有證明圖片從未下載過

            }
        }else{
            Log.i(TAG, "getBitmap:lrucache中取出數據");
        }

        //將得到的bitmap對象返回
        return result;
    }
}


 

8.xml布局文件MyMainActivity和MyBaseAdapter布局文件

 




    





    
    


 

 

9.xml清單配置文件,添加網絡請求訪問權限和本地SD卡讀寫權限。

 




    
    
    

    
        

        
        
            
                

                
            
        
    



 

10.實現效果如圖面和log打印顯示,讀取和存儲的順序可以見緩存效果。

\

\

 

\

\

\

\

\

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved