先来看看效果示意图:
step1:新建项目DataAsyncLoad,如下图所示
step2:设置应用的UI界面
a.应用的主界面 main.xml
b.每个ListView的界面 listview_item.xml
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 ListgetContacts() 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文件如下。。。。。。 * */ private static List ....... Roco_1 ![]()
parseXML(InputStream inputStream) throws Exception { List contacts = new ArrayList (); 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 Liststep7:应用的主程序 cn.roco.data.MainActivity.javadata; private int listviewItem; private File cache; /** * LayoutInflater的作用类似于 findViewById(),不同点是LayoutInflater是用来找layout文件夹下的xml布局文件,并且实例化! * 而 findViewById()是找具体某一个xml下的具体 widget控件(如:Button,TextView等)。 */ private LayoutInflater layoutInflater; public ContactAdapter(Context context, List 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 电话服务 */ } /** 得到数据的总数 */ @Override public int getCount() { return data.size(); } /** 根据数据索引,得到集合中所对应的数据 */ @Override public Object getItem(int position) { return data.get(position); } @Override public long getItemId(int position) { return position; } @Override public 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(){ @Override public void handleMessage(Message msg) {//运行在主线程中 Uri uri=(Uri) msg.obj; if (uri!=null&&imageView!=null) { imageView.setImageURI(uri); } } }; Runnable runnable=new Runnable() { @Override public 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 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 { private ImageView imageView; public AsycImageTask(ImageView imageView) { this.imageView=imageView; } /** * 后台执行,比较耗时的操作都可以放在这里。 注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作 ,通常需要较长的时间。在执行过程中可以调用 Public progress(progress…)来更新任务的进度。 */ @Override protected Uri doInBackground(String... params) {//子线程中执行 try { return ContactService.getImage(params[0], cache); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 相当于handler处理UI的方式,在这里可以使用在doinbackground得到的结果 * 处理操作UI。此方法在主线程执行,任务执行的结果作为此方法的参数返回。 */ @Override protected 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; } }}
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) { //设置适配器,将获取的数据使用适配器更新View listView.setAdapter(new ContactAdapter(MainActivity.this, (List) msg.obj, R.layout.listview_item, cache)); }; }; @Override public 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() { @Override public void run() { try { //获取联系人数据 List data= ContactService.getContacts(); // 向Handler发送消息,更新UI handler.sendMessage(handler.obtainMessage(22, data)); } catch (Exception e) { e.printStackTrace(); } } }).start(); } @Override protected void onDestroy() { /**清除缓存文件*/ for (File file:cache.listFiles()) { file.delete(); } cache.delete(); super.onDestroy(); } }
step8:AndroidManifest.xml
step9:编写服务器端的代码,主要是一个contact.xml文件
以及在images目录下放置了一些图片Roco_1 ![]()
Roco_2 ![]()
Roco_3 ![]()
Roco_4 ![]()
Roco_5 ![]()
Roco_6 ![]()
Roco_7 ![]()
Roco_8 ![]()
Roco_9 ![]()
Roco_10 ![]()
Roco_11 ![]()
Roco_12 ![]()
Roco_13 ![]()
Roco_14 ![]()
Roco_15 ![]()
Roco_16 ![]()
Roco_17 ![]()
Roco_18 ![]()
Roco_19 ![]()
Roco_20 ![]()
step10:将项目部署到模拟器上运行效果如下图:
==================================下面看一个gif动画===========================================
在SD卡中会生成缓存文件
当应用退出的时候,会将缓存文件删除
有了缓存文件,只要应用没有退出,即使联网不成功,也可以读取缓存中的图片文件
==================================================================================================
作者:欧阳鹏 欢迎转载,与人分享是进步的源泉!
转载请保留原文地址:
==================================================================================================