先来看看效果示意图:

android异步加载图片显示,并且对图片进行缓存实例_第1张图片" width="370" height="527" style="border:1px solid black;">

step1:新建项目DataAsyncLoad,如下图所示

android异步加载图片显示,并且对图片进行缓存实例_第2张图片" width="251" height="519" style="border:1px solid black;">

step2:设置应用的UI界面

a.应用的主界面 main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    ><ListView      android:layout_width="fill_parent"     android:layout_height="fill_parent"     android:id="@+id/listView"    /></LinearLayout>

b.每个ListView的界面 listview_item.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="horizontal" >    <ImageView         android:layout_width="42dp"        android:layout_height="42dp"        android:id="@+id/imageView"          />    <TextView         android:layout_width="match_parent"        android:layout_height="wrap_content"        android:textSize="18sp"        android:textColor="#FFFFFF"        android:id="@+id/textView"                /></LinearLayout>

step3:写一些辅助类 cn.roco.data.utilsMD5.java

package cn.roco.data.utils;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;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();    }}


step4:写应用使用的JavaBean cn.roco.data.domain.Contact.java

package cn.roco.data.domain;public class Contact {private int id;private String name;private String image;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getImage() {return image;}public void setImage(String image) {this.image = image;}public Contact(int id, String name, String image) {this.id = id;this.name = name;this.image = image;}public Contact(){}}

step5:写一个应用的service层,用于对javabean进行操作 cn.roco.data.service.ContactService.java

package cn.roco.data.service;import java.io.File;import java.io.FileOutputStream;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.URL;import java.util.ArrayList;import java.util.List;import org.xmlpull.v1.XmlPullParser;import android.net.Uri;import android.util.Xml;import cn.roco.data.domain.Contact;import cn.roco.data.utils.MD5;public class ContactService {/** * 获取联系人数据 *  * @return * @throws Exception */public static List<Contact> getContacts() throws Exception {String path = "http://192.168.1.100:8080/Hello/contact.xml";HttpURLConnection connection = (HttpURLConnection) new URL(path).openConnection();connection.setConnectTimeout(5000);connection.setRequestMethod("GET");if (connection.getResponseCode() == 200) {return parseXML(connection.getInputStream());}return null;}/**转化XML获取数据 * 服务器端的xml文件如下。。。。。。 * <?xml version="1.0" encoding="UTF-8"?><contacts><contact id="1"><name>Roco_1</name><image src="http://192.168.1.100:8080/Hello/images/1.png" /></contact>.......</contacts>*/private static List<Contact> parseXML(InputStream inputStream)throws Exception {List<Contact> contacts = new ArrayList<Contact>();Contact contact = null;XmlPullParser pullParser = Xml.newPullParser();pullParser.setInput(inputStream, "UTF-8");int event = pullParser.getEventType();while (event != XmlPullParser.END_DOCUMENT) {switch (event) {case XmlPullParser.START_TAG:if ("contact".equals(pullParser.getName())) {contact = new Contact();contact.setId(new Integer(pullParser.getAttributeValue(0)));} else if ("name".equals(pullParser.getName())) {contact.setName(pullParser.nextText());} else if ("image".equals(pullParser.getName())) {contact.setImage(pullParser.getAttributeValue(0));}break;case XmlPullParser.END_TAG:if ("contact".equals(pullParser.getName())) {contacts.add(contact);contact = null;}}event = pullParser.next();}return contacts;}/** * 获取网络图片,如果图片存在于缓存中,就返回该图片,否则从网络中加载该图片并缓存起来 *  * @param path *            图片路径 * @return */public static Uri getImage(String imagePath, File cacheDir)throws Exception {//缓存文件的文件名用MD5进行加密File localFile = new File(cacheDir, MD5.getMD5(imagePath)+ imagePath.substring(imagePath.lastIndexOf("."))); if (localFile.exists()) {return Uri.fromFile(localFile);} else {HttpURLConnection connection = (HttpURLConnection) new URL(imagePath).openConnection();connection.setConnectTimeout(5000);connection.setRequestMethod("GET");//将文件缓存起来if (connection.getResponseCode() == 200) {FileOutputStream outputStream = new FileOutputStream(localFile);InputStream inputStream = connection.getInputStream();byte[] buffer = new byte[1024];int len = 0;while ((len = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, len);}inputStream.close();outputStream.close();return Uri.fromFile(localFile);}}return null;}}


step6:写一个Adapter用于对ListView进行数据更新cn.roco.data.adapter.ContactAdapter.java

package cn.roco.data.adapter;import java.io.File;import java.util.List;import cn.roco.data.R;import cn.roco.data.domain.Contact;import cn.roco.data.service.ContactService;import android.content.Context;import android.net.Uri;import android.os.AsyncTask;import android.os.Handler;import android.os.Message;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;/**适配器,用于更新View*/public class ContactAdapter extends BaseAdapter {private List<Contact> data;private int listviewItem;private File cache;/** * LayoutInflater的作用类似于 findViewById(),不同点是LayoutInflater是用来找layout文件夹下的xml布局文件,并且实例化! * 而 findViewById()是找具体某一个xml下的具体 widget控件(如:Button,TextView等)。 */private LayoutInflater layoutInflater;public ContactAdapter(Context context, List<Contact> data,int listviewItem, File cache) {this.data = data;this.listviewItem = listviewItem;this.cache = cache;this.layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);//取得xml里定义的view/*** * getSystemService()是Android很重要的一个API,它是Activity的一个方法, * 根据传入的NAME来取得对应的Object,然后转换成相应的服务对象。以下介绍系统相应的服务。  * 传入的Name 返回的对象 说明 WINDOW_SERVICE   WindowManager 管理打开的窗口程序 LAYOUT_INFLATER_SERVICE LayoutInflater 取得xml里定义的view ACTIVITY_SERVICE ActivityManager 管理应用程序的系统状态 POWER_SERVICE PowerManger 电源的服务 ALARM_SERVICE AlarmManager 闹钟的服务 NOTIFICATION_SERVICE NotificationManager 状态栏的服务 KEYGUARD_SERVICE KeyguardManager 键盘锁的服务 LOCATION_SERVICE LocationManager 位置的服务,如GPS SEARCH_SERVICE SearchManager 搜索的服务 VEBRATOR_SERVICE Vebrator 手机震动的服务 CONNECTIVITY_SERVICE Connectivity 网络连接的服务 WIFI_SERVICE WifiManager Wi-Fi服务 TELEPHONY_SERVICE TeleponyManager 电话服务  */}/** 得到数据的总数 */@Overridepublic int getCount() {return data.size();}/** 根据数据索引,得到集合中所对应的数据 */@Overridepublic Object getItem(int position) {return data.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ImageView imageView = null;TextView textView = null;if (convertView == null) {convertView = layoutInflater.inflate(listviewItem, null);imageView = (ImageView) convertView.findViewById(R.id.imageView);textView = (TextView) convertView.findViewById(R.id.textView);convertView.setTag(new DataWrapper(imageView, textView));//将内容包装起来以备以后使用} else {DataWrapper dataWrapper=(DataWrapper) convertView.getTag();//将包装类取出来//从包装类中取数据imageView=dataWrapper.getImageView();textView=dataWrapper.getTextView();}Contact contact=data.get(position);textView.setText(contact.getName());/**异步加载图片文件*/asynchImageLoad(imageView,contact.getImage());return convertView;}/*//该方法会创建很多的线程,也会很耗资源private void asynchImageLoad(final ImageView imageView, final String imagePath) {final Handler handler=new Handler(){@Overridepublic void handleMessage(Message msg) {//运行在主线程中Uri uri=(Uri) msg.obj;if (uri!=null&&imageView!=null) {imageView.setImageURI(uri);}}};Runnable runnable=new Runnable() {@Overridepublic void run() {try {Uri uri=ContactService.getImage(imagePath, cache);handler.sendMessage(handler.obtainMessage(10,uri));} catch (Exception e) {e.printStackTrace();}}};new Thread(runnable).start();}*//**异步加载图片文件*/private void asynchImageLoad(ImageView imageView, String imagePath) {AsycImageTask asycImageTask=new AsycImageTask(imageView);asycImageTask.execute(imagePath);}/** * 使用AsyncTask提高性能 * 可选方法:1,  onprogressupdate(progress…) 可以使用进度条增加用户体验度。此方法在主线程执行,用户显示任务执行的进度。2,  onpreExecute()  这里是最新用户调用excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。3,  onCancelled()  用户调用取消时,要做的操作。 AsyncTask<Params, Progress, Result>  AsyscTask定义了三种泛型类型params,progress和result. 1,  params启动任务执行的输入参数,比如http请求的URL 2,  progress后台任务执行的百分比 3,  result后台执行任务最终返回的结果,比如String,比如我需要得到的list。使用AsyncTask类,遵守的准则:1,  Task的实例必须在UI thread中创建;2,  Execute方法必须在UI thread中调用3,  不要手动的调用onPfreexecute(),onPostExecute(result)Doinbackground(params…),onProgressupdate(progress…)这几个方法;4,  该task只能被执行一次,否则多次调用时将会出现异常;AsyncTask的整个调用过程都是从execute方法开始的,一旦在主线程中调用execute方法,就可以通过onpreExecute方法,这是一个预处理方法,比如可以在这里开始一个进度框,同样也可以通过onprogressupdate方法给用户一个进度条的显示,增加用户体验;最后通过onpostexecute方法,相当于handler处理UI的方式,在这里可以使用在doinbackground得到的结果处理操作UI。此方法在主线程执行,任务执行的结果作为此方法的参数返回 */private final class AsycImageTask extends AsyncTask<String, Integer, Uri>{private ImageView imageView;public AsycImageTask(ImageView imageView) {this.imageView=imageView;}/** *  后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用Public progress(progress…)来更新任务的进度。 */@Overrideprotected Uri doInBackground(String... params) {//子线程中执行try {return ContactService.getImage(params[0], cache);} catch (Exception e) {e.printStackTrace();}return null;}/** * 相当于handler处理UI的方式,在这里可以使用在doinbackground得到的结果 * 处理操作UI。此方法在主线程执行,任务执行的结果作为此方法的参数返回。 */@Overrideprotected void onPostExecute(Uri result) {//运行在主线程if (result!=null&&imageView!=null) {imageView.setImageURI(result);}}}/**数据包装类*/private final class DataWrapper {private ImageView imageView;private TextView textView;public ImageView getImageView() {return imageView;}public TextView getTextView() {return textView;}public DataWrapper(ImageView imageView, TextView textView) {this.imageView = imageView;this.textView = textView;}}}


step7:应用的主程序 cn.roco.data.MainActivity.java

package cn.roco.data;import java.io.File;import java.util.List;import cn.roco.data.adapter.ContactAdapter;import cn.roco.data.domain.Contact;import cn.roco.data.service.ContactService;import android.app.Activity;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.widget.ListView;public class MainActivity extends Activity {private ListView listView;/**缓存文件*/private File cache;/**接受消息,处理消息 ,此Handler会与当前主线程一块运行 * 使用匿名内部类来复写Handler当中的handlerMessage()方法  */Handler handler = new Handler() {// 接受数据public void handleMessage(android.os.Message msg) {//设置适配器,将获取的数据使用适配器更新ViewlistView.setAdapter(new ContactAdapter(MainActivity.this,(List<Contact>) msg.obj, R.layout.listview_item, cache));};};@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);listView = (ListView) this.findViewById(R.id.listView);/**在SD卡中生成缓存目录*/cache = new File(Environment.getExternalStorageDirectory(), "cache");/**如果目录不存在就新建一个*/if (!cache.exists())  cache.mkdir();new Thread(new Runnable() {@Overridepublic void run() {try {//获取联系人数据List<Contact> data= ContactService.getContacts();// 向Handler发送消息,更新UIhandler.sendMessage(handler.obtainMessage(22, data));} catch (Exception e) {e.printStackTrace();}}}).start();}@Overrideprotected void onDestroy() {/**清除缓存文件*/for (File file:cache.listFiles()) {file.delete();}cache.delete();super.onDestroy();}}

step8:AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"package="cn.roco.data" android:versionCode="1" android:versionName="1.0"><uses-sdk android:minSdkVersion="8" /><!-- 访问Internet权限 --><uses-permission android:name="android.permission.INTERNET" /><!-- 在SD卡中创建和删除文件权限 --><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/><!-- 往SD卡中写入数据权限 --><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><application android:icon="@drawable/icon" android:label="@string/app_name"><activity android:name=".MainActivity" android:label="@string/app_name"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>

step9:编写服务器端的代码,主要是一个contact.xml文件

<?xml version="1.0" encoding="UTF-8"?><contacts><contact id="1"><name>Roco_1</name><image src="http://192.168.1.100:8080/Hello/images/1.png" /></contact><contact id="2"><name>Roco_2</name><image src="http://192.168.1.100:8080/Hello/images/2.png" /></contact><contact id="3"><name>Roco_3</name><image src="http://192.168.1.100:8080/Hello/images/3.png" /></contact><contact id="4"><name>Roco_4</name><image src="http://192.168.1.100:8080/Hello/images/4.png" /></contact><contact id="5"><name>Roco_5</name><image src="http://192.168.1.100:8080/Hello/images/5.png" /></contact><contact id="6"><name>Roco_6</name><image src="http://192.168.1.100:8080/Hello/images/6.png" /></contact><contact id="7"><name>Roco_7</name><image src="http://192.168.1.100:8080/Hello/images/7.png" /></contact><contact id="8"><name>Roco_8</name><image src="http://192.168.1.100:8080/Hello/images/8.png" /></contact><contact id="9"><name>Roco_9</name><image src="http://192.168.1.100:8080/Hello/images/9.png" /></contact><contact id="10"><name>Roco_10</name><image src="http://192.168.1.100:8080/Hello/images/10.png" /></contact><contact id="11"><name>Roco_11</name><image src="http://192.168.1.100:8080/Hello/images/11.png" /></contact><contact id="12"><name>Roco_12</name><image src="http://192.168.1.100:8080/Hello/images/12.png" /></contact><contact id="13"><name>Roco_13</name><image src="http://192.168.1.100:8080/Hello/images/13.png" /></contact><contact id="14"><name>Roco_14</name><image src="http://192.168.1.100:8080/Hello/images/14.png" /></contact><contact id="15"><name>Roco_15</name><image src="http://192.168.1.100:8080/Hello/images/15.png" /></contact><contact id="16"><name>Roco_16</name><image src="http://192.168.1.100:8080/Hello/images/16.png" /></contact><contact id="17"><name>Roco_17</name><image src="http://192.168.1.100:8080/Hello/images/17.png" /></contact><contact id="18"><name>Roco_18</name><image src="http://192.168.1.100:8080/Hello/images/18.png" /></contact><contact id="19"><name>Roco_19</name><image src="http://192.168.1.100:8080/Hello/images/19.png" /></contact><contact id="20"><name>Roco_20</name><image src="http://192.168.1.100:8080/Hello/images/20.png" /></contact></contacts>

以及在images目录下放置了一些图片

android异步加载图片显示,并且对图片进行缓存实例_第3张图片" width="273" height="442" style="border:1px solid black;">


step10:将项目部署到模拟器上运行效果如下图:

android异步加载图片显示,并且对图片进行缓存实例_第4张图片" width="374" height="534" style="border:1px solid black;"> android异步加载图片显示,并且对图片进行缓存实例_第5张图片" width="378" height="536" style="border:1px solid black;">


==================================下面看一个gif动画===========================================

android异步加载图片显示,并且对图片进行缓存实例_第6张图片" width="370" height="527" style="border:1px solid black;">



在SD卡中会生成缓存文件

android异步加载图片显示,并且对图片进行缓存实例_第7张图片" width="650" height="329" style="border:1px solid black;">

当应用退出的时候,会将缓存文件删除

android异步加载图片显示,并且对图片进行缓存实例_第8张图片" width="650" height="116" style="border:1px solid black;">


有了缓存文件,只要应用没有退出,即使联网不成功,也可以读取缓存中的图片文件



==================================================================================================

作者:欧阳鹏 欢迎转载,与人分享是进步的源泉!

转载请保留原文地址:http://blog.csdn.net/ouyang_peng

==================================================================================================


更多相关文章

  1. Android(安卓)Jetpack Components of Lifecycle 学习笔记
  2. android那点事
  3. Android(安卓)基础知识复习
  4. MediaRecorder流程分析
  5. Android之解决ViewPager2+PhotoView滑动图片花屏问题
  6. Android图片下载缓存库picasso解析
  7. Android(安卓)Studio中图片的格式转换
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. centos 7 修改主机名
  2. centos临时ip和永久ip的配置
  3. Android的HTTP基础与之使用HttpClient和H
  4. Python+Dash快速web应用开发——基础概念
  5. 地图可视化神器kepler.gl近期重要更新
  6. Android(安卓)样式之shape入门使用
  7. 12个令人惊奇的CSS实验项目 [每日前端夜
  8. 使用Python验证常见的50个正则表达式
  9. 计算机专业学习的一些思考和总结
  10. 这个库堪称Python编程的瑞士军刀!