Android异步下载图片并且缓存图片到本地DEMO详解
在android开发中我们经常有这样的需求,从服务器上下载xml或者json类型的数据,其中包括一些图片资源,本demo模拟了这个需求,从网络上加载xml资源,其中包括图片,我们要做的解析xml里面的数据,并且把图片缓存到本地一个cache目录里面,并且用一个自定义的adapter去填充到listview,demo运行效果见下图:
通过这个demo,要学会有一下几点
1.怎么解析一个xml
2.demo中用到的缓存图片到本地一个临时目录的思想是怎样的?
3.asynctask类的使用,因为要去异步的加载数据,就必须开启线程,但是在开启线程的时有时候不能很好的控制线程的数量,线程数量太大的时候手机会很快被卡死 这里就采用asynstask类的去解决这个问题,这个类里面封装了线程池的技术,从而保证不会因开启过多的线程而消耗太多的资源
4.本demo中的handler类的使用情况 5.自定义adapter的使用
下面是demo中的activity。
public class mainactivity extends activity { protected static final int success_get_contact = 0; private listview mlistview; private mycontactadapter madapter; private file cache; private handler mhandler = new handler(){ public void handlemessage(android.os.message msg) { if(msg.what == success_get_contact){ list<contact> contacts = (list<contact>) msg.obj; madapter = new mycontactadapter(getapplicationcontext(),contacts,cache); mlistview.setadapter(madapter); } }; }; @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); mlistview = (listview) findviewbyid(r.id.listview); //创建缓存目录,系统一运行就得创建缓存目录的, cache = new file(environment.getexternalstoragedirectory(), "cache"); if(!cache.exists()){ cache.mkdirs(); } //获取数据,主ui线程是不能做耗时操作的,所以启动子线程来做 new thread(){ public void run() { contactservice service = new contactservice(); list<contact> contacts = null; try { contacts = service.getcontactall(); } catch (exception e) { e.printstacktrace(); } //子线程通过message对象封装信息,并且用初始化好的, //handler对象的sendmessage()方法把数据发送到主线程中,从而达到更新ui主线程的目的 message msg = new message(); msg.what = success_get_contact; msg.obj = contacts; mhandler.sendmessage(msg); }; }.start(); } @override protected void ondestroy() { super.ondestroy(); //清空缓存 file[] files = cache.listfiles(); for(file file :files){ file.delete(); } cache.delete(); } }
activity中,注意以下几点,
1.初始化了一个缓存目录,这个目录最好是应用开启就去创建好,为手续缓存图片做准备,在这里把数据存放在sdcard上
2.要去服务器加载数据,这个耗时操作最好是去开启线程加载数据,加载完毕后去异步的更新ui线程,利用handler机制能很好的解决这个问题,
3.最后退出应用的时候,要删掉缓存目录和目录里面的数据,避免给手机制造很多的垃圾文件
下面就是一个service类了,
public class contactservice { /* * 从服务器上获取数据 */ public list<contact> getcontactall() throws exception { list<contact> contacts = null; string parth = "http://192.168.1.103:8080/myweb/list.xml"; url url = new url(parth); httpurlconnection conn = (httpurlconnection) url.openconnection(); conn.setconnecttimeout(3000); conn.setrequestmethod("get"); if (conn.getresponsecode() == httpurlconnection.http_ok) { inputstream is = conn.getinputstream(); // 这里获取数据直接放在xmlpullparser里面解析 contacts = xmlparser(is); return contacts; } else { return null; } } // 这里并没有下载图片下来,而是把图片的地址保存下来了 private list<contact> xmlparser(inputstream is) throws exception { list<contact> contacts = null; contact contact = null; xmlpullparser parser = xml.newpullparser(); parser.setinput(is, "utf-8"); int eventtype = parser.geteventtype(); while ((eventtype = parser.next()) != xmlpullparser.end_document) { switch (eventtype) { case xmlpullparser.start_tag: if (parser.getname().equals("contacts")) { contacts = new arraylist<contact>(); } else if (parser.getname().equals("contact")) { contact = new contact(); contact.setid(integer.valueof(parser.getattributevalue(0))); } else if (parser.getname().equals("name")) { contact.setname(parser.nexttext()); } else if (parser.getname().equals("image")) { contact.setimage(parser.getattributevalue(0)); } break; case xmlpullparser.end_tag: if (parser.getname().equals("contact")) { contacts.add(contact); } break; } } return contacts; } /* * 从网络上获取图片,如果图片在本地存在的话就直接拿,如果不存在再去服务器上下载图片 * 这里的path是图片的地址 */ public uri getimageuri(string path, file cache) throws exception { string name = md5.getmd5(path) + path.substring(path.lastindexof(".")); file file = new file(cache, name); // 如果图片存在本地缓存目录,则不去服务器下载 if (file.exists()) { return uri.fromfile(file);//uri.fromfile(path)这个方法能得到文件的uri } else { // 从网络上获取图片 url url = new url(path); httpurlconnection conn = (httpurlconnection) url.openconnection(); conn.setconnecttimeout(5000); conn.setrequestmethod("get"); conn.setdoinput(true); if (conn.getresponsecode() == 200) { inputstream is = conn.getinputstream(); fileoutputstream fos = new fileoutputstream(file); byte[] buffer = new byte[1024]; int len = 0; while ((len = is.read(buffer)) != -1) { fos.write(buffer, 0, len); } is.close(); fos.close(); // 返回一个uri对象 return uri.fromfile(file); } } return null; } }
serivce类中,注意以下几点
1.httpurlconnection conn = (httpurlconnection) url.openconnection();获取一个链接,从而进行通讯
2.怎么利用xxmlpullpaser类去解析xml,从而把数据封装成对象
3.getimageuri(string path, file cache) 这个方法具体实现
4.uri.fromfile(file);这个方法能够直接返回一个uri来
下面是自定义的adapter类,
public class mycontactadapter extends baseadapter { protected static final int success_get_image = 0; private context context; private list<contact> contacts; private file cache; private layoutinflater minflater; // 自己定义的构造函数 public mycontactadapter(context context, list<contact> contacts, file cache) { this.context = context; this.contacts = contacts; this.cache = cache; minflater = (layoutinflater) context.getsystemservice(context.layout_inflater_service); } @override public int getcount() { return contacts.size(); } @override public object getitem(int position) { return contacts.get(position); } @override public long getitemid(int position) { return position; } @override public view getview(int position, view convertview, viewgroup parent) { // 1获取item,再得到控件 // 2 获取数据 // 3绑定数据到item view view = null; if (convertview != null) { view = convertview; } else { view = minflater.inflate(r.layout.item, null); } imageview iv_header = (imageview) view.findviewbyid(r.id.iv_header); textview tv_name = (textview) view.findviewbyid(r.id.tv_name); contact contact = contacts.get(position); // 异步的加载图片 (线程池 + handler ) ---> asynctask asyncloadimage(iv_header, contact.image); tv_name.settext(contact.name); return view; } private void asyncloadimage(imageview iv_header, string path) { contactservice service = new contactservice(); asyncimagetask task = new asyncimagetask(service, iv_header); task.execute(path); } private final class asyncimagetask extends asynctask<string, integer, uri> { private contactservice service; private imageview iv_header; public asyncimagetask(contactservice service, imageview iv_header) { this.service = service; this.iv_header = iv_header; } // 后台运行的子线程子线程 @override protected uri doinbackground(string... params) { try { return service.getimageuri(params[0], cache); } catch (exception e) { e.printstacktrace(); } return null; } // 这个放在在ui线程中执行 @override protected void onpostexecute(uri result) { super.onpostexecute(result); // 完成图片的绑定 if (iv_header != null && result != null) { iv_header.setimageuri(result); } } } /** * 采用普通方式异步的加载图片 */ /*private void asyncloadimage(final imageview iv_header, final string path) { final handler mhandler = new handler() { @override public void handlemessage(message msg) { super.handlemessage(msg); if (msg.what == success_get_image) { uri uri = (uri) msg.obj; if (iv_header != null && uri != null) { iv_header.setimageuri(uri); } } } }; // 子线程,开启子线程去下载或者去缓存目录找图片,并且返回图片在缓存目录的地址 runnable runnable = new runnable() { @override public void run() { contactservice service = new contactservice(); try { //这个uri是图片下载到本地后的缓存目录中的uri uri uri = service.getimageuri(path, cache); message msg = new message(); msg.what = success_get_image; msg.obj = uri; mhandler.sendmessage(msg); } catch (exception e) { e.printstacktrace(); } } }; new thread(runnable).start(); }*/ }
自定义adapter中,我们要注意 asyncimagetask这个类继承了asynctask类,asynctask是android中常用来做异步任务的类,对线程池进行了封装,详细分析稍后再贴出一篇blog。
下面是我们从服务器上获取并且解析的xml文件
<?xml version="1.0" encoding="utf-8"?> <contacts> <contact id="1"> <name>张飞</name> <image src="http://192.168.1.103:8080/mymyweb/images/1.gif"/> </contact> <contact id="2"> <name>博文</name> <image src="http://192.168.1.103:8080/myweb/images/2.gif"/> </contact> <contact id="3"> <name>张天佑</name> <image src="http://192.168.1.103:8080/myweb/images/3.gif"/> </contact> <contact id="4"> <name>松德</name> <image src="http://192.168.1.103:8080/myweb/images/4.gif"/> </contact> <contact id="5"> <name>赵薇</name> <image src="http://192.168.1.103:8080/myweb/images/5.gif"/> </contact> <contact id="6"> <name>李静</name> <image src="http://192.168.1.103:8080/myweb/images/6.gif"/> </contact> <contact id="7"> <name>李明</name> <image src="http://192.168.1.103:8080/myweb/images/7.gif"/> </contact> <contact id="8"> <name>黎明</name> <image src="http://192.168.1.103:8080/myweb/images/8.gif"/> </contact> <contact id="9"> <name>秦桧</name> <image src="http://192.168.1.103:8080/myweb/images/9.gif"/> </contact> <contact id="10"> <name>朱德</name> <image src="http://192.168.1.103:8080/myweb/images/10.gif"/> </contact> <contact id="11"> <name>冯巩</name> <image src="http://192.168.1.103:8080/myweb/images/11.gif"/> </contact> <contact id="12"> <name>dylan</name> <image src="http://192.168.1.103:8080/myweb/images/12.gif"/> </contact> <contact id="13"> <name>黄单</name> <image src="http://192.168.1.103:8080/myweb/images/13.gif"/> </contact> <contact id="14"> <name>含蕊</name> <image src="http://192.168.1.103:8080/myweb/images/14.gif"/> </contact> <contact id="15"> <name>欣琪</name> <image src="http://192.168.1.103:8080/myweb/images/15.jpg"/> </contact> <contact id="16"> <name>李忠华</name> <image src="http://192.168.1.103:8080/myweb/images/16.jpg"/> </contact> <contact id="17"> <name>方产员</name> <image src="http://192.168.1.103:8080/myweb/images/17.jpg"/> </contact> <contact id="18"> <name>张光</name> <image src="http://192.168.1.103:8080/myweb/images/18.jpg"/> </contact> </contacts>
本demo中为了安全起见,还对下载下来的图片的文件名进行了md5加密,下面是md5加密的代码,
public class md5 { public static string getmd5(string content) { try { messagedigest digest = messagedigest.getinstance("md5"); digest.update(content.getbytes()); return gethashstring(digest); } catch (nosuchalgorithmexception e) { e.printstacktrace(); } return null; } private static string gethashstring(messagedigest digest) { stringbuilder builder = new stringbuilder(); for (byte b : digest.digest()) { builder.append(integer.tohexstring((b >> 4) & 0xf)); builder.append(integer.tohexstring(b & 0xf)); } return builder.tostring(); } }
以上省略了contact.java这个domain类,通过这个demo,可以看出android中会经常需要进行异步任务的处理,所以我们会常常用到自己手动开启线程,handler机制,或者asynctask类等手段来保证应用的性能。
以上所述是小编给大家介绍的android异步下载图片并且缓存图片到本地demo详解,希望对大家有所帮助