8、Android-搭建简单服务端+ListView异步加载数据

Android-搭建简单服务端+ListView异步加载数据


2014年5月6日

本篇博文带给大家的是教大家如何在MyEclipse中搭建一个服务端,并通过手机端与其通信,异步加载数据。


笔者使用的是MyEclipse,各位也可以直接用Eclipse创建Java Web项目,谷歌提供的ADT Bundle是不能创建Web项目,读者可以下载Eclipse For JaveEE Developer这个IDE。

下面来介绍如何在MyEclipse创建一个Web项目,并部署到Tomcat当中,关于Tomcat的配置笔者在这里就不多说了。

创建一个名为Test的Web项目,项目结构如下图所示:

我创建了一个images文件夹和list.xml文件,images文件夹存放的是一些图片,是我们下面要获取的,list.xml是一个xml文件,内容如下:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <contacts>  
  3.   
  4.     <contact id="1">  
  5.         <name>青蛙1</name>  
  6.         <image src="http://192.192.8.233:8080/Test/images/1.gif" />  
  7.     </contact>  
  8.     <contact id="2">  
  9.         <name>青蛙2</name>  
  10.         <image src="http://192.192.8.233:8080/Test/images/2.gif" />  
  11.     </contact>  
  12.     <contact id="3">  
  13.         <name>青蛙3</name>  
  14.         <image src="http://192.192.8.233:8080/Test/images/3.gif" />  
  15.     </contact>  
  16.     <contact id="4">  
  17.         <name>青蛙4</name>  
  18.         <image src="http://192.192.8.233:8080/Test/images/4.gif" />  
  19.     </contact>  
  20.     <contact id="5">  
  21.         <name>青蛙5</name>  
  22.         <image src="http://192.192.8.233:8080/Test/images/5.gif" />  
  23.     </contact>  
  24.     <contact id="6">  
  25.         <name>青蛙6</name>  
  26.         <image src="http://192.192.8.233:8080/Test/images/6.gif" />  
  27.     </contact>  
  28.     <contact id="7">  
  29.         <name>青蛙7</name>  
  30.         <image src="http://192.192.8.233:8080/Test/images/7.gif" />  
  31.     </contact>  
  32.     <contact id="8">  
  33.         <name>青蛙8</name>  
  34.         <image src="http://192.192.8.233:8080/Test/images/8.gif" />  
  35.     </contact>  
  36.     <contact id="9">  
  37.         <name>青蛙9</name>  
  38.         <image src="http://192.192.8.233:8080/Test/images/9.gif" />  
  39.     </contact>  
  40.     <contact id="10">  
  41.         <name>青蛙10</name>  
  42.         <image src="http://192.192.8.233:8080/Test/images/10.gif" />  
  43.     </contact>  
  44.     <contact id="11">  
  45.         <name>青蛙11</name>  
  46.         <image src="http://192.192.8.233:8080/Test/images/11.gif" />  
  47.     </contact>  
  48.     <contact id="12">  
  49.         <name>青蛙12</name>  
  50.         <image src="http://192.192.8.233:8080/Test/images/12.gif" />  
  51.     </contact>  
  52.     <contact id="13">  
  53.         <name>青蛙13</name>  
  54.         <image src="http://192.192.8.233:8080/Test/images/13.gif" />  
  55.     </contact>  
  56.     <contact id="14">  
  57.         <name>青蛙14</name>  
  58.         <image src="http://192.192.8.233:8080/Test/images/14.gif" />  
  59.     </contact>  
  60.     <contact id="15">  
  61.         <name>青蛙15</name>  
  62.         <image src="http://192.192.8.233:8080/Test/images/15.gif" />  
  63.     </contact>  
  64.     <contact id="16">  
  65.         <name>青蛙16</name>  
  66.         <image src="http://192.192.8.233:8080/Test/images/16.gif" />  
  67.     </contact>  
  68.     <contact id="17">  
  69.         <name>青蛙17</name>  
  70.         <image src="http://192.192.8.233:8080/Test/images/17.gif" />  
  71.     </contact>  
  72.     <contact id="18">  
  73.         <name>青蛙18</name>  
  74.         <image src="http://192.192.8.233:8080/Test/images/18.gif" />  
  75.     </contact>  
  76.   
  77. </contacts>  

我们可以看到list.xml最外层是一个contacts标签,里面有多个子contact标签,每个子标签包含id、name和image内容,这就是我们下面要解析的内容对应每一个Contact对象。

这里要提一下,我们看到image标签,src是图片url地址,这个地址是我PC的IP地址,读者在测试的时候需要将这个IP地址改为你的PC的IP地址,如何得到?运行->cmd->ipconfig /all查看ipv4地址,就是你电脑的ip地址了。



创建好Web项目之后,我们在电脑上测试一下,在浏览器输入地址:

http://192.192.8.233:8080/Test/list.xml



看到以上内容,说明我们已经可以访问到我们的服务端了,下面我们就可以开发我们的客户端:

我这里创建了一个07_DataAsyncLoad的项目:

目录结构如下:



因为需要联网,在AndroidManifest.xml设置权限:

[html]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <!-- 联网权限 -->  
  2. <uses-permission android:name="android.permission.INTERNET" />  

根据服务端list.xml,我们需要定义一个实体类:

/07_DataAsyncLoad/src/com/wwj/domain/Contact.java

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.wwj.domain;  
  2.   
  3. /** 
  4.  * 联系人实体类 
  5.  *  
  6.  * @author wwj 
  7.  *  
  8.  */  
  9. public class Contact {  
  10.     public int id;  
  11.     public String name;  
  12.     public String image;  
  13.   
  14.     public Contact(int id, String name, String image) {  
  15.         this.id = id;  
  16.         this.name = name;  
  17.         this.image = image;  
  18.     }  
  19.   
  20.     public Contact() {  
  21.     }  
  22. }  

需要访问服务端并且解析xml文件,我们定义一个服务类:

/07_DataAsyncLoad/src/com/wwj/service/ContactService.java

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.wwj.service;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileOutputStream;  
  5. import java.io.InputStream;  
  6. import java.net.HttpURLConnection;  
  7. import java.net.URL;  
  8. import java.util.ArrayList;  
  9. import java.util.List;  
  10.   
  11. import org.xmlpull.v1.XmlPullParser;  
  12.   
  13. import com.wwj.domain.Contact;  
  14. import com.wwj.utils.MD5;  
  15.   
  16. import android.net.Uri;  
  17. import android.util.Xml;  
  18.   
  19.   
  20. public class ContactService {  
  21.   
  22.     /** 
  23.      * 获取联系人 
  24.      * @return 
  25.      */  
  26.     public static List<Contact> getContacts() throws Exception{  
  27.         // 服务器文件路径  
  28.         String path = "http://192.192.8.233:8080/Test/list.xml";  
  29.         HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();  
  30.         conn.setConnectTimeout(5000);   //设置超时5秒  
  31.         conn.setRequestMethod("GET");   //设置请求方式  
  32.         if(conn.getResponseCode() == 200){  //连接成功返回码200  
  33.             return parseXML(conn.getInputStream());  
  34.         }  
  35.         return null;  
  36.     }  
  37.     /** 
  38.      * 利用pull解析器对xml文件进行解析 
  39.      * @param xml 
  40.      * @return 
  41.      * @throws Exception 
  42.      */  
  43.     private static List<Contact> parseXML(InputStream xml) throws Exception{  
  44.         List<Contact> contacts = new ArrayList<Contact>();  
  45.         Contact contact = null;  
  46.         XmlPullParser pullParser = Xml.newPullParser();  
  47.         pullParser.setInput(xml, "UTF-8");    
  48.         int event = pullParser.getEventType();  //取得开始文档语法  
  49.         while(event != XmlPullParser.END_DOCUMENT){     //只要不等于文档结束事件,循环解析  
  50.             switch (event) {  
  51.             case XmlPullParser.START_TAG:       //开始标签  
  52.                 if("contact".equals(pullParser.getName())){   
  53.                     contact = new Contact();  
  54.                     contact.id = new Integer(pullParser.getAttributeValue(0));    
  55.                 }else if("name".equals(pullParser.getName())){  
  56.                     contact.name = pullParser.nextText();   //取得后面节点的文本值  
  57.                 }else if("image".equals(pullParser.getName())){  
  58.                     contact.image = pullParser.getAttributeValue(0);    //取得第一个属性的值  
  59.                 }  
  60.                 break;  
  61.                   
  62.             case XmlPullParser.END_TAG:     //结束标签  
  63.                 if("contact".equals(pullParser.getName())){  
  64.                     contacts.add(contact);  //将contact对象添加到集合中  
  65.                     contact = null;  
  66.                 }  
  67.                 break;  
  68.             }  
  69.             event = pullParser.next();  //去下一个标签  
  70.         }  
  71.         return contacts;  
  72.     }  
  73.     /** 
  74.      * 获取网络图片,如果图片存在于缓存中,就返回该图片,否则从网络中加载该图片并缓存起来 
  75.      * @param path 图片路径 
  76.      * @return 
  77.      */  
  78.     public static Uri getImage(String path, File cacheDir) throws Exception{// path -> MD5 ->32字符串.jpg  
  79.         File localFile = new File(cacheDir, MD5.getMD5(path)+ path.substring(path.lastIndexOf(".")));  
  80.         if(localFile.exists()){  
  81.             return Uri.fromFile(localFile);  
  82.         }else{  
  83.             HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();  
  84.             conn.setConnectTimeout(5000);  
  85.             conn.setRequestMethod("GET");  
  86.             if(conn.getResponseCode() == 200){  
  87.                 FileOutputStream outStream = new FileOutputStream(localFile);  
  88.                 InputStream inputStream = conn.getInputStream();  
  89.                 byte[] buffer = new byte[1024];  
  90.                 int len = 0;  
  91.                 while( (len = inputStream.read(buffer)) != -1){  
  92.                     outStream.write(buffer, 0, len);  
  93.                 }  
  94.                 inputStream.close();  
  95.                 outStream.close();  
  96.                 return Uri.fromFile(localFile);  
  97.             }  
  98.         }  
  99.         return null;  
  100.     }  
  101.   
  102. }  

上面代码已经很清楚的定义了获取服务端数据的方法,大致流程是这样的:传递一个网络路径path,通过URL打开连接,通过HttpURLConnection连接服务端,得到输入流,解析xml文件再获得数据。


上面代码获取网络图片,需要进行MD5加密计算,具体方法如下:

/07_DataAsyncLoad/src/com/wwj/utils/MD5.java

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.wwj.utils;  
  2.   
  3. import java.security.MessageDigest;  
  4. import java.security.NoSuchAlgorithmException;  
  5.   
  6. public class MD5 {  
  7.   
  8.     /** 
  9.      * MD5加密算法 
  10.      *  
  11.      * @param content 
  12.      * @return 
  13.      */  
  14.     public static String getMD5(String content) {  
  15.         try {  
  16.             MessageDigest digest = MessageDigest.getInstance("MD5");  
  17.             digest.update(content.getBytes());  
  18.             return getHashString(digest);  
  19.   
  20.         } catch (NoSuchAlgorithmException e) {  
  21.             e.printStackTrace();  
  22.         }  
  23.         return null;  
  24.     }  
  25.   
  26.     /** 
  27.      * 获得哈希字符串 
  28.      *  
  29.      * @param digest 
  30.      * @return 
  31.      */  
  32.     private static String getHashString(MessageDigest digest) {  
  33.         StringBuilder builder = new StringBuilder();  
  34.         for (byte b : digest.digest()) {  
  35.             builder.append(Integer.toHexString((b >> 4) & 0xf));  
  36.             builder.append(Integer.toHexString(b & 0xf));  
  37.         }  
  38.         return builder.toString();  
  39.     }  
  40. }  

好,这样我们的服务类就已经写完了,这时我们在MainActivity进行异步加载数据:

/07_DataAsyncLoad/src/com/wwj/asyntask/MainActivity.java

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.wwj.asyntask;  
  2.   
  3. import java.io.File;  
  4. import java.util.List;  
  5.   
  6. import com.wwj.adapter.ContactAdapter;  
  7. import com.wwj.asyntask.R;  
  8. import com.wwj.domain.Contact;  
  9. import com.wwj.service.ContactService;  
  10.   
  11. import android.app.Activity;  
  12. import android.os.Bundle;  
  13. import android.os.Environment;  
  14. import android.os.Handler;  
  15. import android.os.Message;  
  16. import android.widget.ListView;  
  17.   
  18. public class MainActivity extends Activity {  
  19.     ListView listView;  
  20.     File cache; // 缓存文件  
  21.   
  22.     Handler handler = new Handler() {  
  23.         public void handleMessage(Message msg) {  
  24.             listView.setAdapter(new ContactAdapter(MainActivity.this,  
  25.                     (List<Contact>) msg.obj, R.layout.listview_item, cache));  
  26.         }  
  27.     };  
  28.   
  29.     @Override  
  30.     public void onCreate(Bundle savedInstanceState) {  
  31.         super.onCreate(savedInstanceState);  
  32.         setContentView(R.layout.activity_main);  
  33.         listView = (ListView) this.findViewById(R.id.listView);  
  34.   
  35.         cache = new File(Environment.getExternalStorageDirectory(), "cache"); // 实例化缓存文件  
  36.         if (!cache.exists())  
  37.             cache.mkdirs(); // 如果文件不存在,创建  
  38.   
  39.         // 开一个线程来加载数据  
  40.         new Thread(new Runnable() {  
  41.             public void run() {  
  42.                 try {  
  43.                     List<Contact> data = ContactService.getContacts();  
  44.                     // 通过handler来发送消息  
  45.                     handler.sendMessage(handler.obtainMessage(22, data));  
  46.                 } catch (Exception e) {  
  47.                     e.printStackTrace();  
  48.                 }  
  49.             }  
  50.         }).start();  
  51.     }  
  52.   
  53.     @Override  
  54.     protected void onDestroy() {  
  55.         // 删除缓存  
  56.         for (File file : cache.listFiles()) {  
  57.             file.delete();  
  58.         }  
  59.         cache.delete();  
  60.         super.onDestroy();  
  61.     }  
  62.   
  63. }  

这里我们开了一个线程来加载数据,是因为网络操作不能在UI线程中进行,加载完数据后通过Hanlder发送消息,显示列表。


一般情况下,我们获取图片需要另外处理,我们有很多种方法,最常用的就是Handler+Thread和AsyncTask两种,具体实现来看:

/07_DataAsyncLoad/src/com/wwj/adapter/ContactAdapter.java

我们定义了一个列表适配器,用来填充我们的数据,我们的图片异步加载也在这里实现了:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package com.wwj.adapter;  
  2.   
  3. import java.io.File;  
  4. import java.util.List;  
  5.   
  6. import android.content.Context;  
  7. import android.net.Uri;  
  8. import android.os.AsyncTask;  
  9. import android.view.LayoutInflater;  
  10. import android.view.View;  
  11. import android.view.ViewGroup;  
  12. import android.widget.BaseAdapter;  
  13. import android.widget.ImageView;  
  14. import android.widget.TextView;  
  15.   
  16. import com.wwj.asyntask.R;  
  17. import com.wwj.domain.Contact;  
  18. import com.wwj.service.ContactService;  
  19.   
  20. /** 
  21.  * 自定义适配器 
  22.  *  
  23.  * @author wwj 
  24.  *  
  25.  */  
  26. public class ContactAdapter extends BaseAdapter {  
  27.     private List<Contact> data; // 缓存数据  
  28.     private int listviewItem; // 条目id  
  29.     private File cache; // 缓存文件  
  30.     LayoutInflater layoutInflater;  
  31.   
  32.     public ContactAdapter(Context context, List<Contact> data,  
  33.             int listviewItem, File cache) {  
  34.         this.data = data;  
  35.         this.listviewItem = listviewItem;  
  36.         this.cache = cache;  
  37.         layoutInflater = (LayoutInflater) context  
  38.                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);// 获取布局填充服务  
  39.     }  
  40.   
  41.     /** 
  42.      * 得到数据的总数 
  43.      */  
  44.     public int getCount() {  
  45.         return data.size();  
  46.     }  
  47.   
  48.     /** 
  49.      * 根据数据索引得到集合所对应的数据 
  50.      */  
  51.     public Object getItem(int position) {  
  52.         return data.get(position);  
  53.     }  
  54.   
  55.     public long getItemId(int position) {  
  56.         return position;  
  57.     }  
  58.   
  59.     /** 
  60.      * 当listView每显示一个条目的时候,都会调用这个方法 
  61.      */  
  62.     public View getView(int position, View convertView, ViewGroup parent) {  
  63.         ImageView imageView = null;  
  64.         TextView textView = null;  
  65.   
  66.         if (convertView == null) {  
  67.             convertView = layoutInflater.inflate(listviewItem, null); // 获取条目的view对象  
  68.             imageView = (ImageView) convertView.findViewById(R.id.imageView);  
  69.             textView = (TextView) convertView.findViewById(R.id.textView);  
  70.             convertView.setTag(new DataWrapper(imageView, textView));  
  71.         } else {  
  72.             DataWrapper dataWrapper = (DataWrapper) convertView.getTag();  
  73.             imageView = dataWrapper.imageView;  
  74.             textView = dataWrapper.textView;  
  75.         }  
  76.         Contact contact = data.get(position);  
  77.         textView.setText(contact.name);  
  78.         asyncImageLoad(imageView, contact.image);  
  79.         return convertView;  
  80.     }  
  81.   
  82.     private void asyncImageLoad(ImageView imageView, String path) {  
  83.         AsyncImageTask asyncImageTask = new AsyncImageTask(imageView);  
  84.         asyncImageTask.execute(path);  
  85.   
  86.     }  
  87.   
  88.     /** 
  89.      * 使用AsyncTask异步加载图片 
  90.      *  
  91.      * @author Administrator 
  92.      *  
  93.      */  
  94.     private final class AsyncImageTask extends AsyncTask<String, Integer, Uri> {  
  95.         private ImageView imageView;  
  96.   
  97.         public AsyncImageTask(ImageView imageView) {  
  98.             this.imageView = imageView;  
  99.         }  
  100.   
  101.         protected Uri doInBackground(String... params) {// 子线程中执行的  
  102.             try {  
  103.                 return ContactService.getImage(params[0], cache);  
  104.             } catch (Exception e) {  
  105.                 e.printStackTrace();  
  106.             }  
  107.             return null;  
  108.         }  
  109.   
  110.         protected void onPostExecute(Uri result) {// 运行在主线程  
  111.             if (result != null && imageView != null)  
  112.                 imageView.setImageURI(result);  
  113.         }  
  114.     }  
  115.   
  116.     // 使用Handler进行异步加载图片  
  117.     /* 
  118.      * private void asyncImageLoad(final ImageView imageView, final String path) 
  119.      * { 
  120.      *  final Handler handler = new Handler(){ 
  121.      *   public void 
  122.      *      handleMessage(Message msg) {//运行在主线程中  
  123.      *      Uri uri = (Uri)msg.obj; 
  124.      *      if(uri!=null && imageView!= null) imageView.setImageURI(uri); 
  125.      *  }  
  126.      *  }; 
  127.      *  
  128.      * Runnable runnable = new Runnable() { 
  129.      *  public void run() { 
  130.      *   try { 
  131.      *    Uri uri = 
  132.      *          ContactService.getImage(path, cache); 
  133.      *      handler.sendMessage(handler.obtainMessage(10, uri)); 
  134.      *  } catch (Exception e) {  
  135.      *      e.printStackTrace();  
  136.      *   }  
  137.      *  }  
  138.      *  };  
  139.      *   new Thread(runnable).start();  
  140.      *  } 
  141.      */  
  142.     private final class DataWrapper {  
  143.         public ImageView imageView;  
  144.         public TextView textView;  
  145.   
  146.         public DataWrapper(ImageView imageView, TextView textView) {  
  147.             this.imageView = imageView;  
  148.             this.textView = textView;  
  149.         }  
  150.     }  
  151. }  

以上就是本项目所有的代码,运行项目效果如下:



最后附上服务端和客户端源码:http://download.csdn.net/detail/wwj_748/7300567


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值