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文件,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<contacts>

	<contact id="1">
		<name>青蛙1</name>
		<image src="http://192.192.8.233:8080/Test/images/1.gif" />
	</contact>
	<contact id="2">
		<name>青蛙2</name>
		<image src="http://192.192.8.233:8080/Test/images/2.gif" />
	</contact>
	<contact id="3">
		<name>青蛙3</name>
		<image src="http://192.192.8.233:8080/Test/images/3.gif" />
	</contact>
	<contact id="4">
		<name>青蛙4</name>
		<image src="http://192.192.8.233:8080/Test/images/4.gif" />
	</contact>
	<contact id="5">
		<name>青蛙5</name>
		<image src="http://192.192.8.233:8080/Test/images/5.gif" />
	</contact>
	<contact id="6">
		<name>青蛙6</name>
		<image src="http://192.192.8.233:8080/Test/images/6.gif" />
	</contact>
	<contact id="7">
		<name>青蛙7</name>
		<image src="http://192.192.8.233:8080/Test/images/7.gif" />
	</contact>
	<contact id="8">
		<name>青蛙8</name>
		<image src="http://192.192.8.233:8080/Test/images/8.gif" />
	</contact>
	<contact id="9">
		<name>青蛙9</name>
		<image src="http://192.192.8.233:8080/Test/images/9.gif" />
	</contact>
	<contact id="10">
		<name>青蛙10</name>
		<image src="http://192.192.8.233:8080/Test/images/10.gif" />
	</contact>
	<contact id="11">
		<name>青蛙11</name>
		<image src="http://192.192.8.233:8080/Test/images/11.gif" />
	</contact>
	<contact id="12">
		<name>青蛙12</name>
		<image src="http://192.192.8.233:8080/Test/images/12.gif" />
	</contact>
	<contact id="13">
		<name>青蛙13</name>
		<image src="http://192.192.8.233:8080/Test/images/13.gif" />
	</contact>
	<contact id="14">
		<name>青蛙14</name>
		<image src="http://192.192.8.233:8080/Test/images/14.gif" />
	</contact>
	<contact id="15">
		<name>青蛙15</name>
		<image src="http://192.192.8.233:8080/Test/images/15.gif" />
	</contact>
	<contact id="16">
		<name>青蛙16</name>
		<image src="http://192.192.8.233:8080/Test/images/16.gif" />
	</contact>
	<contact id="17">
		<name>青蛙17</name>
		<image src="http://192.192.8.233:8080/Test/images/17.gif" />
	</contact>
	<contact id="18">
		<name>青蛙18</name>
		<image src="http://192.192.8.233:8080/Test/images/18.gif" />
	</contact>

</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设置权限:

    <!-- 联网权限 -->
    <uses-permission android:name="android.permission.INTERNET" />

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

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

package com.wwj.domain;

/**
 * 联系人实体类
 *
 * @author wwj
 *
 */
public class Contact {
	public int id;
	public String name;
	public String image;

	public Contact(int id, String name, String image) {
		this.id = id;
		this.name = name;
		this.image = image;
	}

	public Contact() {
	}
}

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

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

package com.wwj.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 com.wwj.domain.Contact;
import com.wwj.utils.MD5;

import android.net.Uri;
import android.util.Xml;

public class ContactService {

	/**
	 * 获取联系人
	 * @return
	 */
	public static List<Contact> getContacts() throws Exception{
		// 服务器文件路径
		String path = "http://192.192.8.233:8080/Test/list.xml";
		HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();
		conn.setConnectTimeout(5000);	//设置超时5秒
		conn.setRequestMethod("GET");	//设置请求方式
		if(conn.getResponseCode() == 200){	//连接成功返回码200
			return parseXML(conn.getInputStream());
		}
		return null;
	}
	/**
	 * 利用pull解析器对xml文件进行解析
	 * @param xml
	 * @return
	 * @throws Exception
	 */
	private static List<Contact> parseXML(InputStream xml) throws Exception{
		List<Contact> contacts = new ArrayList<Contact>();
		Contact contact = null;
		XmlPullParser pullParser = Xml.newPullParser();
		pullParser.setInput(xml, "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.id = new Integer(pullParser.getAttributeValue(0));
				}else if("name".equals(pullParser.getName())){
					contact.name = pullParser.nextText();	//取得后面节点的文本值
				}else if("image".equals(pullParser.getName())){
					contact.image = pullParser.getAttributeValue(0);	//取得第一个属性的值
				}
				break;

			case XmlPullParser.END_TAG:		//结束标签
				if("contact".equals(pullParser.getName())){
					contacts.add(contact);	//将contact对象添加到集合中
					contact = null;
				}
				break;
			}
			event = pullParser.next();	//去下一个标签
		}
		return contacts;
	}
	/**
	 * 获取网络图片,如果图片存在于缓存中,就返回该图片,否则从网络中加载该图片并缓存起来
	 * @param path 图片路径
	 * @return
	 */
	public static Uri getImage(String path, File cacheDir) throws Exception{// path -> MD5 ->32字符串.jpg
		File localFile = new File(cacheDir, MD5.getMD5(path)+ path.substring(path.lastIndexOf(".")));
		if(localFile.exists()){
			return Uri.fromFile(localFile);
		}else{
			HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();
			conn.setConnectTimeout(5000);
			conn.setRequestMethod("GET");
			if(conn.getResponseCode() == 200){
				FileOutputStream outStream = new FileOutputStream(localFile);
				InputStream inputStream = conn.getInputStream();
				byte[] buffer = new byte[1024];
				int len = 0;
				while( (len = inputStream.read(buffer)) != -1){
					outStream.write(buffer, 0, len);
				}
				inputStream.close();
				outStream.close();
				return Uri.fromFile(localFile);
			}
		}
		return null;
	}

}

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

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

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

package com.wwj.utils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5 {

	/**
	 * MD5加密算法
	 *
	 * @param content
	 * @return
	 */
	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;
	}

	/**
	 * 获得哈希字符串
	 *
	 * @param digest
	 * @return
	 */
	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();
	}
}

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

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

package com.wwj.asyntask;

import java.io.File;
import java.util.List;

import com.wwj.adapter.ContactAdapter;
import com.wwj.asyntask.R;
import com.wwj.domain.Contact;
import com.wwj.service.ContactService;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.widget.ListView;

public class MainActivity extends Activity {
	ListView listView;
	File cache; // 缓存文件

	Handler handler = new Handler() {
		public void handleMessage(Message msg) {
			listView.setAdapter(new ContactAdapter(MainActivity.this,
					(List<Contact>) msg.obj, R.layout.listview_item, cache));
		}
	};

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		listView = (ListView) this.findViewById(R.id.listView);

		cache = new File(Environment.getExternalStorageDirectory(), "cache"); // 实例化缓存文件
		if (!cache.exists())
			cache.mkdirs(); // 如果文件不存在,创建

		// 开一个线程来加载数据
		new Thread(new Runnable() {
			public void run() {
				try {
					List<Contact> data = ContactService.getContacts();
					// 通过handler来发送消息
					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();
	}

}

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

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

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

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

package com.wwj.adapter;

import java.io.File;
import java.util.List;

import android.content.Context;
import android.net.Uri;
import android.os.AsyncTask;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.wwj.asyntask.R;
import com.wwj.domain.Contact;
import com.wwj.service.ContactService;

/**
 * 自定义适配器
 *
 * @author wwj
 *
 */
public class ContactAdapter extends BaseAdapter {
	private List<Contact> data; // 缓存数据
	private int listviewItem; // 条目id
	private File cache; // 缓存文件
	LayoutInflater layoutInflater;

	public ContactAdapter(Context context, List<Contact> data,
			int listviewItem, File cache) {
		this.data = data;
		this.listviewItem = listviewItem;
		this.cache = cache;
		layoutInflater = (LayoutInflater) context
				.getSystemService(Context.LAYOUT_INFLATER_SERVICE);// 获取布局填充服务
	}

	/**
	 * 得到数据的总数
	 */
	public int getCount() {
		return data.size();
	}

	/**
	 * 根据数据索引得到集合所对应的数据
	 */
	public Object getItem(int position) {
		return data.get(position);
	}

	public long getItemId(int position) {
		return position;
	}

	/**
	 * 当listView每显示一个条目的时候,都会调用这个方法
	 */
	public View getView(int position, View convertView, ViewGroup parent) {
		ImageView imageView = null;
		TextView textView = null;

		if (convertView == null) {
			convertView = layoutInflater.inflate(listviewItem, null); // 获取条目的view对象
			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.imageView;
			textView = dataWrapper.textView;
		}
		Contact contact = data.get(position);
		textView.setText(contact.name);
		asyncImageLoad(imageView, contact.image);
		return convertView;
	}

	private void asyncImageLoad(ImageView imageView, String path) {
		AsyncImageTask asyncImageTask = new AsyncImageTask(imageView);
		asyncImageTask.execute(path);

	}

	/**
	 * 使用AsyncTask异步加载图片
	 *
	 * @author Administrator
	 *
	 */
	private final class AsyncImageTask extends AsyncTask<String, Integer, Uri> {
		private ImageView imageView;

		public AsyncImageTask(ImageView imageView) {
			this.imageView = imageView;
		}

		protected Uri doInBackground(String... params) {// 子线程中执行的
			try {
				return ContactService.getImage(params[0], cache);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return null;
		}

		protected void onPostExecute(Uri result) {// 运行在主线程
			if (result != null && imageView != null)
				imageView.setImageURI(result);
		}
	}

	// 使用Handler进行异步加载图片
	/*
	 * private void asyncImageLoad(final ImageView imageView, final String path)
	 * {
	 *  final Handler handler = new Handler(){
	 *   public void
	 * 		handleMessage(Message msg) {//运行在主线程中
	 * 		Uri uri = (Uri)msg.obj;
	 * 		if(uri!=null && imageView!= null) imageView.setImageURI(uri);
	 *  }
	 *  };
	 *
	 * Runnable runnable = new Runnable() {
	 *  public void run() {
	 *   try {
	 *    Uri uri =
	 * 			ContactService.getImage(path, cache);
	 * 		handler.sendMessage(handler.obtainMessage(10, uri));
	 *  } catch (Exception e) {
	 *  	e.printStackTrace();
	 * 	 }
	 *  }
	 *  };
	 * 	 new Thread(runnable).start();
	 *  }
	 */
	private final class DataWrapper {
		public ImageView imageView;
		public TextView textView;

		public DataWrapper(ImageView imageView, TextView textView) {
			this.imageView = imageView;
			this.textView = textView;
		}
	}
}

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

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

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

时间: 2024-10-11 21:03:03

Android-搭建简单服务端+ListView异步加载数据的相关文章

android开发干货:实现listview异步加载图片

针对listview异步加载图片这个问题,麦子学院android开发老师讲了一种非常实用的方法,麦子学院android开发老师说凡是是要通过网络获取图片资源一般使用这种方法比较好,用户体验好,下面就说实现方法,先贴上主方法的代码: package cn.wangmeng.test; import java.io.IOException; import java.io.InputStream; import java.lang.ref.SoftReference; import java.net.

Android ListView异步加载数据

1.主Activity 1 public class MainActivity extends Activity { 2 3 private ListView listView; 4 private ArrayList<Person> persons; 5 private ListAdapter adapter; 6 private Handler handler=null; 7 //xml文件的网络地址 8 final String path="http://192.168.5.1

Android中ListView异步加载数据

http://www.cnblogs.com/snake-hand/p/3206655.html 1.主Activity 1 public class MainActivity extends Activity { 2 3 private ListView listView; 4 private ArrayList<Person> persons; 5 private ListAdapter adapter; 6 private Handler handler=null; 7 //xml文件的

Android之ListView异步加载图片且仅显示可见子项中的图片

折腾了好多天,遇到 N 多让人崩溃无语的问题,不过今天终于有些收获了,这是实验的第一版,有些混乱,下一步进行改造细分,先把代码记录在这儿吧. 网上查了很多资料,发现都千篇一律,抄来抄去,很多细节和完整实例都没看到,只有自己一点点研究了,总体感觉 android 下面要显示个图片真不容易啊. 项目主要实现的功能: 异步加载图片图片内存缓存.异步磁盘文件缓存解决使用 viewHolder 后出现的图片错位问题优化列表滚动性能,仅显示可见子项中的图片无需固定图片显示高度,对高度进行缓存使列表滚动时不会

Android ListView异步加载图片乱序问题,原因分析及解决方案

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/45586553 在Android所有系统自带的控件当中,ListView这个控件算是用法比较复杂的了,关键是用法复杂也就算了,它还经常会出现一些稀奇古怪的问题,让人非常头疼.比如说在ListView中加载图片,如果是同步加载图片倒还好,但是一旦使用异步加载图片那么问题就来了,这个问题我相信很多Android开发者都曾经遇到过,就是异步加载图片会出现错位乱序的情况.遇到这个问题时,不

android listview 异步加载图片并防止错位

网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题. 我简单分析一下: 当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView. 当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Ite

android listview 异步加载图片并防止错位+双缓存

网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题. 我简单分析一下: 当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView. 当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Ite

Android之ListView异步加载网络图片(优化缓存机制)【转】

网上关于这个方面的文章也不少,基本的思路是线程+缓存来解决.下面提出一些优化: 1.采用线程池 2.内存缓存+文件缓存 3.内存缓存中网上很多是采用SoftReference来防止堆溢出,这儿严格限制只能使用最大JVM内存的1/4 4.对下载的图片进行按比例缩放,以减少内存的消耗 具体的代码里面说明.先放上内存缓存类的代码MemoryCache.java: public class MemoryCache { private static final String TAG = "MemoryCa

android listview 异步加载图片并防止错位 解决办法

网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题. 我简单分析一下: 当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView. 当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Ite