Android_ListView图片下载三级缓存处理
Posted 9编程小王子9
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android_ListView图片下载三级缓存处理相关的知识,希望对你有一定的参考价值。
转载表明出处:http://blog.csdn.net/zcr317121966/article/details/52146297
这次练习的是图片的三级缓存处理。主要是避免下载图片过大过多导致的内存泄露的问题,我们要将下载的图片进行缓存。缓存主要分三级,是首先存
储再强引用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<DataBean> 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<DataBean> 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<String,Void,List<DataBean>>
private static final String TAG = "===";
private InterfaceData interfaceData;
private List<DataBean> list = new ArrayList<>();
public AsyncTaskOfJson(InterfaceData interfaceData)
this.interfaceData = interfaceData;
@Override
protected List<DataBean> 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<DataBean> 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<String,Void,Bitmap>
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<DataBean> 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<DataBean> list = new ArrayList<>();
//用于接受传来的context上下文对象
private Context context;
//首先定义好要存储图片的SD卡的路径方便引用
private String path = Environment.getExternalStorageDirectory().getAbsolutePath()+"/zcr_xwz";
//声明强引用LruCache引用 范型为String此键用来存储图片对应网址作为键,bitmap对象为值
private LruCache<String,Bitmap> lrucache;
//创建一个HashMap对象 同理范型为String此键用来存储图片对应网址作为键,值为软引用并存储bitmap对象
private HashMap<String,SoftReference<Bitmap>> map = new HashMap<>();
//声明一个线程池用来同时下载多张图片
private Executor executor;
//构造方法传参 并初始化声明的引用
public MyBaseAdapter(Context context, List<DataBean> list)
this.context = context;
this.list = list;
//创建一个容量为3的线程池
executor = Executors.newFixedThreadPool(3);
//定义出强引用的最大大小为当前运行时内存的八分之一
int maxSize = (int) (Runtime.getRuntime().maxMemory()/8);
//创建强引用lrucache对象并指明大小和重写方法
lrucache = new LruCache<String,Bitmap>(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<Bitmap> bitmapSoftReference = new SoftReference<Bitmap>(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<Bitmap> 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布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.administrator.android_picturesave.MainActivity">
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:id="@+id/imageview"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@mipmap/ic_launcher"
android:scaleType="fitXY"
/>
<TextView
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/imageview"
android:textSize="30sp"
android:gravity="center_vertical"
android:layout_alignBottom="@+id/imageview"
android:layout_alignTop="@id/imageview"
/>
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.administrator.android_picturesave">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
</activity>
<activity android:name="com.bane.mymain.MyMainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
10.实现效果如图面和log打印显示,读取和存储的顺序可以见缓存效果。
以上是关于Android_ListView图片下载三级缓存处理的主要内容,如果未能解决你的问题,请参考以下文章