经常会用到 网络文件 比如查看大图片数据 资源优化的问题,当然用开源的项目 Android-Universal-Image-Loader 或者 ignition 都是个很好的选择。
在这里把原来 写过的优化的代码直接拿出来,经过测试千张图片效果还是不错的。
免费培训课:http://www.jinhusns.com/Products/Curriculum/?type=xcj
工程目录
至于 Activity 就是加载了 1个网格布局
class="number">01.
/**
02.
* 实现 异步加载 和 2级缓存
03.
*/
04.
public
class
ImagedownActivity
extends
Activity {
05.
06.
public
static
String filepath;
07.
@Override
08.
public
void
onCreate(Bundle savedInstanceState) {
09.
super
.onCreate(savedInstanceState);
10.
setContentView(R.layout.main);
11.
filepath =
this
.getCacheDir().getAbsolutePath();
12.
GridView gv=(GridView)findViewById(R.id.gridview01);
13.
//设置列数
14.
gv.setNumColumns(
3
);
15.
//配置适配器
16.
gv.setAdapter(
new
Myadapter(
this
));
17.
}
18.
19.
@Override
20.
protected
void
onDestroy() {
21.
// TODO Auto-generated method stub
22.
//activity 销毁时,清除缓存
23.
MyImageLoader.removeCache(filepath);
24.
super
.onDestroy();
25.
}
26.
27.
}
接下来 Myadapter.java(给网格每个item塞入图片 )在生成每个 item 异步请求网络获取image
01.
public
class
Myadapter
extends
BaseAdapter {
02.
private
Context context;
03.
private
String root =
"http://192.168.0.100:8080/Android_list/"
;
04.
private
String[] URLS;
05.
private
final
MyImageLoader myImageLoader =
new
MyImageLoader(context);;
06.
07.
/**
08.
* adapter 初始化的时候早一堆数据
09.
* 这里我请求的是自己搭的服务器
10.
* @param context
11.
*/
12.
public
Myadapter(Context context){
13.
this
.context =context;
14.
URLS =
new
String[
999
];
15.
for
(
int
i =
0
; i <
999
; i++) {
16.
URLS[i] = root + (i+
1
)+
".jpg"
;
17.
}
18.
}
19.
20.
21.
@Override
22.
public
int
getCount() {
23.
return
URLS.length;
24.
}
25.
26.
@Override
27.
public
Object getItem(
int
position) {
28.
return
URLS[position];
29.
}
30.
31.
@Override
32.
public
long
getItemId(
int
position) {
33.
return
URLS[position].hashCode();
34.
}
35.
36.
37.
38.
@Override
39.
public
View getView(
int
position, View view, ViewGroup parent) {
40.
ImageView imageView;
41.
if
(view==
null
){
42.
imageView=
new
ImageView(context);
43.
imageView.setLayoutParams(
new
GridView.LayoutParams(
200
,
190
));
44.
imageView.setAdjustViewBounds(
false
);
45.
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
46.
imageView.setPadding(
5
,
5
,
5
,
5
);
47.
}
else
{
48.
imageView=(ImageView)view;
49.
}
50.
myImageLoader.downLoad(URLS[position], (ImageView)imageView , context);
51.
return
imageView;
52.
}
53.
}
MyImageLoader.java
001.
public
class
MyImageLoader {
002.
003.
//最大内存
004.
final
static
int
memClass = (
int
) Runtime.getRuntime().maxMemory();
005.
private
Context context;
006.
007.
// 是否缓存到硬盘
008.
private
boolean
diskcache =
true
;
009.
010.
// 定义一级 缓存的图片数
011.
private
static
final
int
catch_num =
10
;
012.
013.
// 定义二级缓存 容器 软引用
014.
private
static
ConcurrentHashMap<String, SoftReference<Bitmap>> current_hashmap =
new
ConcurrentHashMap<String, SoftReference<Bitmap>>();
015.
016.
// 定义一级缓存容器 强引用 (catch_num ,0.75f,true) 默认参 数 2.加载因子默认 3.排序模式 true
017.
private
static
LinkedHashMap<String, Bitmap> link_hashmap =
new
LinkedHashMap<String, Bitmap>(catch_num ,
0
.75f,
true
) {
018.
019.
// 必须实现的方法
020.
protected
boolean
removeEldestEntry(java.util.Map.Entry<String, Bitmap> eldest) {
021.
/** 当一级缓存中 图片数量大于 定义的数量 放入二级缓存中
022.
*/
023.
if
(
this
.size() > catch_num) {
024.
// 软连接的方法 存进二级缓存中
025.
current_hashmap.put(eldest.getKey(),
new
SoftReference<Bitmap>(
026.
eldest.getValue()));
027.
//缓存到本地
028.
cancheToDisk(eldest.getKey(),eldest.getValue() );
029.
030.
return
true
;
031.
}
032.
return
false
;
033.
};
034.
};
035.
036.
public
MyImageLoader(Context context) {
037.
038.
}
039.
040.
041.
/**
042.
* 外部调用此方法 进行下载图片
043.
*/
044.
public
void
downLoad(String key , ImageView imageView,Context context){
045.
// 先从缓存中找 。
046.
context =
this
.context;
047.
048.
Bitmap bitmap = getBitmapFromCache(key);
049.
if
(
null
!= bitmap){
050.
imageView.setImageBitmap(bitmap);
051.
cancleDownload(key, imageView);
//取消下载
052.
return
;
053.
}
054.
055.
// 缓存中 没有 把当前的 imageView 给他 得到 task
056.
if
(cancleDownload(key, imageView)){
//没有任务进行。,。。开始下载
057.
ImageDownloadTask task =
new
ImageDownloadTask(imageView);
058.
Zhanwei_Image zhanwei_image =
new
Zhanwei_Image(task);
059.
//先把占位的图片放进去
060.
imageView.setImageDrawable(zhanwei_image);
061.
// task执行任务
062.
task.execute(key);
063.
}
064.
}
065.
066.
067.
/** 此方法 用于优化 : 用户直接 翻到 哪个 就先加载 哪个、
068.
* @param key - URL
069.
* @param imageView - imageView
070.
* core: 给当前的 imageView 得到给他下载的 task
071.
*/
072.
073.
private
boolean
cancleDownload(String key,ImageView imageView){
074.
// 给当前的 imageView 得到给他下载的 task
075.
ImageDownloadTask task = getImageDownloadTask(imageView);
076.
if
(
null
!= task){
077.
String down_key = task.key;
078.
if
(
null
== down_key || !down_key.equals(key)){
079.
task.cancel(
true
);
// imageview 和 url 的key不一样 取消下载
080.
}
else
{
081.
return
false
;
//正在下载:
082.
}
083.
}
084.
return
true
;
//没有正在下载
085.
}
086.
087.
088.
089.
// public void getThisProcessMemeryInfo() {
090.
// int pid = android.os.Process.myPid();
091.
// android.os.Debug.MemoryInfo[] memoryInfoArray = activityManager.getProcessMemoryInfo(new int[] {pid});
092.
// System.out.println("本应用当前使用了" + (float)memoryInfoArray[0].getTotalPrivateDirty() / 1024 + "mb的内存");
093.
// }
094.
095.
096.
097.
/**
098.
* 从缓存中得到 图片的方法 1.先从一级 缓存找 linkhashmap 不是线程安全的 必须要加同步
099.
*/
100.
public
Bitmap getBitmapFromCache(String key) {
101.
//1.先在一级缓存中找
102.
synchronized
(link_hashmap) {
103.
Bitmap bitmap = link_hashmap.get(key);
104.
if
(
null
!= bitmap) {
105.
link_hashmap.remove(key);
106.
// 按照 LRU是Least Recently Used 近期最少使用算法 内存算法 就近 就 原则 放到首位
107.
link_hashmap.put(key, bitmap);
108.
System.out.println(
" 在缓存1中找图片了 ="
+key);
109.
return
bitmap;
110.
}
111.
}
112.
113.
// 2. 到二级 缓存找
114.
SoftReference<Bitmap> soft = current_hashmap.get(key);
115.
if
(soft !=
null
) {
116.
//得到 软连接 中的图片
117.
Bitmap soft_bitmap = soft.get();
118.
if
(
null
!= soft_bitmap) {
119.
System.out.println(
" 在缓存2中找图片了 ="
+key);
120.
return
soft_bitmap;
121.
}
122.
}
else
{
123.
// 没有图片的话 把这个key删除
124.
current_hashmap.remove(key);
125.
}
126.
127.
128.
//3.都没有的话去从外部缓存文件读取
129.
if
(diskcache){
130.
Bitmap bitmap = getBitmapFromFile(key);
131.
if
(bitmap!=
null
){
132.
link_hashmap.put(key, bitmap);
//将图片放到一级缓存首位
133.
return
bitmap;
134.
}
135.
}
136.
137.
return
null
;
138.
}
139.
140.
141.
/**
142.
* 缓存到本地文件
143.
* @param key
144.
* @param bitmap
145.
*/
146.
public
static
void
cancheToDisk(String key ,Bitmap bitmap ){
147.
//2.缓存bitmap至/data/data/packageName/cache/文件夹中
148.
try
{
149.
String fileName = getMD5Str(key);
150.
String filePath = ImagedownActivity.filepath +
"/"
+ fileName;
151.
System.out.println(
"缓存到本地==="
+ filePath);
152.
FileOutputStream fos =
new
FileOutputStream(filePath);
153.
bitmap.compress(Bitmap.CompressFormat.JPEG,
100
, fos);
154.
155.
}
catch
(Exception e) {
156.
157.
}
158.
}
159.
160.
161.
/**
162.
* 从外部文件缓存中获取bitmap
163.
* @param url
164.
* @return
165.
*/
166.
private
Bitmap getBitmapFromFile(String url){
167.
Bitmap bitmap =
null
;
168.
String fileName = getMD5Str(url);
169.
if
(fileName ==
null
){
170.
return
null
;
171.
}
172.
String filePath = ImagedownActivity.filepath +
"/"
+ fileName;
173.
try
{
174.
FileInputStream fis =
new
FileInputStream(filePath);
175.
bitmap = BitmapFactory.decodeStream(fis);
176.
System.out.println(
"在本地缓存中找到图片==="
+ filePath);
177.
}
catch
(FileNotFoundException e) {
178.
System.out.println(
"getBitmapFromFile==="
+ e.toString());
179.
e.printStackTrace();
180.
bitmap =
null
;
181.
}
182.
return
bitmap;
183.
}
184.
185.
186.
187.
/**
188.
* 清理文件缓存
189.
* @param dirPath
190.
* @return
191.
*/
192.
public
static
boolean
removeCache(String dirPath) {
193.
File dir =
new
File(dirPath);
194.
File[] files = dir.listFiles();
195.
if
(files ==
null
|| files.length ==
0
) {
196.
return
true
;
197.
}
198.
int
dirSize =
0
;
199.
//这里删除所有的缓存
200.
int
all_ = (
int
) (
1
* files.length +
1
);
201.
//对files 进行排序
202.
Arrays.sort(files,
new
FileLastModifiedSort());
203.
for
(
int
i =
0
; i < all_ ; i++) {
204.
files[i].delete();
205.
}
206.
return
true
;
207.
}
208.
209.
210.
/**
211.
* 根据文件最后修改时间进行排序
212.
*/
213.
private
static
class
FileLastModifiedSort
implements
Comparator<File> {
214.
@Override
215.
public
int
compare(File lhs, File rhs) {
216.
if
(lhs.lastModified() > rhs.lastModified()) {
217.
return
1
;
218.
}
else
if
(lhs.lastModified() == rhs.lastModified()) {
219.
return
0
;
220.
}
else
{
221.
return
-
1
;
222.
}
223.
}
224.
}
225.
226.
227.
/**
228.
* MD5 加密
229.
*/
230.
private
static
String getMD5Str(String str) {
231.
MessageDigest messageDigest =
null
;
232.
try
{
233.
messageDigest = MessageDigest.getInstance(
"MD5"
);
234.
messageDigest.reset();
235.
messageDigest.update(str.getBytes(
"UTF-8"
));
236.
}
catch
(NoSuchAlgorithmException e) {
237.
System.out.println(
"NoSuchAlgorithmException caught!"
);
238.
return
null
;
239.
}
catch
(UnsupportedEncodingException e) {
240.
e.printStackTrace();
241.
return
null
;
242.
}
243.
244.
byte
[] byteArray = messageDigest.digest();
245.
StringBuffer md5StrBuff =
new
StringBuffer();
246.
for
(
int
i =
0
; i < byteArray.length; i++) {
247.
if
(Integer.toHexString(
0xFF
& byteArray[i]).length() ==
1
)
248.
md5StrBuff.append(
"0"
).append(Integer.toHexString(
0xFF
& byteArray[i]));
249.
else
250.
md5StrBuff.append(Integer.toHexString(
0xFF
& byteArray[i]));
251.
}
252.
return
md5StrBuff.toString();
253.
}
254.
255.
256.
// ------------------------ 异步加载----------------------------
257.
/**
258.
* 占位的 图片 或者 颜色 用来绑定 相应的图片
259.
*/
260.
class
Zhanwei_Image
extends
ColorDrawable{
261.
//里面存放 相应 的异步 处理时加载好的图片 ----- 相应的 task
262.
private
final
WeakReference<ImageDownloadTask> taskReference;
263.
public
Zhanwei_Image(ImageDownloadTask task){
264.
super
(Color.BLUE);
265.
taskReference =
new
WeakReference<MyImageLoader.ImageDownloadTask>(task);
266.
}
267.
// 返回去这个 task 用于比较
268.
public
ImageDownloadTask getImageDownloadTask(){
269.
return
taskReference.get();
270.
}
271.
}
272.
273.
274.
// 根据 给 的 iamgeView、 得到里面的 task 用于和当前的 task比较是不是同1个
275.
private
ImageDownloadTask getImageDownloadTask(ImageView imageView){
276.
if
(
null
!= imageView){
277.
Drawable drawable = imageView.getDrawable();
278.
if
( drawable
instanceof
Zhanwei_Image)
279.
return
((Zhanwei_Image)drawable).getImageDownloadTask();
280.
281.
}
282.
return
null
;
283.
}
284.
285.
286.
287.
/**
288.
* 把图片 添加到缓存中
289.
*/
290.
public
void
addBitmap(String key, Bitmap bitmap) {
291.
if
(
null
!= bitmap) {
292.
synchronized
(link_hashmap) {
// 添加到一级 缓存中
293.
link_hashmap.put(key, bitmap);
294.
}
295.
}
296.
}
297.
298.
299.
/** 在后台 加载每个图片
300.
* 第一个参数 第2个要进度条不 第三个返回结果 bitmap
301.
*/
302.
class
ImageDownloadTask
extends
AsyncTask<String, Void, Bitmap> {
303.
304.
private
String key;
305.
private
WeakReference<ImageView> imgViReference;
306.
307.
public
ImageDownloadTask(ImageView imageView) {
308.
//imageView 传进来 。。要给哪个iamgeView加载图片
309.
imgViReference =
new
WeakReference<ImageView>(
310.
imageView);
311.
}
312.
313.
@Override
314.
protected
Bitmap doInBackground(String... params){
315.
key = params[
0
];
316.
//调用下载函数 根据 url 下载
317.
return
downloadBitmap(key);
318.
}
319.
320.
@Override
321.
protected
void
onPostExecute(Bitmap result) {
322.
if
(isCancelled()){
323.
result =
null
;
324.
}
325.
326.
System.out.println(
"result=="
+ result.getByteCount()+
"---memClassmemery="
+memClass);
327.
328.
if
(
null
!= result){
329.
//保存到缓存中
330.
addBitmap(key, result);
331.
ImageView imageView = imgViReference.get();
332.
if
(
null
!= imageView){
333.
//向 imageView 里面放入 bitmap
334.
ImageDownloadTask task = getImageDownloadTask(imageView);
335.
336.
/**
337.
* 判断 是不是 同一个 task( )
338.
* 如果当前这个 task == imageView 里面的那个 task 就是同1个
339.
*/
340.
if
(
this
== task ){
341.
imageView.setImageBitmap(result);
342.
343.
}
344.
}
345.
}
346.
}
347.
}
348.
349.
350.
/**
351.
* 连接网络 客户端 下载图片
352.
*/
353.
private
Bitmap downloadBitmap(String url) {
354.
355.
final
HttpClient client = AndroidHttpClient.newInstance(
"Android"
);
356.
final
HttpGet getRequest =
new
HttpGet(url);
357.
try
{
358.
HttpResponse response = client.execute(getRequest);
359.
final
int
statusCode = response.getStatusLine().getStatusCode();
360.
361.
if
(statusCode != HttpStatus.SC_OK) {
362.
363.
Log.w(
"ImageDownloader"
,
"Error "
+ statusCode +
" while retrieving bitmap from "
+ url);
364.
return
null
;
365.
}
366.
367.
final
HttpEntity entity = response.getEntity();
368.
if
(entity !=
null
) {
369.
InputStream inputStream =
null
;
370.
try
{
371.
372.
inputStream = entity.getContent();
373.
/**
374.
* 1.没有压缩直接将生成的bitmap返回去
375.
*/
376.
// return BitmapFactory.decodeStream(inputStream);
377.
378.
/**
379.
* 2.得到data后在这里把图片进行压缩
380.
*/
381.
byte
[] data = StreamTool.read(inputStream);
382.
return
BitmapManager.scaleBitmap(context, data,
0
.3f);
383.
// return BitmapFactory.decodeStream(new FlushedInputStream(inputStream));
384.
}
finally
{
385.
if
(inputStream !=
null
) {
386.
inputStream.close();
387.
}
388.
entity.consumeContent();
389.
}
390.
}
391.
}
catch
(IOException e) {
392.
getRequest.abort();
393.
}
catch
(IllegalStateException e) {
394.
getRequest.abort();
395.
}
catch
(Exception e) {
396.
getRequest.abort();
397.
}
finally
{
398.
if
((client
instanceof
AndroidHttpClient)) {
399.
((AndroidHttpClient) client).close();
400.
}
401.
}
402.
return
null
;
403.
}
404.
405.
}
StreamTool.java
01.
public
class
StreamTool {
02.
03.
public
static
byte
[] read(InputStream in)
throws
Exception{
04.
ByteArrayOutputStream out_byte =
new
ByteArrayOutputStream();
05.
byte
[] buff =
new
byte
[
1024
];
06.
int
len=
0
;
07.
while
((len = in.read(buff))!= -
1
){
08.
//写到内存中 字节流
09.
out_byte.write( buff,
0
, len);
10.
}
11.
out_byte.close();
12.
// 把内存数据返回
13.
return
out_byte.toByteArray();
14.
}
15.
}
BitmapManager.java ( 这个类里面对 网络资源的图片 进行了优化)
001.
public
class
BitmapManager {
002.
003.
/**
004.
* 按屏幕适配Bitmap
005.
*/
006.
public
static
Bitmap scaleBitmap(Context context,
byte
[] data ,
float
percent) {
007.
008.
//这里我不获取了,假设是下面这个分辨率
009.
int
screenWidth =
540
;
010.
int
screenrHeight =
950
;
011.
//设置 options
012.
BitmapFactory.Options options =
new
BitmapFactory.Options();
013.
/**
014.
* BitmapFactory.Options这个类,有一个字段叫做 inJustDecodeBounds.SDK中对这个成员的说明是这样的:
015.
* If set to true, the decoder will return null (no bitmap), but the out…
016.
* 也就是说,如果我们把它设为true,那么BitmapFactory.decodeFile(String path, Options opt)并不会真的返回一个Bitmap给你,
017.
* 它仅仅会把它的宽,高取回来给你,这样就不会占用太多的内存,也就不会那么频繁的发生OOM了。
018.
*/
019.
options.inJustDecodeBounds =
true
;
020.
021.
//读取
022.
Bitmap bitmap = BitmapFactory.decodeByteArray(data,
0
, data.length, options);
023.
024.
int
imgWidth = options.outWidth;
025.
int
imgHeight = options.outHeight;
026.
027.
//如果比你设置的宽高大 就进行缩放,
028.
if
(imgWidth > screenWidth * percent || imgHeight > screenrHeight * percent) {
029.
options.inSampleSize = calculateInSampleSize(options, screenWidth, screenrHeight, percent);
030.
}
031.
032.
033.
/**
034.
* If set to true, the decoder will return null (no bitmap), but the out... fields will still be set, allowing the caller
035.
* to query the bitmap without having to allocate the memory for its pixels.
036.
*
037.
* 如果设置成 true,这个编码将会返回1个null , 但是那个区域仍将被设置(也就是存在),允许(调用者)去查询那个没有分配 内存的像素 bitmap
038.
*/
039.
options.inJustDecodeBounds =
false
;
040.
041.
/**
042.
* Android的Bitmap.Config给出了bitmap的一个像素所对应的存储方式,
043.
* 有RGB_565,ARGB_8888,ARGB_4444,ALPHA_8四种。RGB_565表示的是红绿蓝三色分别用5,6,5个比特来存储,
044.
* 一个像素占用了5+6+5=16个比特。ARGB_8888表示红绿蓝和半透明分别用8,8,8,8个比特来存储,
045.
* 一个像素占用了8+8+8+8=32个比特。这样的话如果图片是以RGB_8888读入的,那么占用内存的大小将是RGB_565读入方式的2倍。
046.
* 通常我们给Imagview加载图片是通过setDrawable或者在xml文件中用android:src来设置
047.
* 默认的加载图片大小的方式是以RGB_8888读入的。
048.
*
049.
*/
050.
options.inPreferredConfig = Bitmap.Config.RGB_565;
051.
052.
/**
053.
* If this is set to true, then the resulting bitmap will allocate its pixels such that they can be purged
054.
* if the system needs to reclaim memory.
055.
*
056.
* 如果设置成 true, 这个结果bitmap 将会被分配像素,这样他们就能被 系统回收了,当系统需要回收内存的时候
057.
*/
058.
options.inPurgeable =
true
;
059.
060.
/**
061.
* This field works in conjuction with inPurgeable.
062.
* 这个方法是在 inPurgeable 的基础上工作的
063.
*/
064.
options.inInputShareable =
true
;
065.
066.
067.
bitmap = BitmapFactory.decodeByteArray(data,
0
, data.length, options);
068.
069.
System.out.println(
"data==="
+ data.length +
" change == bitmap byte "
+ bitmap.getByteCount());
070.
return
bitmap;
071.
}
072.
073.
074.
075.
// options reqWidth 屏幕宽 reqHeight屏幕高 你的view是屏幕的多大
076.
public
static
int
calculateInSampleSize(BitmapFactory.Options options,
int
screenWidth,
int
screenHeight ,
float
percent) {
077.
078.
// 原始图片宽高
079.
final
int
height = options.outHeight;
080.
final
int
width = options.outWidth;
081.
// 倍数
082.
int
inSampleSize =
1
;
083.
084.
if
(height > screenHeight * percent || width > screenWidth * percent) {
085.
086.
// 计算目标宽高与原始宽高的比值
087.
final
int
inSampleSize_h = Math.round((
float
) height / (
float
)( screenHeight * percent));
088.
089.
final
int
inSampleSize_w = Math.round((
float
) width / (
float
)( screenWidth * percent));
090.
091.
// 选择两个比值中较小的作为inSampleSize的
092.
inSampleSize = inSampleSize_h < inSampleSize_w ? inSampleSize_h : inSampleSize_w;
093.
094.
System.out.println(
"inSampleSize===="
+ inSampleSize);
095.
//
096.
if
(inSampleSize <
1
) {
097.
inSampleSize =
1
;
098.
}
099.
}
100.
//简单说这个数字就是 缩小为原来的几倍,根据你的image需要占屏幕多大动态算的(比如你用的权重设置layout)
101.
return
inSampleSize;
102.
}
103.
}
这个是代码输出的最多给这个进程分配的内存 128M
可以看到我上面的bitmapManager 里面有个 options.inPreferredConfig 注释写的很清楚,可以上去看一下,接下来贴几种格式的效果图
rgb565 和 argb_444 所占的内存 (54000)
看一下 argb_8888 ( 108000)
当然可能仔细看的人会看到我一开始截的 鸣人的效果图 上半部分 和 下半部分的颜色会有点问题。上面的rgb_565 生成的,和原图色彩可能会有点出入。
但是内存真心少了一半,所以各种取舍就看个人了,代码注释都谢的很清楚了。
至于 : MyImageLoaderLru.java 其实就是 MyImageLoader.java
先贴出代码不同地方的代码 : 就是在强引用的地方 把 LinkedHashMap 换成了 LruCache
01.
// 获取单个进程可用内存的最大值
02.
// 方式一:使用ActivityManager服务(计量单位为M)
03.
/*int memClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();*/
04.
// 方式二:使用Runtime类(计量单位为Byte)
05.
final
static
int
memClass = (
int
) Runtime.getRuntime().maxMemory();
06.
// 3. 定义一级缓存容器 强引用 (catch_num /2,0.75f,true) 默认参 数 2.加载因子默认 3.排序模式 true
07.
final
static
int
max = memClass/
5
;
08.
09.
// LruCache 用强引用将 图片放入 LinkedHashMap
10.
private
static
LruCache<String, Bitmap> lrucache =
new
LruCache<String, Bitmap>(max) {
11.
protected
int
sizeOf(String key, Bitmap value) {
12.
if
(value !=
null
) {
13.
// 计算存储bitmap所占用的字节数
14.
return
value.getRowBytes() * value.getHeight();
15.
}
else
{
16.
return
0
;
17.
}
18.
}
19.
20.
@Override
21.
protected
void
entryRemoved(
boolean
evicted, String key, Bitmap oldValue, Bitmap newValue) {
22.
if
(oldValue !=
null
) {
23.
// 当硬引用缓存容量已满时,会使用LRU算法将最近没有被使用的图片转入软引用缓存
24.
current_hashmap.put(key,
new
SoftReference<Bitmap>(oldValue));
25.
}
26.
}
27.
};
1. 强引用:LruCache 后面再说,其实他内的内部封装的就是1个 LinkedHashMap 。LinkedHashMap 是线程不安全的,所以上面都会用到同步。
2. 软引用:ConcurrentHashMap 是线程安全的,并且支持高并发很有效率,这个后面也会说到,为什么要用 软引用 SoftReference,这个是在系统将要oom时,就会回收
软引用的对象资源,所以才会用到他,防止程序出异常 。
3. 磁盘缓存: 这个经常会看到网易新闻等,应用有些界面你看了很多图片,往上翻很多, 其实没有再次访问网络,会将部分image缓存在sdcard里。
4. 其中1个优化: 当比如用户快速滑动到 最底部,其实是最先加载显示给用户的部分的内容的,这样就是用户看到哪加载哪,1个是快,1个是避免资源浪费。
原理: 当用户进入界面加载图片 ,首先会从1级缓存强引用中找,找不到回去2级缓存软引用中找,找不到再去sdcard中找,再找不到才会去请求网络加载资源。
当然sdcard的缓存 看个人需求是否需要。
注: android 4.0 后 对 SoftReference 的回收机制进行了改变,所以你是可以不用 2级缓存的,直接去掉就好了。
只要控制好你的 lrucache 或者 linkedhashmap就好了。
免费培训课:http://www.jinhusns.com/Products/Curriculum/?type=xcj