Android實(shí)現(xiàn)異步加載圖片
麥洛開通博客以來,有一段時(shí)間沒有更新博文了.主要是麥洛這段時(shí)間因項(xiàng)目開發(fā)實(shí)在太忙了.今天周六還在公司加班,苦逼程序猿都是這樣生活的.
今天在做項(xiàng)目的時(shí)候,有一個(gè)實(shí)現(xiàn)異步加載圖片的功能,雖然比較簡(jiǎn)單但還是記錄一下吧.因?yàn)辂溌逯皩?shí)現(xiàn)異步加載圖片都是使用了AsynTask這個(gè)API,繼續(xù)這個(gè)類,實(shí)現(xiàn)起來非常簡(jiǎn)單也很方便.在doInBackground()方法里實(shí)現(xiàn)下載邏輯.具體實(shí)現(xiàn)如下
實(shí)現(xiàn)邏輯是:先從內(nèi)存中讀取,如果內(nèi)存中有這張圖片,則直接使用;如果內(nèi)存沒有再到sdcard上讀取,如果有則顯示;如果sdcard上還沒有則到網(wǎng)絡(luò)上讀取.內(nèi)存中開啟緩存是參考了網(wǎng)上的實(shí)現(xiàn).麥洛在這里非常感謝喜歡分享的程序猿們.
public class ImageDownloader extends AsyncTask<String, Integer, Object> {
private static final String TAG = "ImageDownloader";
// 為了加快速度,在內(nèi)存中開啟緩存(主要應(yīng)用于重復(fù)圖片較多時(shí),或者同一個(gè)圖片要多次被訪問,比如在ListView時(shí)來回滾動(dòng))
private Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();
/**
* 顯示圖片的控件
*/
private ImageView mImageView;
public ImageDownloader(ImageView image) {
mImageView = image;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Object doInBackground(String... params) {
// Log.i("ImageDownloader", "loading image...");
String url = params[0];
Drawable drawable = null;
try {
if (!"".equals(url) && url != null) {
String fileName = url.hashCode()+".jpg";
// 如果緩存過就從緩存中取出數(shù)據(jù)
if (imageCache.containsKey(fileName)) {
SoftReference<Drawable> softReference = imageCache.get(fileName);
drawable = softReference.get();
if (drawable != null) {
return drawable;
}
}
File dir = new File(FileConstant.IMAGE_FILE_PATH);
if (!dir.exists()) {
boolean m = dir.mkdirs();
}
File file = new File(dir, fileName);
if (file.exists() && file.length() > 0) {
Log.i(TAG, "load image from sd card");
// 如果文件存在則直接讀取sdcard
drawable = readFromSdcard(file);
} else {
//file.createNewFile();
Log.i(TAG, "load image from network");
URL imageUrl = new URL(url);
// 寫入sdcard
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
saveImageFile(imageUrl, file);
drawable = Drawable.createFromStream(new FileInputStream(file), fileName);
}else{
//直接從流讀取
drawable = Drawable.createFromStream(imageUrl.openStream(), fileName);
}
}
if(drawable!=null){
//保存在緩存中
imageCache.put(fileName, new SoftReference<Drawable>(drawable));
}
}
} catch (Exception e) {
e.printStackTrace();
}
return drawable;
}
/**
* save image
*/
private void saveImageFile(URL url, File file) {
FileOutputStream out = null;
InputStream in = null;
try {
file.deleteOnExit();
out = new FileOutputStream(file);
in = url.openStream();
byte[] buf = new byte[1024];
int len = -1;
while((len = in.read(buf))!=-1){
out.write(buf, 0, len);
out.flush();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(out!=null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(in!=null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 從sdcard中獲取圖片
*/
private Drawable readFromSdcard(File file) throws Exception {
FileInputStream in = new FileInputStream(file);
return Drawable.createFromStream(in, file.getName());
}
@Override
protected void onPostExecute(Object result) {
super.onPostExecute(result);
Drawable drawable = (Drawable) result;
if (mImageView != null && drawable != null) {
mImageView.setBackgroundDrawable(drawable);
}
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
@Override
protected void onCancelled() {
super.onCancelled();
}
}
使用時(shí):
ImageDownloader loader = new ImageDownloader(imageView); loader.execute(url);
其實(shí)這樣的話,還有一些隱患的,就是說這個(gè)類實(shí)現(xiàn)還是有些問題的.比如每次都在imageView中設(shè)置網(wǎng)絡(luò)上的圖片時(shí),其實(shí)是沒有使用到這個(gè)類里面的內(nèi)存緩存的,就是imageCache
Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();
因?yàn)槊看卧O(shè)置imageView的時(shí)候,都是new了一個(gè)ImageDownloader的對(duì)象.所以每個(gè)ImageDownloader對(duì)象里面都是獨(dú)立的一個(gè)imageCache.
另外,AsynTask也是一個(gè)線程.而每次使用都開一個(gè)線程來load 圖片,對(duì)線程個(gè)數(shù)沒有進(jìn)行顯示,畢竟線程數(shù)目還是有限制的.
所以麥洛今天發(fā)現(xiàn)了這個(gè)問題,于是參考了別人的實(shí)現(xiàn),使用了線程池,實(shí)現(xiàn)邏輯也上面的代碼一樣,先從內(nèi)存讀取,如果沒有到sdcard讀取,如果還是沒有,則是網(wǎng)絡(luò)讀取;實(shí)現(xiàn)沒有使用AsynTask,具體代碼如下:
/**
* 異步加載圖片,并將圖片設(shè)置到ImageView控件中
*/
public class ImageDownloader extends AsyncTask<String, Integer, Object> {
private static final String TAG = "ImageDownloader";
// 為了加快速度,在內(nèi)存中開啟緩存(主要應(yīng)用于重復(fù)圖片較多時(shí),或者同一個(gè)圖片要多次被訪問,比如在ListView時(shí)來回滾動(dòng))
private Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();
/**
* 顯示圖片的控件
*/
private ImageView mImageView;
public ImageDownloader(ImageView image) {
mImageView = image;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Object doInBackground(String... params) {
// Log.i("ImageDownloader", "loading image...");
String url = params[0];
Drawable drawable = null;
try {
if (!"".equals(url) && url != null) {
String fileName = url.hashCode()+".jpg";
// 如果緩存過就從緩存中取出數(shù)據(jù)
if (imageCache.containsKey(fileName)) {
SoftReference<Drawable> softReference = imageCache.get(fileName);
drawable = softReference.get();
if (drawable != null) {
return drawable;
}
}
File dir = new File(FileConstant.IMAGE_FILE_PATH);
if (!dir.exists()) {
boolean m = dir.mkdirs();
}
File file = new File(dir, fileName);
if (file.exists() && file.length() > 0) {
Log.i(TAG, "load image from sd card");
// 如果文件存在則直接讀取sdcard
drawable = readFromSdcard(file);
} else {
//file.createNewFile();
Log.i(TAG, "load image from network");
URL imageUrl = new URL(url);
// 寫入sdcard
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
saveImageFile(imageUrl, file);
drawable = Drawable.createFromStream(new FileInputStream(file), fileName);
}else{
//直接從流讀取
drawable = Drawable.createFromStream(imageUrl.openStream(), fileName);
}
}
if(drawable!=null){
//保存在緩存中
imageCache.put(fileName, new SoftReference<Drawable>(drawable));
}
}
} catch (Exception e) {
e.printStackTrace();
}
return drawable;
}
/**
* save image
*/
private void saveImageFile(URL url, File file) {
FileOutputStream out = null;
InputStream in = null;
try {
file.deleteOnExit();
out = new FileOutputStream(file);
in = url.openStream();
byte[] buf = new byte[1024];
int len = -1;
while((len = in.read(buf))!=-1){
out.write(buf, 0, len);
out.flush();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(out!=null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(in!=null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 從sdcard中獲取圖片
*/
private Drawable readFromSdcard(File file) throws Exception {
FileInputStream in = new FileInputStream(file);
return Drawable.createFromStream(in, file.getName());
}
@Override
protected void onPostExecute(Object result) {
super.onPostExecute(result);
Drawable drawable = (Drawable) result;
if (mImageView != null && drawable != null) {
mImageView.setBackgroundDrawable(drawable);
}
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
@Override
protected void onCancelled() {
super.onCancelled();
}
}
這個(gè)ImageDownloader2的使用也很簡(jiǎn)單
public class ImageUtil {
/**
* image loader
*/
static ImageDownloader2 loader = null;
/**
* load image
*/
public static void loadImage(String url,final ImageView imageView){
if(loader == null){
loader = new ImageDownloader2();
}
loader.loadDrawable(url, new ImageCallback() {
@Override
public void imageLoaded(Drawable imageDrawable) {
if(imageDrawable!=null){
imageView.setBackgroundDrawable(imageDrawable);
}
}
});
}
}
每次在使用是需要調(diào)用ImageUtil.loadImage(url,imageView)將圖片url已經(jīng)需要顯示圖片的控件ImageView的引用傳入就可以了.
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android之PreferenceActivity應(yīng)用詳解
為了引入這個(gè)概率 首先從需求說起 即:現(xiàn)有某Activity專門用于手機(jī)屬性設(shè)置 那么應(yīng)該如何做呢2012-11-11
Android獲取雙卡雙待手機(jī)的SIM卡信息示例代碼
這篇文章主要給大家介紹了關(guān)于Android獲取雙卡雙待手機(jī)的SIM卡信息的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11
Kotlin作用域函數(shù)之間的區(qū)別和使用場(chǎng)景詳解
這篇文章主要給大家介紹了關(guān)于Kotlin作用域函數(shù)之間的區(qū)別和使用場(chǎng)景的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
Android實(shí)現(xiàn)百度地圖兩點(diǎn)畫弧線
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)百度地圖兩點(diǎn)畫弧線,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
Android中TabLayout添加小紅點(diǎn)的示例代碼
本篇文章主要介紹了Android中TabLayout添加小紅點(diǎn)的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-12-12
Android小掛件(APP Widgets)設(shè)計(jì)指導(dǎo)
這篇文章主要為大家詳細(xì)介紹了Android小掛件APP Widgets設(shè)計(jì)指導(dǎo),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12
Android?EventBus粘性事件實(shí)現(xiàn)機(jī)制探究
最近項(xiàng)目做組件化,需要進(jìn)行組件化的通信,有時(shí)候可能會(huì)出現(xiàn)異步的情況,事件接收方還沒準(zhǔn)備好事件就已經(jīng)發(fā)送過來了,這時(shí)候想到了EventBus的粘性事件,這篇文章主要給大家介紹了關(guān)于Android?EventBus粘性事件實(shí)現(xiàn)機(jī)制的相關(guān)資料,需要的朋友可以參考下2022-05-05
Android 用戶Session管理的設(shè)計(jì)方案
這篇文章主要介紹了Android 用戶Session管理的設(shè)計(jì)方案,需要的朋友可以參考下2017-12-12

