[置顶] 我的Android进阶之旅------>android异步加载图片显示,并且对图片进行缓存实例
先来看看效果示意图:
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(); }}
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
==================================================================================================
更多相关文章
- Android(安卓)Jetpack Components of Lifecycle 学习笔记
- android那点事
- Android(安卓)基础知识复习
- MediaRecorder流程分析
- Android之解决ViewPager2+PhotoView滑动图片花屏问题
- Android图片下载缓存库picasso解析
- Android(安卓)Studio中图片的格式转换
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用