首页
Web开发
Windows程序
编程语言
数据库
移动开发
系统相关
微信
其他好文
会员
首页
>
移动开发
> 详细
Android中用双缓存技术,加载网络图片
时间:
2016-08-19 20:50:40
阅读:
433
评论:
0
收藏:
0
[点我收藏+]
标签:
最近在学校参加一个比赛,写的一个Android应用,里面要加载大量的网络图片,可是用传统的方法图片一多就会造成程序出现内存溢出而崩溃.因为自己也在学习中,所以看了很多博客和视频,然后参照这些大神的写源码,自己写了一个加载网络图片工具类.
里面要用到一个经典的图片缓存库
DiskLruCache
下载地址为:
DiskLruCache下载
下面是使用这个类实现的 双缓存网络图片加载
[java]
view plain
copy
public
class DiskLruCacheUtils {
private
static DiskLruCacheUtils diskLruCacheUtils;
private DiskLruCache diskLruCache;
//LRU 磁盘缓存
private LruCache<String, Bitmap> lruCache;
//LRU 内存缓存
private Context context;
public DiskLruCacheUtils() {
}
public
static DiskLruCacheUtils getInstance() {
if (diskLruCacheUtils ==
null) {
diskLruCacheUtils =
new DiskLruCacheUtils();
}
return diskLruCacheUtils;
}
public
void open(Context context, String disk_cache_subdir,
int disk_cache_size) {
try {
this.context = context;
// 获取到可用内存的最大值,使用内存超出这个值会引起OutOfMemory异常。
// LruCache通过构造函数传入缓存值,以KB为单位。
int maxMemory = (
int) (Runtime.getRuntime().maxMemory() /
1024);
// 使用最大可用内存值的1/8作为缓存的大小。
int cacheSize = maxMemory /
8;
lruCache =
new LruCache<>(cacheSize);
/**
* open()方法接受四个参数:
* 第一个参数: 指定缓存地址
* 第二个参数: 指定当前引用程序的版本号
* 第三个参数: 指定同一个key可以对应多少个缓存文件,基本都是传1
* 第四个参数: 指定最多可以缓存的字节数. 通常是10MB
*/
diskLruCache = DiskLruCache.open(getCacheDir(disk_cache_subdir), getAppVersion(),
1, disk_cache_size);
}
catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取磁盘缓存
* @param url
* @return
*/
public InputStream getDiskCache(String url) {
String key = hashkeyForDisk(url);
try {
DiskLruCache.Snapshot snapshot = diskLruCache.get(key);
if (snapshot !=
null) {
return snapshot.getInputStream(
0);
}
}
catch (IOException e) {
e.printStackTrace();
}
return
null;
}
/**
* 下载图片并缓存到内存和磁盘中
* @param url
* @param callBack
*/
public
void putCache(
final String url,
final CallBack callBack){
new AsyncTask<String,Void,Bitmap>(){
@Override
protected Bitmap doInBackground(String... params) {
String key = hashkeyForDisk(params[
0]);
// System.out.println("Key = "+key);
DiskLruCache.Editor editor =
null;
Bitmap bitmap =
null;
URL url =
null;
try {
url =
new URL(params[
0]);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(
30*
1000);
conn.setConnectTimeout(
30*
1000);
ByteArrayOutputStream baos =
null;
if (conn.getResponseCode()==HttpURLConnection.HTTP_OK){
BufferedInputStream bis =
new BufferedInputStream(conn.getInputStream());
baos =
new ByteArrayOutputStream();
byte[] bytes =
new
byte[
1024];
int len = -
1;
while ((len = bis.read(bytes)) != -
1) {
baos.write(bytes,
0, len);
}
bis.close();
baos.close();
conn.disconnect();
}
if (baos !=
null){
bitmap = decodeSampleadBitmapFromStream(baos.toByteArray(),
300,
300);
// bitmap = BitmapFactory.decodeByteArray(baos.toByteArray(),0,baos.toByteArray().length);
addBitmapToCache(params[
0],bitmap);
// 添加到内存缓存
editor = diskLruCache.edit(key);
// 加入磁盘缓存
// System.out.println(url.getFile());
//位图压缩后输出(参数1: 压缩格式, 参数2: 质量(100 表示不压缩,30 表示压缩70%),参数3: 输出流)
bitmap.compress(Bitmap.CompressFormat.JPEG,
30,editor.newOutputStream(
0));
editor.commit();
//提交
}
}
catch (Exception e) {
try {
editor.abort();
//放弃写入
}
catch (IOException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
return bitmap;
}
@Override
protected
void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
callBack.response(bitmap);
}
}.execute(url);
}
/**
* 关闭磁盘缓存
*/
public
void close(){
if (diskLruCache!=
null&& !diskLruCache.isClosed()){
try {
diskLruCache.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 刷新磁盘缓存
*/
public
void flush(){
if (diskLruCache!=
null){
try {
diskLruCache.flush();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 回调接口
* @param <T>
*/
public
interface CallBack<T>{
public
void response(T entity);
}
/**
* 位图重新采样
*
* @param reqWidth 自定义的宽高
* @param reqHeight
* @return
*/
public
static Bitmap decodeSampleadBitmapFromStream(
byte[] bytes,
int reqWidth,
int reqHeight) {
BitmapFactory.Options options =
new BitmapFactory.Options();
options.inJustDecodeBounds =
true;
//只解析边界,不加载到内存中
BitmapFactory.decodeByteArray(bytes,
0, bytes.length, options);
options.inSampleSize = calculatInSampleSize(options, reqWidth, reqHeight);
//设置采样比为计算出的采样比例
options.inJustDecodeBounds =
false;
return BitmapFactory.decodeByteArray(bytes,
0, bytes.length, options);
//重新解析图片
}
//添加缓存的对象
public
void addBitmapToCache(String url,Bitmap bitmap){
String key = hashkeyForDisk(url);
if (getBitmapFromMenCache(key)==
null){
lruCache.put(key,bitmap);
}
}
//从缓存中获取对象
public Bitmap getBitmapFromMenCache(String url){
String key = hashkeyForDisk(url);
return lruCache.get(key);
}
/**
* 计算位图的采样比例大小
*
* @param options
* @param reqWidth 需要的宽高
* @param reqHeight
* @return
*/
private
static
int calculatInSampleSize(BitmapFactory.Options options,
int reqWidth,
int reqHeight) {
//获取位图的原宽高
final
int w = options.outWidth;
final
int h = options.outHeight;
int inSampleSize =
1;
//如果原图的宽高比需要的图片宽高大
if (w > reqWidth || h > reqHeight) {
if (w > h) {
inSampleSize = Math.round((
float) h / (
float) reqHeight);
}
else {
inSampleSize = Math.round((
float) w / (
float) reqWidth);
}
}
return inSampleSize;
}
/**
* MD5加密计算
*
* @param key
* @return
*/
private String hashkeyForDisk(String key) {
String cachekey;
try {
final MessageDigest mDigest = MessageDigest.getInstance(
"MD5");
mDigest.update(key.getBytes());
cachekey = bytesToHexString(mDigest.digest());
}
catch (NoSuchAlgorithmException e) {
cachekey = String.valueOf(key.hashCode());
}
return cachekey;
}
private String bytesToHexString(
byte[] bytes) {
StringBuilder sb =
new StringBuilder();
for (
int i =
0; i < bytes.length; i++) {
String hex = Integer.toHexString(
0xff & bytes[i]);
if (hex.length() ==
1) {
sb.append(
0);
}
sb.append(hex);
}
return sb.toString();
}
/**
* 获取缓存的地址
*
* @param name
* @return
*/
private File getCacheDir(String name) {
String cachePath = Environment.getExternalStorageState()
== Environment.MEDIA_MOUNTED || !Environment.isExternalStorageRemovable() ?
context.getExternalCacheDir().getPath() : context.getCacheDir().getPath();
return
new File(cachePath + File.separator + name);
}
/**
* 获取App的版本号
*
* @return
*/
private
int getAppVersion() {
try {
return context.getPackageManager().getPackageInfo(context.getPackageName(),
0).versionCode;
}
catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return
1;
}
}
decodeSampleadBitmapFromStream(byte[] bytes, int reqWidth, int reqHeight)这个函数的实现可以参照 郭大神的博客:
Android高效加载大图、多图方案,有效避免程序OOM
。
自己也是小白,好多都是复制粘贴,嘿嘿 ! 这里就不进行代码的分析了(其实好多我也不懂...),下面就自己上demo把:
现将上面的
DiskLruCache
, 在项目中创建一个libcore.io包,将这.jar文件复制进去,然后实现上边的代码(有点多哈!直接复制过去把!). 我这里直接创建了一个DiskLruCacheUtils类里面就是上面的代码! 还是截个图:
↓↓↓↓
使用这个工具类的方法:
在你需要的使用这类的Activity 或fragment中,首先:
[java]
view plain
copy
private DiskLruCacheUtils diskLruCacheUtils;
//创建对象
private
static
final String DISK_CACHE_SUBDIR =
"temp";
//设置图片缓存的文件
private
static
final
int DISK_CACHE_SIZE=
100*
1024*
1024;
// 设置SD卡缓存的大小
然后在他们的声明周期中:
[html]
view plain
copy
@Override
protected void onResume() {
super.onResume();
diskLruCacheUtils =
DiskLruCacheUtils.getInstance();
diskLruCacheUtils.open(this,DISK_CACHE_SUBDIR,DISK_CACHE_SIZE);//打开缓存
}
@Override
protected void onPause() {
super.onPause();
diskLruCacheUtils.flush(); //刷新缓存
}
@Override
protected void onStop() {
super.onStop();
diskLruCacheUtils.close(); //关闭缓存
}
加了这代码就可以真正的使用这个工具类了.
但是这样还不行,还得写个图片加载方法:
[html]
view plain
copy
private void loadBitmap(String url, final ImageView imageView) {
if (imageView.getTag().equals(url)) {
//从内存缓存中取图片
Bitmap
bitmap =
diskLruCacheUtils.getBitmapFromMenCache(url);
if (
bitmap == null) {
//如果内存中为空 从磁盘缓存中取
InputStream
in =
diskLruCacheUtils.getDiskCache(url);
if (
in == null) {
//如果缓存中都为空,就通过网络加载,并加入缓存
diskLruCacheUtils.putCache(url, new DiskLruCacheUtils.CallBack
<
Bitmap
>() {
@Override
public void response(Bitmap entity) {
// System.out.println("网络中下载...");
imageView.setImageBitmap(entity);
}
});
} else {
System.out.println("磁盘中取出...");
bitmap =
BitmapFactory.decodeStream(in);
diskLruCacheUtils.addBitmapToCache(url, bitmap);
imageView.setImageBitmap(bitmap);
}
} else {
// System.out.println("内存中取出...");
imageView.setImageBitmap(bitmap);
}
}
}
然后在你需要加载图片的地方使用该方法就OK, 看起复杂其实还挺简单的 ...(复制过去不就行了...)
直接上Demo:
这是activity_main.xml文件
下面上布局文件 挺简单的 RecyclerView+CardView:
[html]
view plain
copy
<?
xml
version=
"1.0"
encoding=
"utf-8"
?>
<
RelativeLayout
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:paddingBottom=
"@dimen/activity_vertical_margin"
android:paddingLeft=
"@dimen/activity_horizontal_margin"
android:paddingRight=
"@dimen/activity_horizontal_margin"
android:paddingTop=
"@dimen/activity_vertical_margin"
tools:context=
"zhengliang.com.bitmaplrucache.MainActivity"
>
<
android.support.v7.widget.RecyclerView
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:id=
"@+id/rlv_list"
>
</
android.support.v7.widget.RecyclerView
>
</
RelativeLayout
>
挺简单的就一个 RecyclerView 因为要加载很多图片所以就用这个了,(哈哈! 我喜欢他的瀑布流! 爽到爆炸啊...)
这是item.xml文件
[html]
view plain
copy
<?
xml
version=
"1.0"
encoding=
"utf-8"
?>
<
android.support.v7.widget.CardView
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_margin=
"2dp"
app:cardBackgroundColor=
"@color/colorAccent"
app:cardCornerRadius=
"2dp"
android:background=
"@color/colorAccent"
>
<
ImageView
android:id=
"@+id/pic"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:scaleType=
"centerCrop"
/>
</
android.support.v7.widget.CardView
>
就一个CardView ,里面放了一个ImageView
MainActivity类中代码如下:
因为这里没有图片资源所以自己用Volley框架写了一个获取图片资源的getImageUrl()方法里面返回一些图片资源的URL地址 (找图片真的很恼火啊,一条一条的把图片地址复制过来不是我的风范啊! 就在百度图片中经过千辛万苦扒了个图片API接口下来,哈哈 有图片咯!)
[java]
view plain
copy
<pre name=
"code"
class=
"java">
public
class MainActivity
extends AppCompatActivity{
private List<String> data;
private DiskLruCacheUtils diskLruCacheUtils;
private
static
final String DISK_CACHE_SUBDIR =
"temp";
private
static
final
int DISK_CACHE_SIZE=
100*
1024*
1024;
private RecyclerView rlvlist;
private MyAdapter myAdapter;
@Override
protected
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
getImageUrl(
"http://image.baidu.com/channel/listjson?pn=0&rn=200&tag1=美女&tag2=小清新&ie=utf8");
}
private
void initViews() {
data =
new ArrayList<String>();
this.rlvlist = (RecyclerView) findViewById(R.id.rlv_list);
rlvlist.setLayoutManager(
new StaggeredGridLayoutManager(
4,StaggeredGridLayoutManager.VERTICAL));
}
@Override
protected
void onResume() {
super.onResume();
diskLruCacheUtils = DiskLruCacheUtils.getInstance();
diskLruCacheUtils.open(
this,DISK_CACHE_SUBDIR,DISK_CACHE_SIZE);
}
@Override
protected
void onPause() {
super.onPause();
diskLruCacheUtils.flush();
}
@Override
protected
void onStop() {
super.onStop();
diskLruCacheUtils.close();
}
public
void getImageUrl(String url){
final RequestQueue mQueue = Volley.newRequestQueue(
this);
JsonObjectRequest stringRequest =
new JsonObjectRequest(url,
null,
new Response.Listener<JSONObject>() {
@Override
public
void onResponse(JSONObject jsonObject) {
// System.out.println(jsonObject);
try {
JSONArray jsonArray = jsonObject.getJSONArray(
"data");
for (
int i =
0; i <jsonArray.length() ; i++) {
JSONObject item = jsonArray.getJSONObject(i);
String url = item.getString(
"image_url");
String name = item.getString(
"tags");
data.add(url);
myAdapter =
new MyAdapter(data,MainActivity.
this,diskLruCacheUtils);
rlvlist.setAdapter(myAdapter);
myAdapter.notifyDataSetChanged();
}
}
catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
@Override
public
void onErrorResponse(VolleyError volleyError) {
}
}
);
mQueue.add(stringRequest);
if (data.size()==
200){
getImageUrl(
"http://image.baidu.com/channel/listjson?pn=0&rn=200&tag1=美女&tag2=全部&ie=utf8");
}
}
public
void getImageUrl2(String url){
final RequestQueue mQueue = Volley.newRequestQueue(
this);
JsonObjectRequest stringRequest =
new JsonObjectRequest(url,
null,
new Response.Listener<JSONObject>() {
@Override
public
void onResponse(JSONObject jsonObject) {
// System.out.println(jsonObject);
try {
JSONArray jsonArray = jsonObject.getJSONArray(
"imgs");
for (
int i =
0; i <jsonArray.length() ; i++) {
JSONObject item = jsonArray.getJSONObject(i);
String url = item.getString(
"hoverURL");
String name = item.getString(
"fromPageTitle");
data.add(url);
myAdapter =
new MyAdapter(data,MainActivity.
this,diskLruCacheUtils);
rlvlist.setAdapter(myAdapter);
myAdapter.notifyDataSetChanged();
}
}
catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
@Override
public
void onErrorResponse(VolleyError volleyError) {
}
}
);
mQueue.add(stringRequest);
}
}
然后是就是实现RecyclerView 的Adapter,因为网络图片的加载都要在Adapter中,所以loadBitmap()方法我就直接写在这里了 废话少说直接上代码
[java]
view plain
copy
public
class MyAdapter
extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private List<String> list;
private Context context;
private DiskLruCacheUtils diskLruCacheUtils;
public MyAdapter(List<String> list, Context context, DiskLruCacheUtils diskLruCacheUtils) {
this.list = list;
this.context = context;
this.diskLruCacheUtils = diskLruCacheUtils;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_view,parent,
false);
return
new ViewHolder(view);
}
@Override
public
void onBindViewHolder(ViewHolder holder,
int position) {
holder.pic.setTag(list.get(position));
loadBitmap(list.get(position),holder.pic);
System.out.println(position);
}
@Override
public
int getItemCount() {
return list==
null?
0:list.size();
}
public
static
class ViewHolder
extends RecyclerView.ViewHolder {
public ImageView pic;
public ViewHolder(View itemView) {
super(itemView);
pic = (ImageView) itemView.findViewById(R.id.pic);
}
}
private
void loadBitmap(String url,
final ImageView imageView) {
if (imageView.getTag().equals(url)) {
//从内存缓存中取图片
Bitmap bitmap = diskLruCacheUtils.getBitmapFromMenCache(url);
if (bitmap ==
null) {
//如果内存中为空 从磁盘缓存中取
InputStream in = diskLruCacheUtils.getDiskCache(url);
if (in ==
null) {
//如果缓存中都为空,就通过网络加载,并加入缓存
diskLruCacheUtils.putCache(url,
new DiskLruCacheUtils.CallBack<Bitmap>() {
@Override
public
void response(Bitmap entity) {
// System.out.println("网络中下载...");
imageView.setImageBitmap(entity);
}
});
}
else {
System.out.println(
"磁盘中取出...");
bitmap = BitmapFactory.decodeStream(in);
diskLruCacheUtils.addBitmapToCache(url, bitmap);
imageView.setImageBitmap(bitmap);
}
}
else {
// System.out.println("内存中取出...");
imageView.setImageBitmap(bitmap);
}
}
}
}
大工告成 看看效果
第一进入时全是从 显示"网络中下载..." 因为RecyclerView和ListView一样,超出屏幕的Item都会被回收,当再次滑动回到上次的位置就会重新获取item,并且会重新获取图片,.
下面看看滑动回去打印的log
全是显示从内存中取出,并没有再重网络中下载,说明刚才的图片都缓存到内存中了,这样就加快的图片的显示,还节省了流量!(这年头流量伤不起啊!)
下面再看看关闭应用再打开是什么效果吧!
全部显示的是从磁盘中取出... 因为关闭应用,这个时候内存中缓存的图片就会被清空. 这个时候就会自动看SD中是否有缓存了. 并且从SD中取出的图片会再一次缓存到内存中去...我这里是加载的200张图片,完全没有问题,嘿嘿...
Android中用双缓存技术,加载网络图片
标签:
原文地址:http://www.cnblogs.com/yukewei/p/5788852.html
踩
(
0
)
赞
(
0
)
举报
评论
一句话评论(
0
)
登录后才能评论!
分享档案
更多>
2021年07月29日 (22)
2021年07月28日 (40)
2021年07月27日 (32)
2021年07月26日 (79)
2021年07月23日 (29)
2021年07月22日 (30)
2021年07月21日 (42)
2021年07月20日 (16)
2021年07月19日 (90)
2021年07月16日 (35)
周排行
更多
关闭苹果IOS app自动更新
2021-07-29
开发一个即时通讯App
2021-07-28
iOS 跳转App Store进行评分
2021-07-26
诺基亚短信生成!太好玩了
2021-07-26
【Azure 应用服务】App Service 配置 Application Settings 访问Storage Account得到 could not be resolved: '*.file.core.windows.net'的报错。没有解析成对应中国区 Storage Account地址 *.file.core.chinacloudapi.cn
2021-07-26
Android系统编程入门系列之界面Activity响应丝滑的传统动画
2021-07-26
uniapp h5,app两端复制文本
2021-07-22
uni-app滚动视图容器(scroll-view)之监听上拉事件
2021-07-21
新型横向移动工具原理分析、代码分析、优缺点以及检测方案
2021-07-19
Android系统编程入门系列之界面Activity交互响应
2021-07-19
友情链接
兰亭集智
国之画
百度统计
站长统计
阿里云
chrome插件
新版天听网
关于我们
-
联系我们
-
留言反馈
© 2014
mamicode.com
版权所有 联系我们:gaon5@hotmail.com
迷上了代码!