異步加載圖檔的例子,網上也比較多,大部分用了hashmap<string, softreference<drawable>> imagecache ,但是現在已經不再推薦使用這種方式了,因為從 android 2.3 (api level 9)開始,垃圾回收器會更傾向于回收持有軟引用或弱引用的對象,這讓軟引用和弱引用變得不再可靠。另外,android 3.0 (api level 11)中,圖檔的資料會存儲在本地的記憶體當中,因而無法用一種可預見的方式将其釋放,這就有潛在的風險造成應用程式的記憶體溢出并崩潰,是以我這裡用得是lrucache來緩存圖檔,當存儲image的大小大于lrucache設定的值,系統自動釋放記憶體,這個類是3.1版本中提供的,如果你是在更早的android版本中開發,則需要導入android-support-v4的jar包(這裡要注意咯)
為什麼寫這篇文章呢?
這篇文章做了哪些方面的優化
使用了線程池來管理下載下傳任務
使用lrucache來緩存圖檔
使用手機來緩存圖檔
gridview滑動的時候取消下載下傳任務,靜止的時候進行下載下傳,gridview滑動更加的流暢
降低了代碼的耦合性,結構更加的清晰,便于以後重用
接下來我們先來看看項目的結構
fileutils 檔案操作的工具類,提供儲存圖檔,擷取圖檔,判斷圖檔是否存在,删除圖檔的一些方法,這個類比較簡單
package com.example.asyncimageloader;
import java.io.file;
import java.io.fileoutputstream;
import java.io.ioexception;
import android.content.context;
import android.graphics.bitmap;
import android.graphics.bitmap.compressformat;
import android.graphics.bitmapfactory;
import android.os.environment;
public class fileutils {
/**
* sd卡的根目錄
*/
private static string msdrootpath = environment.getexternalstoragedirectory().getpath();
* 手機的緩存根目錄
private static string mdatarootpath = null;
* 儲存image的目錄名
private final static string folder_name = "/androidimage";
public fileutils(context context){
mdatarootpath = context.getcachedir().getpath();
}
* 擷取儲存image的目錄
* @return
private string getstoragedirectory(){
return environment.getexternalstoragestate().equals(environment.media_mounted) ?
msdrootpath + folder_name : mdatarootpath + folder_name;
* 儲存image的方法,有sd卡存儲到sd卡,沒有就存儲到手機目錄
* @param filename
* @param bitmap
* @throws ioexception
public void savabitmap(string filename, bitmap bitmap) throws ioexception{
if(bitmap == null){
return;
}
string path = getstoragedirectory();
file folderfile = new file(path);
if(!folderfile.exists()){
folderfile.mkdir();
file file = new file(path + file.separator + filename);
file.createnewfile();
fileoutputstream fos = new fileoutputstream(file);
bitmap.compress(compressformat.jpeg, 100, fos);
fos.flush();
fos.close();
* 從手機或者sd卡擷取bitmap
* @param filename
public bitmap getbitmap(string filename){
return bitmapfactory.decodefile(getstoragedirectory() + file.separator + filename);
* 判斷檔案是否存在
public boolean isfileexists(string filename){
return new file(getstoragedirectory() + file.separator + filename).exists();
* 擷取檔案的大小
public long getfilesize(string filename) {
return new file(getstoragedirectory() + file.separator + filename).length();
* 删除sd卡或者手機的緩存圖檔和目錄
public void deletefile() {
file dirfile = new file(getstoragedirectory());
if(! dirfile.exists()){
if (dirfile.isdirectory()) {
string[] children = dirfile.list();
for (int i = 0; i < children.length; i++) {
new file(dirfile, children[i]).delete();
}
dirfile.delete();
}<span style="font-family:times new roman;font-size:14px;">
</span>
public class images {
public final static string[] imagethumburls = new string[] {
"https://lh6.googleusercontent.com/-55osaww3x0q/urquutcfr5i/aaaaaaaaabs/rwlj1rukryi/s160-c/a%252520photographer.jpg",
"https://lh4.googleusercontent.com/--dq8nirp7w4/urquvgmxvgi/aaaaaaaaabs/-gnulqfnnba/s160-c/a%252520song%252520of%252520ice%252520and%252520fire.jpg",
"https://lh5.googleusercontent.com/-7qzedtrkfkc/urquwzt1goi/aaaaaaaaabs/hqwgteynxsg/s160-c/another%252520rockaway%252520sunset.jpg",
"https://lh3.googleusercontent.com/--l0km39l5j8/urquxhgcdni/aaaaaaaaabs/3zrsjnrsomq/s160-c/antelope%252520butte.jpg",
"https://lh6.googleusercontent.com/-8ho-4vifnlw/urquznsfgti/aaaaaaaaabs/wt8jvitf7vw/s160-c/antelope%252520hallway.jpg",
"https://lh4.googleusercontent.com/-wiuwgvcu3qw/urqubrvcj4i/aaaaaaaaabs/yvbwggjwdiq/s160-c/antelope%252520walls.jpg",
"https://lh6.googleusercontent.com/-ubmlbpelvoq/urquccdv0ki/aaaaaaaaabs/idnhr2vqoqs/s160-c/apre%2525cc%252580s%252520la%252520pluie.jpg",
"https://lh3.googleusercontent.com/-s-afpvgseew/urquc6df-ji/aaaaaaaaabs/mt3xngrud68/s160-c/backlit%252520cloud.jpg",
"https://lh5.googleusercontent.com/-bvmif9a9yoq/urquea3hehi/aaaaaaaaabs/rcr6wyeqtao/s160-c/bee%252520and%252520flower.jpg",
"https://lh5.googleusercontent.com/-n7mdm7i7fgs/urquet_bt-i/aaaaaaaaabs/9mymxlmpsao/s160-c/bonzai%252520rock%252520sunset.jpg",
"https://lh6.googleusercontent.com/-4cn4x4t0m1k/urqufpozwzi/aaaaaaaaabs/8wk41lg1kps/s160-c/caterpillar.jpg",
"https://lh3.googleusercontent.com/-rrfnvc8xqeg/urqufdrlbai/aaaaaaaaabs/s69wyy_fl1e/s160-c/chess.jpg",
"https://lh5.googleusercontent.com/-wvprptwh8yw/urqugh-qmdi/aaaaaaaaabs/e-mgbgtluwu/s160-c/chihuly.jpg",
"https://lh5.googleusercontent.com/-0bdxkymckbo/urquhkfw84i/aaaaaaaaabs/ogqthctk2jq/s160-c/closed%252520door.jpg",
"https://lh3.googleusercontent.com/-pyggxxzrykm/urquh-kvvoi/aaaaaaaaabs/hftdwhtrhhq/s160-c/colorado%252520river%252520sunset.jpg",
"https://lh3.googleusercontent.com/-zas4dnztalc/urquikvocwi/aaaaaaaaabs/dxz4h3dll1y/s160-c/colors%252520of%252520autumn.jpg",
"https://lh4.googleusercontent.com/-gztnweiimz8/urqukvcu7bi/aaaaaaaaabs/jo2hjv6mz6m/s160-c/countryside.jpg",
"https://lh4.googleusercontent.com/-beg9ez9qoim/urquklz3fgi/aaaaaaaaabs/uuuv8ac2bae/s160-c/death%252520valley%252520-%252520dunes.jpg",
"https://lh6.googleusercontent.com/-ijqj8w68tee/urqulgkvfei/aaaaaaaaabs/zpxviwi_rfw/s160-c/delicate%252520arch.jpg",
"https://lh5.googleusercontent.com/-oh8mmy2ieng/urqulldwehi/aaaaaaaaabs/tbdeefsaizy/s160-c/despair.jpg",
"https://lh5.googleusercontent.com/-gl0y4uiaolk/urqumc_kjbi/aaaaaaaaabs/pm1et7dn4oo/s160-c/eagle%252520fall%252520sunrise.jpg",
"https://lh3.googleusercontent.com/-hyyhd2_vxpq/urqumtja9ei/aaaaaaaaabs/waalxvkbsh0/s160-c/electric%252520storm.jpg",
"https://lh5.googleusercontent.com/-pyy_yiyjpto/urqunuohhfi/aaaaaaaaabs/azzoulnujxc/s160-c/false%252520kiva.jpg",
"https://lh6.googleusercontent.com/-pyvlvdvxywk/urqunwd8hfi/aaaaaaaaabs/qimwgkfvf6i/s160-c/fitzgerald%252520streaks.jpg",
"https://lh4.googleusercontent.com/-kir_uobiiqy/urquocz9sli/aaaaaaaaabs/y4d4q8sxu4c/s160-c/foggy%252520sunset.jpg",
"https://lh6.googleusercontent.com/-9lzok_owzh0/urquoo4xyoi/aaaaaaaaabs/awgzhtnvcwu/s160-c/frantic.jpg",
"https://lh3.googleusercontent.com/-0x3jnakaz48/urquph78wpi/aaaaaaaaabs/lhxxu_zbh8s/s160-c/golden%252520gate%252520afternoon.jpg",
"https://lh6.googleusercontent.com/-95sb5ag7abc/urqupl95rdi/aaaaaaaaabs/g73r20ivtra/s160-c/golden%252520gate%252520fog.jpg",
"https://lh3.googleusercontent.com/-jb9v6rtghhk/urqup21f-zi/aaaaaaaaabs/64fb8qmzwxk/s160-c/golden%252520grass.jpg",
"https://lh4.googleusercontent.com/-eibgfnultii/urquqvhwari/aaaaaaaaabs/fa4mcv2u8ve/s160-c/grand%252520teton.jpg",
"https://lh4.googleusercontent.com/-womxzvmn9ny/urquq1v2aoi/aaaaaaaaabs/grj5umhl6na/s160-c/grass%252520closeup.jpg",
"https://lh3.googleusercontent.com/-6hziehxx64q/urqurxvndqi/aaaaaaaaabs/kwmxm3o5ovi/s160-c/green%252520grass.jpg",
"https://lh5.googleusercontent.com/-6lvb9oxtq60/urqutebfuki/aaaaaaaaabs/4f4krgecwfs/s160-c/hanging%252520leaf.jpg",
"https://lh4.googleusercontent.com/-zavf__52onk/urqutt_iuxi/aaaaaaaaabs/d_bcuc0thou/s160-c/highway%2525201.jpg",
"https://lh6.googleusercontent.com/-h4srug615ra/urquul27fxi/aaaaaaaaabs/4aeqjfimsou/s160-c/horseshoe%252520bend%252520sunset.jpg",
"https://lh4.googleusercontent.com/-jhfi4fb_pqw/urquux-qxbi/aaaaaaaaabs/ixpyuxuweym/s160-c/horseshoe%252520bend.jpg",
"https://lh5.googleusercontent.com/-uggssvfrj7g/urquueyjzgi/aaaaaaaaabs/yyibllt0tom/s160-c/into%252520the%252520blue.jpg",
"https://lh3.googleusercontent.com/-ch7koupi7ui/urquu0ff__i/aaaaaaaaabs/r7gdmi7v_g0/s160-c/jelly%252520fish%2525202.jpg",
"https://lh4.googleusercontent.com/-pwuuw6yhg8u/urquvpxr3fi/aaaaaaaaabs/vngk6f-tsge/s160-c/jelly%252520fish%2525203.jpg",
"https://lh5.googleusercontent.com/-gouqvw1fnfw/urquv6xbc0i/aaaaaaaaabs/zeuvtqq43zc/s160-c/kauai.jpg",
"https://lh6.googleusercontent.com/-8qdyyqepyjw/urquwvdh88i/aaaaaaaaabs/cktdy-ysfho/s160-c/kyoto%252520sunset.jpg",
"https://lh4.googleusercontent.com/-vpeekydjoe0/urquwzj28qi/aaaaaaaaabs/qxcyxulszrg/s160-c/lake%252520tahoe%252520colors.jpg",
"https://lh4.googleusercontent.com/-xbpxwpd4yxu/urquxwhk8ai/aaaaaaaaabs/ardpedypimy/s160-c/lava%252520from%252520the%252520sky.jpg",
"https://lh3.googleusercontent.com/-897vxrjb6re/urquxxxd-5i/aaaaaaaaabs/j-cz4t4yviw/s160-c/leica%25252050mm%252520summilux.jpg",
"https://lh5.googleusercontent.com/-qsj4d4ixzgo/urquydwij1i/aaaaaaaaabs/k2pbxewehoa/s160-c/leica%25252050mm%252520summilux.jpg",
"https://lh6.googleusercontent.com/-dwlpg83vzlg/urquyltvufi/aaaaaaaaabs/g6syq8b4ysi/s160-c/leica%252520m8%252520%252528front%252529.jpg",
"https://lh3.googleusercontent.com/-r3_eyayjvfk/urquzqbv8ei/aaaaaaaaabs/b9xhpum3pei/s160-c/light%252520to%252520sand.jpg",
"https://lh3.googleusercontent.com/-fhy5h67qpi0/urqu0cp4j1i/aaaaaaaaabs/0lg6m94z6vm/s160-c/little%252520bit%252520of%252520paradise.jpg",
"https://lh5.googleusercontent.com/-tzf_lwrcnrm/urqu0rddpoi/aaaaaaaaabs/gaj2dliux0s/s160-c/lone%252520pine%252520sunset.jpg",
"https://lh3.googleusercontent.com/-4hdpj4_dxu4/urqu046dj9i/aaaaaaaaabs/eboodtk2_uk/s160-c/lonely%252520rock.jpg",
"https://lh6.googleusercontent.com/-erbf--z-w4s/urqu1ajslki/aaaaaaaaabs/xjdcdo1inzm/s160-c/longue%252520vue.jpg",
"https://lh6.googleusercontent.com/-0cxjrdjaqvc/urqu1opnzni/aaaaaaaaabs/pfb2opuu7lk/s160-c/look%252520me%252520in%252520the%252520eye.jpg",
"https://lh3.googleusercontent.com/-d_5lnxndn6g/urqu2tk7hvi/aaaaaaaaabs/p0ddca9w__y/s160-c/lost%252520in%252520a%252520field.jpg",
"https://lh6.googleusercontent.com/-flsqwmrik2q/urqu24pcmji/aaaaaaaaabs/5ocih85xofm/s160-c/marshall%252520beach%252520sunset.jpg",
"https://lh4.googleusercontent.com/-y4lgryevtmu/urqu28kg3gi/aaaaaaaaabs/ojxpekqtbj4/s160-c/mono%252520lake%252520blue.jpg",
"https://lh4.googleusercontent.com/-aahajpmcgya/urqu3pildhi/aaaaaaaaabs/lctqk1sicrs/s160-c/monument%252520valley%252520overlook.jpg",
"https://lh4.googleusercontent.com/-vkxfdq83dqa/urqu31yq_bi/aaaaaaaaabs/ouogk_2ayfm/s160-c/moving%252520rock.jpg",
"https://lh5.googleusercontent.com/-cg62qippwxg/urqu4ia4vri/aaaaaaaaabs/0yodqlalcac/s160-c/napali%252520coast.jpg",
"https://lh6.googleusercontent.com/-wdgrp5pmmjq/urqu5pzvn7i/aaaaaaaaabs/m0abecdpxe4/s160-c/one%252520wheel.jpg",
"https://lh6.googleusercontent.com/-6ws5docguoa/urqu5qx1ugi/aaaaaaaaabs/gimw2ixpvry/s160-c/open%252520sky.jpg",
"https://lh6.googleusercontent.com/-u8ehkj8g8gq/urqu55sm6yi/aaaaaaaaabs/lixx_gltdmi/s160-c/orange%252520sunset.jpg",
"https://lh6.googleusercontent.com/-74z5qj4btde/urqu6lsrjri/aaaaaaaaabs/xzmvkw90szq/s160-c/orchid.jpg",
"https://lh6.googleusercontent.com/-leqe4h6tepe/urqu6t_lski/aaaaaaaaabs/zvgykoea_qy/s160-c/over%252520there.jpg",
"https://lh5.googleusercontent.com/-cauh-53jh2m/urqu66v_usi/aaaaaaaaabs/eucwwqclfkq/s160-c/plumes.jpg",
"https://lh3.googleusercontent.com/-edlt2jhdoy4/urqu7axzkai/aaaaaaaaabs/ivze-xj7lzs/s160-c/rainbokeh.jpg",
"https://lh5.googleusercontent.com/-j1nlqefiyco/urqu8l1cgci/aaaaaaaaabs/aqzkgx66zli/s160-c/rainbow.jpg",
"https://lh5.googleusercontent.com/-drnqmk0t4vu/urqu8xyn9yi/aaaaaaaaabs/lgvf_592wlu/s160-c/rice%252520fields.jpg",
"https://lh3.googleusercontent.com/-hwh1v3eogcq/urqu8qoakwi/aaaaaaaaabs/iljrjrnbjgw/s160-c/rockaway%252520fire%252520sky.jpg",
"https://lh5.googleusercontent.com/-wjv6fqk7tlk/urqu9jcq8si/aaaaaaaaabs/ryyupdo-c9o/s160-c/rockaway%252520flow.jpg",
"https://lh6.googleusercontent.com/-6caxnfo7d20/urqu-bdzgpi/aaaaaaaaabs/omsyllzjqwo/s160-c/rockaway%252520sunset%252520sky.jpg",
"https://lh3.googleusercontent.com/-sl8fpgps-re/urqu_bokfgi/aaaaaaaaabs/dg2fv-jxoeg/s160-c/russian%252520ridge%252520sunset.jpg",
"https://lh6.googleusercontent.com/-gvty36mmbig/urqu_q91lki/aaaaaaaaabs/3cifmbcy5ma/s160-c/rust%252520knot.jpg",
"https://lh6.googleusercontent.com/-gheimuhqjbe/urqu_fkfvli/aaaaaaaaabs/axuejeqam7q/s160-c/sailing%252520stones.jpg",
"https://lh3.googleusercontent.com/-hbbyzjtowgc/urqu_ycpiri/aaaaaaaaabs/nadjuxngjye/s160-c/seahorse.jpg",
"https://lh3.googleusercontent.com/-iwi6-i6iexy/urqvayzhsvi/aaaaaaaaabs/5etwl4qxsfe/s160-c/shinjuku%252520street.jpg",
"https://lh6.googleusercontent.com/-amhnystm_my/urqvalb5koi/aaaaaaaaabs/pfcfgzlksn0/s160-c/sierra%252520heavens.jpg",
"https://lh5.googleusercontent.com/-djgjepfryso/urqvbvjzrai/aaaaaaaaabs/v-f5qwpyo6s/s160-c/sierra%252520sunset.jpg",
"https://lh4.googleusercontent.com/-z4zgic5nwdc/urqvbdewivi/aaaaaaaaabs/zrzr1vj84qa/s160-c/sin%252520lights.jpg",
"https://lh4.googleusercontent.com/-_0cyiww8ccy/urqvbz3im4i/aaaaaaaaabs/9n_wq8mhlty/s160-c/starry%252520lake.jpg",
"https://lh3.googleusercontent.com/-a9lmoryuqua/urqvcyx_joi/aaaaaaaaabs/s7sde1bz9ci/s160-c/starry%252520night.jpg",
"https://lh3.googleusercontent.com/-ktlj3k858ey/urqvc_2h_bi/aaaaaaaaabs/zzebimwda_g/s160-c/stream.jpg",
"https://lh5.googleusercontent.com/-dfb7lad6rca/urqvduftwwi/aaaaaaaaabs/brhoutxtn7o/s160-c/strip%252520sunset.jpg",
"https://lh5.googleusercontent.com/-at6apgfin20/urqvdyffuzi/aaaaaaaaabs/clabcx171be/s160-c/sunset%252520hills.jpg",
"https://lh4.googleusercontent.com/-7-ehhtqthii/urqveytk4vi/aaaaaaaaabs/qsjzob3yjvg/s160-c/tenaya%252520lake%2525202.jpg",
"https://lh6.googleusercontent.com/-8mrjv_a-pok/urqvfc5repi/aaaaaaaaabs/9inktg9fbce/s160-c/tenaya%252520lake.jpg",
"https://lh5.googleusercontent.com/-b1hw-z4zwao/urqvfwyrwui/aaaaaaaaabs/8peli53bs8i/s160-c/the%252520cave%252520bw.jpg",
"https://lh3.googleusercontent.com/-po4e-xzkanq/urqvgrqjyki/aaaaaaaaabs/42nyadfsxag/s160-c/the%252520fisherman.jpg",
"https://lh4.googleusercontent.com/-ilyzlzfdy7s/urqvg0yscdi/aaaaaaaaabs/1j9edkmkxtk/s160-c/the%252520night%252520is%252520coming.jpg",
"https://lh6.googleusercontent.com/-g-k7ykkuco0/urqvhhah6fi/aaaaaaaaabs/_taqqg7t0vo/s160-c/the%252520road.jpg",
"https://lh6.googleusercontent.com/-h-aljt7ksus/urqvithqyfi/aaaaaaaaabs/ejiv35olws8/s160-c/tokyo%252520heights.jpg",
"https://lh5.googleusercontent.com/-hy9k-tbs7xg/urqvijqmoxi/aaaaaaaaabs/rspmmoatskg/s160-c/tokyo%252520highway.jpg",
"https://lh6.googleusercontent.com/-83oovmb4ozs/urqvjl0t7li/aaaaaaaaabs/c5tecz6ronm/s160-c/tokyo%252520smog.jpg",
"https://lh3.googleusercontent.com/-fb-jfgreefi/urqvji3exai/aaaaaaaaabs/xfyweirf4v8/s160-c/tufa%252520at%252520night.jpg",
"https://lh4.googleusercontent.com/-vngkd5z1u8w/urqvjucegpi/aaaaaaaaabs/ulxcmvcu6eu/s160-c/valley%252520sunset.jpg",
"https://lh6.googleusercontent.com/-doz5i2e2omq/urqvkmnd1ki/aaaaaaaaabs/iqf0isinleo/s160-c/windmill%252520sunrise.jpg",
"https://lh5.googleusercontent.com/-biyiywcj9mu/urqvkculiai/aaaaaaaaabs/jypscpljope/s160-c/windmill.jpg",
"https://lh4.googleusercontent.com/-pdt167_xrda/urqvk36mlci/aaaaaaaaabs/oi2ik9qsemi/s160-c/windmills.jpg",
"https://lh5.googleusercontent.com/-ki_qdyx7vlu/urqvlxcb6gi/aaaaaaaaabs/n31vlz6u89o/s160-c/yet%252520another%252520rockaway%252520sunset.jpg",
"https://lh4.googleusercontent.com/-e9nhz5k5mss/urqvmibzjti/aaaaaaaaabs/1fv810rdnfq/s160-c/yosemite%252520tree.jpg", };
imagedownloader類,異步下載下傳的核心類,儲存圖檔到手機緩存,将圖檔加入lrucache中等等
import java.net.httpurlconnection;
import java.net.url;
import java.util.concurrent.executorservice;
import java.util.concurrent.executors;
import android.os.handler;
import android.os.message;
import android.support.v4.util.lrucache;
public class imagedownloader {
* 緩存image的類,當存儲image的大小大于lrucache設定的值,系統自動釋放記憶體
private lrucache<string, bitmap> mmemorycache;
* 操作檔案相關類對象的引用
private fileutils fileutils;
* 下載下傳image的線程池
private executorservice mimagethreadpool = null;
public imagedownloader(context context){
//擷取系統配置設定給每個應用程式的最大記憶體,每個應用系統配置設定32m
int maxmemory = (int) runtime.getruntime().maxmemory();
int mcachesize = maxmemory / 8;
//給lrucache配置設定1/8 4m
mmemorycache = new lrucache<string, bitmap>(mcachesize){
//必須重寫此方法,來測量bitmap的大小
@override
protected int sizeof(string key, bitmap value) {
return value.getrowbytes() * value.getheight();
};
fileutils = new fileutils(context);
* 擷取線程池的方法,因為涉及到并發的問題,我們加上同步鎖
public executorservice getthreadpool(){
if(mimagethreadpool == null){
synchronized(executorservice.class){
if(mimagethreadpool == null){
//為了下載下傳圖檔更加的流暢,我們用了2個線程來下載下傳圖檔
mimagethreadpool = executors.newfixedthreadpool(2);
}
return mimagethreadpool;
* 添加bitmap到記憶體緩存
* @param key
* @param bitmap
public void addbitmaptomemorycache(string key, bitmap bitmap) {
if (getbitmapfrommemcache(key) == null && bitmap != null) {
mmemorycache.put(key, bitmap);
}
}
* 從記憶體緩存中擷取一個bitmap
public bitmap getbitmapfrommemcache(string key) {
return mmemorycache.get(key);
}
* 先從記憶體緩存中擷取bitmap,如果沒有就從sd卡或者手機緩存中擷取,sd卡或者手機緩存
* 沒有就去下載下傳
* @param url
* @param listener
public bitmap downloadimage(final string url, final onimageloaderlistener listener){
//替換url中非字母和非數字的字元,這裡比較重要,因為我們用url作為檔案名,比如我們的url
//是http://xiaanming/abc.jpg;用這個作為圖檔名稱,系統會認為xiaanming為一個目錄,
//我們沒有建立此目錄儲存檔案就會報錯
final string suburl = url.replaceall("[^\\w]", "");
bitmap bitmap = showcachebitmap(suburl);
if(bitmap != null){
return bitmap;
}else{
final handler handler = new handler(){
@override
public void handlemessage(message msg) {
super.handlemessage(msg);
listener.onimageloader((bitmap)msg.obj, url);
};
getthreadpool().execute(new runnable() {
public void run() {
bitmap bitmap = getbitmapformurl(url);
message msg = handler.obtainmessage();
msg.obj = bitmap;
handler.sendmessage(msg);
try {
//儲存在sd卡或者手機目錄
fileutils.savabitmap(suburl, bitmap);
} catch (ioexception e) {
e.printstacktrace();
}
//将bitmap 加入記憶體緩存
addbitmaptomemorycache(suburl, bitmap);
});
return null;
* 擷取bitmap, 記憶體中沒有就去手機或者sd卡中擷取,這一步在getview中會調用,比較關鍵的一步
public bitmap showcachebitmap(string url){
if(getbitmapfrommemcache(url) != null){
return getbitmapfrommemcache(url);
}else if(fileutils.isfileexists(url) && fileutils.getfilesize(url) != 0){
//從sd卡擷取手機裡面擷取bitmap
bitmap bitmap = fileutils.getbitmap(url);
//将bitmap 加入記憶體緩存
addbitmaptomemorycache(url, bitmap);
* 從url中擷取bitmap
private bitmap getbitmapformurl(string url) {
bitmap bitmap = null;
httpurlconnection con = null;
try {
url mimageurl = new url(url);
con = (httpurlconnection) mimageurl.openconnection();
con.setconnecttimeout(10 * 1000);
con.setreadtimeout(10 * 1000);
con.setdoinput(true);
con.setdooutput(true);
bitmap = bitmapfactory.decodestream(con.getinputstream());
} catch (exception e) {
e.printstacktrace();
} finally {
if (con != null) {
con.disconnect();
return bitmap;
* 取消正在下載下傳的任務
public synchronized void canceltask() {
if(mimagethreadpool != null){
mimagethreadpool.shutdownnow();
mimagethreadpool = null;
* 異步下載下傳圖檔的回調接口
* @author len
*
public interface onimageloaderlistener{
void onimageloader(bitmap bitmap, string url);
}
imagedownloader中有幾個方法比較重要
首先我們需要重寫sizeof(string key, bitmap value)來計算圖檔的大小,預設傳回圖檔的數量
downloadimage(final string url, final onimageloaderlistener listener)先去lrucache檢視image,沒有再去手機緩存中檢視,在沒有則開啟線程下載下傳,這裡我們提供了一個回調接口,回調方法中我們将bitmap和圖檔url作為參數,string suburl = url.replaceall("[^\\w]", "") 我在代碼中注釋寫的比較清楚
showcachebitmap(string url)方法,此方法在adapter中的getview()當中調用,如果getview()中不調用此方法試試你就知道效果了
imageadapter gridview的擴充卡類,主要是gridview滑動的時候取消下載下傳任務,靜止的時候去下載下傳目前顯示的item的圖檔,其他也沒什麼不同了
import android.view.view;
import android.view.viewgroup;
import android.widget.abslistview;
import android.widget.abslistview.onscrolllistener;
import android.widget.baseadapter;
import android.widget.gridview;
import android.widget.imageview;
import com.example.asyncimageloader.imagedownloader.onimageloaderlistener;
public class imageadapter extends baseadapter implements onscrolllistener{
* 上下文對象的引用
private context context;
* image url的數組
private string [] imagethumburls;
* gridview對象的應用
private gridview mgridview;
* image 下載下傳器
private imagedownloader mimagedownloader;
* 記錄是否剛打開程式,用于解決進入程式不滾動螢幕,不會下載下傳圖檔的問題。
* 參考http://blog.csdn.net/guolin_blog/article/details/9526203#comments
private boolean isfirstenter = true;
* 一屏中第一個item的位置
private int mfirstvisibleitem;
* 一屏中所有item的個數
private int mvisibleitemcount;
public imageadapter(context context, gridview mgridview, string [] imagethumburls){
this.context = context;
this.mgridview = mgridview;
this.imagethumburls = imagethumburls;
mimagedownloader = new imagedownloader(context);
mgridview.setonscrolllistener(this);
@override
public void onscrollstatechanged(abslistview view, int scrollstate) {
//僅當gridview靜止時才去下載下傳圖檔,gridview滑動時取消所有正在下載下傳的任務
if(scrollstate == abslistview.onscrolllistener.scroll_state_idle){
showimage(mfirstvisibleitem, mvisibleitemcount);
canceltask();
* gridview滾動的時候調用的方法,剛開始顯示gridview也會調用此方法
public void onscroll(abslistview view, int firstvisibleitem,
int visibleitemcount, int totalitemcount) {
mfirstvisibleitem = firstvisibleitem;
mvisibleitemcount = visibleitemcount;
// 是以在這裡為首次進入程式開啟下載下傳任務。
if(isfirstenter && visibleitemcount > 0){
isfirstenter = false;
public int getcount() {
return imagethumburls.length;
public object getitem(int position) {
return imagethumburls[position];
public long getitemid(int position) {
return position;
public view getview(int position, view convertview, viewgroup parent) {
imageview mimageview;
final string mimageurl = imagethumburls[position];
if(convertview == null){
mimageview = new imageview(context);
mimageview = (imageview) convertview;
mimageview.setlayoutparams(new gridview.layoutparams(150, 150));
mimageview.setscaletype(imageview.scaletype.center_inside);
//給imageview設定tag,這裡已經是司空見慣了
mimageview.settag(mimageurl);
/*******************************去掉下面這幾行試試是什麼效果****************************/
bitmap bitmap = mimagedownloader.showcachebitmap(mimageurl.replaceall("[^\\w]", ""));
mimageview.setimagebitmap(bitmap);
mimageview.setimagedrawable(context.getresources().getdrawable(r.drawable.ic_empty));
/**********************************************************************************/
return mimageview;
* 顯示目前螢幕的圖檔,先會去查找lrucache,lrucache沒有就去sd卡或者手機目錄查找,在沒有就開啟線程去下載下傳
* @param firstvisibleitem
* @param visibleitemcount
private void showimage(int firstvisibleitem, int visibleitemcount){
for(int i=firstvisibleitem; i<firstvisibleitem + visibleitemcount; i++){
string mimageurl = imagethumburls[i];
final imageview mimageview = (imageview) mgridview.findviewwithtag(mimageurl);
bitmap = mimagedownloader.downloadimage(mimageurl, new onimageloaderlistener() {
public void onimageloader(bitmap bitmap, string url) {
if(mimageview != null && bitmap != null){
mimageview.setimagebitmap(bitmap);
//if(bitmap != null){
// mimageview.setimagebitmap(bitmap);
//}else{
// mimageview.setimagedrawable(context.getresources().getdrawable(r.drawable.ic_empty));
//}
* 取消下載下傳任務
public void canceltask(){
mimagedownloader.canceltask();
mainactivity 裡面一個gridview,然後提供一個系統菜單來删除手機上的緩存圖檔,直接上代碼,比較簡單是以裡面也沒有注釋
import android.app.activity;
import android.os.bundle;
import android.view.menu;
import android.view.menuitem;
import android.widget.toast;
public class mainactivity extends activity {
private string [] imagethumburls = images.imagethumburls;
private imageadapter mimageadapter;
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.activity_main);
fileutils = new fileutils(this);
mgridview = (gridview) findviewbyid(r.id.gridview);
mimageadapter = new imageadapter(this, mgridview, imagethumburls);
mgridview.setadapter(mimageadapter);
protected void ondestroy() {
mimageadapter.canceltask();
super.ondestroy();
public boolean oncreateoptionsmenu(menu menu) {
super.oncreateoptionsmenu(menu);
menu.add("删除手機中圖檔緩存");
return super.oncreateoptionsmenu(menu);
public boolean onoptionsitemselected(menuitem item) {
switch (item.getitemid()) {
case 0:
fileutils.deletefile();
toast.maketext(getapplication(), "清空緩存成功", toast.length_short).show();
break;
return super.onoptionsitemselected(item);
mainactivity的布局檔案
<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"
tools:context=".mainactivity" >
<gridview
android:id="@+id/gridview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:stretchmode="columnwidth"
android:columnwidth="90dip"
android:verticalspacing="10dip"
android:horizontalspacing="10dip"
android:cachecolorhint="@android:color/transparent"
android:numcolumns="auto_fit" >
</gridview>
</relativelayout>
因為有網絡操作和對sd卡的操作,是以我們必須加上相對應的權限
<uses-permission android:name="android.permission.internet" />
<uses-permission android:name="android.permission.write_external_storage" />
好了,代碼就全部介紹完了,我們運作下程式看看效果,這裡的效果是用一個線程下載下傳圖檔的效果,可能有一些不完整,想看看效果自己動手試試,郁悶,家裡的網絡卡,gif的圖檔出不來效果,隻能明天上傳圖檔啦,公司也上傳不了動畫效果,我去,那我就貼一張普通圖檔,哈哈
好了,今天的講解到此結束,有疑問的朋友請在下面留言,源碼裡面gridview的布局檔案 columnwedth設定了120dip,有點醜,大家可以改成90dip