国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Android - ListView異步加載圖片實現思路(優化篇)

ListView異步加載圖片實現思路(優化篇)

2021-01-09 15:29Android開發網 Android

關于listview的異步加載,網上其實很多示例了,中心思想都差不多,不過很多版本或是有bug,或是有性能問題有待優化,下面就讓在下闡述其原理以探索個中奧秘

在APP應用中,listview的異步加載圖片方式能夠帶來很好的用戶體驗,同時也是考量程序性能的一個重要指標。關于listview的異步加載,網上其實很多示例了,中心思想都差不多,不過很多版本或是有bug,或是有性能問題有待優化。有鑒于此,本人在網上找了個相對理想的版本并在此基礎上進行改造,下面就讓在下闡述其原理以探索個中奧秘,與諸君共賞…

貼張效果圖先:
異步加載圖片基本思想
1.先從內存緩存中獲取圖片顯示(內存緩沖)
2.獲取不到的話從SD卡里獲?。⊿D卡緩沖)
3.都獲取不到的話從網絡下載圖片并保存到SD卡同時加入內存并顯示(視情況看是否要顯示)
OK,先上adapter的代碼:

復制代碼 代碼如下:


public class LoaderAdapter extends BaseAdapter{
private static final String TAG = "LoaderAdapter";
private boolean mBusy = false;
public void setFlagBusy(boolean busy) {
this.mBusy = busy;
}
private ImageLoader mImageLoader;
private int mCount;
private Context mContext;
private String[] urlArrays;
public LoaderAdapter(int count, Context context, String []url) {
this.mCount = count;
this.mContext = context;
urlArrays = url;
mImageLoader = new ImageLoader(context);
}
public ImageLoader getImageLoader(){
return mImageLoader;
}
@Override
public int getCount() {
return mCount;
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(
R.layout.list_item, null);
viewHolder = new ViewHolder();
viewHolder.mTextView = (TextView) convertView
.findViewById(R.id.tv_tips);
viewHolder.mImageView = (ImageView) convertView
.findViewById(R.id.iv_image);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
String url = "";
url = urlArrays[position % urlArrays.length];
viewHolder.mImageView.setImageResource(R.drawable.ic_launcher);
if (!mBusy) {
mImageLoader.DisplayImage(url, viewHolder.mImageView, false);
viewHolder.mTextView.setText("--" + position
+ "--IDLE ||TOUCH_SCROLL");
} else {
mImageLoader.DisplayImage(url, viewHolder.mImageView, true);
viewHolder.mTextView.setText("--" + position + "--FLING");
}
return convertView;
}
static class ViewHolder {
TextView mTextView;
ImageView mImageView;
}
}


關鍵代碼是ImageLoader的DisplayImage方法,再看ImageLoader的實現

復制代碼 代碼如下:


public class ImageLoader {
private MemoryCache memoryCache = new MemoryCache();
private AbstractFileCache fileCache;
private Map<ImageView, String> imageViews = Collections
.synchronizedMap(new WeakHashMap<ImageView, String>());
// 線程池
private ExecutorService executorService;
public ImageLoader(Context context) {
fileCache = new FileCache(context);
executorService = Executors.newFixedThreadPool(5);
}
// 最主要的方法
public void DisplayImage(String url, ImageView imageView, boolean isLoadOnlyFromCache) {
imageViews.put(imageView, url);
// 先從內存緩存中查找
Bitmap bitmap = memoryCache.get(url);
if (bitmap != null)
imageView.setImageBitmap(bitmap);
else if (!isLoadOnlyFromCache){
// 若沒有的話則開啟新線程加載圖片
queuePhoto(url, imageView);
}
}
private void queuePhoto(String url, ImageView imageView) {
PhotoToLoad p = new PhotoToLoad(url, imageView);
executorService.submit(new PhotosLoader(p));
}
private Bitmap getBitmap(String url) {
File f = fileCache.getFile(url);
// 先從文件緩存中查找是否有
Bitmap b = null;
if (f != null && f.exists()){
b = decodeFile(f);
}
if (b != null){
return b;
}
// 最后從指定的url中下載圖片
try {
Bitmap bitmap = null;
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) imageUrl
.openConnection();
conn.setConnectTimeout(30000);
conn.setReadTimeout(30000);
conn.setInstanceFollowRedirects(true);
InputStream is = conn.getInputStream();
OutputStream os = new FileOutputStream(f);
CopyStream(is, os);
os.close();
bitmap = decodeFile(f);
return bitmap;
} catch (Exception ex) {
Log.e("", "getBitmap catch Exception...\nmessage = " + ex.getMessage());
return null;
}
}
// decode這個圖片并且按比例縮放以減少內存消耗,虛擬機對每張圖片的緩存大小也是有限制的
private Bitmap decodeFile(File f) {
try {
// decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f), null, o);
// Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE = 100;
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = 1;
while (true) {
if (width_tmp / 2 < REQUIRED_SIZE
|| height_tmp / 2 < REQUIRED_SIZE)
break;
width_tmp /= 2;
height_tmp /= 2;
scale *= 2;
}
// decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {
}
return null;
}
// Task for the queue
private class PhotoToLoad {
public String url;
public ImageView imageView;
public PhotoToLoad(String u, ImageView i) {
url = u;
imageView = i;
}
}
class PhotosLoader implements Runnable {
PhotoToLoad photoToLoad;
PhotosLoader(PhotoToLoad photoToLoad) {
this.photoToLoad = photoToLoad;
}
@Override
public void run() {
if (imageViewReused(photoToLoad))
return;
Bitmap bmp = getBitmap(photoToLoad.url);
memoryCache.put(photoToLoad.url, bmp);
if (imageViewReused(photoToLoad))
return;
BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);
// 更新的操作放在UI線程中
Activity a = (Activity) photoToLoad.imageView.getContext();
a.runOnUiThread(bd);
}
}
/**
* 防止圖片錯位
*
* @param photoToLoad
* @return
*/
boolean imageViewReused(PhotoToLoad photoToLoad) {
String tag = imageViews.get(photoToLoad.imageView);
if (tag == null || !tag.equals(photoToLoad.url))
return true;
return false;
}
// 用于在UI線程中更新界面
class BitmapDisplayer implements Runnable {
Bitmap bitmap;
PhotoToLoad photoToLoad;
public BitmapDisplayer(Bitmap b, PhotoToLoad p) {
bitmap = b;
photoToLoad = p;
}
public void run() {
if (imageViewReused(photoToLoad))
return;
if (bitmap != null)
photoToLoad.imageView.setImageBitmap(bitmap);
}
}
public void clearCache() {
memoryCache.clear();
fileCache.clear();
}
public static void CopyStream(InputStream is, OutputStream os) {
final int buffer_size = 1024;
try {
byte[] bytes = new byte[buffer_size];
for (;;) {
int count = is.read(bytes, 0, buffer_size);
if (count == -1)
break;
os.write(bytes, 0, count);
}
} catch (Exception ex) {
Log.e("", "CopyStream catch Exception...");
}
}
}


先從內存中加載,沒有則開啟線程從SD卡或網絡中獲取,這里注意從SD卡獲取圖片是放在子線程里執行的,否則快速滑屏的話會不夠流暢,這是優化一。于此同時,在adapter里有個busy變量,表示listview是否處于滑動狀態,如果是滑動狀態則僅從內存中獲取圖片,沒有的話無需再開啟線程去外存或網絡獲取圖片,這是優化二。ImageLoader里的線程使用了線程池,從而避免了過多線程頻繁創建和銷毀,有的童鞋每次總是new一個線程去執行這是非常不可取的,好一點的用的AsyncTask類,其實內部也是用到了線程池。在從網絡獲取圖片時,先是將其保存到sd卡,然后再加載到內存,這么做的好處是在加載到內存時可以做個壓縮處理,以減少圖片所占內存,這是優化三。

而圖片錯位問題的本質源于我們的listview使用了緩存convertView,假設一種場景,一個listview一屏顯示九個item,那么在拉出第十個item的時候,事實上該item是重復使用了第一個item,也就是說在第一個item從網絡中下載圖片并最終要顯示的時候其實該item已經不在當前顯示區域內了,此時顯示的后果將是在可能在第十個item上輸出圖像,這就導致了圖片錯位的問題。所以解決之道在于可見則顯示,不可見則不顯示。在ImageLoader里有個imageViews的map對象,就是用于保存當前顯示區域圖像對應的url集,在顯示前判斷處理一下即可。
下面再說下內存緩沖機制,本例采用的是LRU算法,先看看MemoryCache的實現

復制代碼 代碼如下:


public class MemoryCache {
private static final String TAG = "MemoryCache";
// 放入緩存時是個同步操作
// LinkedHashMap構造方法的最后一個參數true代表這個map里的元素將按照最近使用次數由少到多排列,即LRU
// 這樣的好處是如果要將緩存中的元素替換,則先遍歷出最近最少使用的元素來替換以提高效率
private Map<String, Bitmap> cache = Collections
.synchronizedMap(new LinkedHashMap<String, Bitmap>(10, 1.5f, true));
// 緩存中圖片所占用的字節,初始0,將通過此變量嚴格控制緩存所占用的堆內存
private long size = 0;// current allocated size
// 緩存只能占用的最大堆內存
private long limit = 1000000;// max memory in bytes
public MemoryCache() {
// use 25% of available heap size
setLimit(Runtime.getRuntime().maxMemory() / 10);
}
public void setLimit(long new_limit) {
limit = new_limit;
Log.i(TAG, "MemoryCache will use up to " + limit / 1024. / 1024. + "MB");
}
public Bitmap get(String id) {
try {
if (!cache.containsKey(id))
return null;
return cache.get(id);
} catch (NullPointerException ex) {
return null;
}
}
public void put(String id, Bitmap bitmap) {
try {
if (cache.containsKey(id))
size -= getSizeInBytes(cache.get(id));
cache.put(id, bitmap);
size += getSizeInBytes(bitmap);
checkSize();
} catch (Throwable th) {
th.printStackTrace();
}
}
/**
* 嚴格控制堆內存,如果超過將首先替換最近最少使用的那個圖片緩存
*
*/
private void checkSize() {
Log.i(TAG, "cache size=" + size + " length=" + cache.size());
if (size > limit) {
// 先遍歷最近最少使用的元素
Iterator<Entry<String, Bitmap>> iter = cache.entrySet().iterator();
while (iter.hasNext()) {
Entry<String, Bitmap> entry = iter.next();
size -= getSizeInBytes(entry.getValue());
iter.remove();
if (size <= limit)
break;
}
Log.i(TAG, "Clean cache. New size " + cache.size());
}
}
public void clear() {
cache.clear();
}
/**
* 圖片占用的內存
*
* <A href='\"http://www.eoeandroid.com/home.php?mod=space&uid=2768922\"' target='\"_blank\"'>@Param</A> bitmap
*
* @return
*/
long getSizeInBytes(Bitmap bitmap) {
if (bitmap == null)
return 0;
return bitmap.getRowBytes() * bitmap.getHeight();
}
}


首先限制內存圖片緩沖的堆內存大小,每次有圖片往緩存里加時判斷是否超過限制大小,超過的話就從中取出最少使用的圖片并將其移除,當然這里如果不采用這種方式,換做軟引用也是可行的,二者目的皆是最大程度的利用已存在于內存中的圖片緩存,避免重復制造垃圾增加GC負擔,OOM溢出往往皆因內存瞬時大量增加而垃圾回收不及時造成的。只不過二者區別在于LinkedHashMap里的圖片緩存在沒有移除出去之前是不會被GC回收的,而SoftReference里的圖片緩存在沒有其他引用保存時隨時都會被GC回收。所以在使用LinkedHashMap這種LRU算法緩存更有利于圖片的有效命中,當然二者配合使用的話效果更佳,即從LinkedHashMap里移除出的緩存放到SoftReference里,這就是內存的二級緩存,有興趣的童鞋不凡一試。

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 色综合天天综合网国产成人网 | 综合网激情五月 | 欧美一级网站 | 成人国产精品免费观看 | 亚洲一级在线 | 特污影院 | 色视av| 久久精品播放 | 一区二区国产在线观看 | 国产黄色大片免费在线观看 | 国产精品永久免费视频 | 国产亚洲在线 | 成人不卡视频 | 久久久99久久久国产自输拍 | 亚洲欧美日韩在线一区二区三区 | 日韩成人一区二区 | 日韩成人免费电影 | 日韩在线观看一区 | 国产免费一区二区三区 | 嫩草在线视频 | 国产精品久久久久久久久久久久久 | 久久免费精品 | 精品日韩一区二区 | 九九精品视频在线观看 | 91精品国产91久久久久久吃药 | 日韩成人不卡 | 中文字幕精品一区二区三区精品 | 日韩一区二区在线观看 | 中文字幕色 | 国产成人精品一区二区三区四区 | 国产成年人视频 | 五月婷婷婷婷 | 国产精品久久嫩一区二区免费 | 精品视频在线免费观看 | 精品国产一区二区三区日日嗨 | 成人在线观看网 | 91在线免费视频 | 日韩精品电影 | 曰批免费视频播放免费 | 国产欧美日韩综合精品一区二区 | 成av在线|