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文件,內容如下:vcD48cD48cHJlIGNsYXNzPQ==”brush:java;”>

青蛙1

青蛙2

青蛙3

青蛙4

青蛙5

青蛙6

青蛙7

青蛙8

青蛙9

青蛙10

青蛙11

青蛙12

青蛙13

青蛙14

青蛙15

青蛙16

青蛙17

青蛙18

我們可以看到list.xml最外層是一個contacts標簽,裡面有多個子contact標簽,每個子標簽包含id、name和image內容,這就是我們下面要解析的內容對應每一個Contact對象。

這裡要提一下,我們看到image標簽,src是圖片url地址,這個地址是我PC的IP地址,讀者在測試的時候需要將這個IP地址改為你的PC的IP地址,如何得到?運行->cmd->ipconfig /all查看ipv4地址,就是你電腦的ip地址瞭。

創建好Web項目之後,我們在電腦上測試一下,在瀏覽器輸入地址:

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

看到以上內容,說明我們已經可以訪問到我們的服務端瞭,下面我們就可以開發我們的客戶端:

我這裡創建瞭一個07_DataAsyncLoad的項目:

目錄結構如下:

因為需要聯網,在AndroidManifest.xml設置權限:

    
    

根據服務端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 getContacts() throws Exception{
		// 服務器文件路徑
		String path = "https://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 parseXML(InputStream xml) throws Exception{
		List contacts = new ArrayList();
		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) 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 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 data; // 緩存數據
	private int listviewItem; // 條目id
	private File cache; // 緩存文件
	LayoutInflater layoutInflater;

	public ContactAdapter(Context context, List 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 {
		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;
		}
	}
}

以上就是本項目所有的代碼,運行項目效果如下:

[?棣薻喎?/ym/源碼:https://download.csdn.net/detail/wwj_748/7300567

發佈留言