先来看看效果示意图:

[置顶] 我的Android进阶之旅------>android异步加载图片显示,并且对图片进行缓存实例_第1张图片

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

[置顶] 我的Android进阶之旅------>android异步加载图片显示,并且对图片进行缓存实例_第2张图片

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进阶之旅------>android异步加载图片显示,并且对图片进行缓存实例_第3张图片


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

[置顶] 我的Android进阶之旅------>android异步加载图片显示,并且对图片进行缓存实例_第4张图片 [置顶] 我的Android进阶之旅------>android异步加载图片显示,并且对图片进行缓存实例_第5张图片


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

[置顶] 我的Android进阶之旅------>android异步加载图片显示,并且对图片进行缓存实例_第6张图片



在SD卡中会生成缓存文件

[置顶] 我的Android进阶之旅------>android异步加载图片显示,并且对图片进行缓存实例_第7张图片

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

[置顶] 我的Android进阶之旅------>android异步加载图片显示,并且对图片进行缓存实例_第8张图片


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



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

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

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

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


更多相关文章

  1. 探讨android图片资源的抖动处理和格式转换
  2. Android之解决ViewPager2+PhotoView滑动图片花屏问题
  3. Android Studio中图片的格式转换
  4. 【Android开源项目分析】android轻量级开源缓存框架——ASimpleC
  5. Android方法数超出限定的问题(multiDex,jumboMode)
  6. Android原生方法和Web JS互相调用-两种写法
  7. android 访问网络不能在主线程中进行以及在线程中操作UI的解决方
  8. Android Studio——Android Studio更新升级方法

随机推荐

  1. Android(安卓)Studio适当修改
  2. 《Android应用开发精解》前言
  3. Android开发该何去何从
  4. 构建Android应用程序
  5. 《Android经验分享》周刊第11期
  6. 【Android】Android插件开发 —— 打开插
  7. Android通过软解码播放视频
  8. android初探
  9. Android 多语言动态切换
  10. Android获取音视频原始流数据方法详解