在android需要大量使用xml進行網絡傳輸時,如何用面向對象的思路降低xml的耦合性並增加重用性

根據一個需求來進行分析。在項目中由於需要經常通過發送接收xml進行網絡通信,從服務器中抓取數據

如果不對xml做優化,需要寫大量的serializer.startTag(null, "header");serializer.endTag(null, "header"); ......如何進行優化呢.


我們來看這個需求,這是一個按照文檔寫出的xml文件作爲發送方,

<?xml version=”1.0” encoding=”utf-8”?>
<message version="1.0">
     <header>              
            <agenterid>889931</agenterid>
            <source>ivr</source>
            <compress>DES</compress>
 
            <messengerid>20131013101533000001</messengerid> 
            <timestamp>20131013101533</timestamp>
            <digest>7ec8582632678032d25866bd4bce114f</digest>

            <transactiontype>12002</transactiontype>
            <username>13200000000</username>
     </header>
     <body>
            <elements>
                  <element>
                         <lotteryid>118</lotteryid>
                         <issues>1</issues>
                  </element>
            </elements> 
     </body>
</message>

裏面的節點意義我就不說了,header表示請求的協議,一般不會變,只有messageid ,timestamp  digest 會有規律的變化,username 和 transactiontype會無規律的變化,body中的elements元素需要進行加密處理,處理完的樣子時<body>sdfsfsdasd34rnh34u53434534534o</body>  是進行des加密的


下面我們根據面向對象的方式來對這個xml進行優化。首先分析這個xml


好的,下面我們開始寫簡單的葉子和值

package com.jrrjw.caipiao.net.protocal;

import org.xmlpull.v1.XmlSerializer;

/**
 * 簡單的葉子和值 <name>value</name>
 * 
 * @author Idea
 * 
 */
public class Leaf {

	private String tagName;

	private String tagValue;

	public void setTagValue(String tagValue) {
		this.tagValue = tagValue;
	}

	/**
	 * 對於一個xml  節點的名稱時必須的
	 * 
	 * @param tagName
	 */
	public Leaf(String tagName) {
		this.tagName = tagName;
	}

	public Leaf(String tagName, String tagValue) {
		this.tagName = tagName;
		this.tagValue = tagValue;
	}

	/**
	 * ͨ通過調用它 的對象提供serializer序列化節點
	 * 
	 * @param serializer
	 */
	public void serializerLeaf(XmlSerializer serializer) {
		try {
			serializer.startTag(null, tagName);
			if (tagValue == null) {
				tagValue = "";
			}
			serializer.text(tagValue);
			serializer.endTag(null, tagName);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}
下面再開始寫header


package com.jrrjw.caipiao.net.protocal;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

import org.apache.commons.codec.digest.DigestUtils;
import org.xmlpull.v1.XmlSerializer;

import com.jrrjw.caipiao.ConstantValue;

public class Header {
	// 這些都是規定好的協議頭
	// <agenterid>889931</agenterid>
	private Leaf agenterid = new Leaf("agenterid", ConstantValue.AGENTERID);
	// <source>ivr</source>
	private Leaf source = new Leaf("source", ConstantValue.SOURCE);
	// <compress>DES</compress>
	private Leaf compress = new Leaf("compress", ConstantValue.COMPRESS);
	// <messengerid>20131013101533000001</messengerid>
	private Leaf messengerid = new Leaf("messengerid");
	// <timestamp>20131013101533</timestamp>
	private Leaf timestamp = new Leaf("timestamp");
	// <digest>7ec8582632678032d25866bd4bce114f</digest>
	private Leaf digest = new Leaf("digest");
	// <transactiontype>12002</transactiontype>
	private Leaf transactiontype = new Leaf("transactiontype");
	// <username>13200000000</username>
	private Leaf username = new Leaf("username");
	/**
	 * 由調用者提供序列化對象,由於header裏的都是固定好的,這裏就不用抽取了。
	 * 
	 * @param serializer
	 */
	public void serializerHeader(XmlSerializer serializer, String body) {
		// 首先保證上面實例化的葉子都有tagValue值
		// messengerid 時間戳+隨機6位數
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
				"yyyyMMddHHmmss");
		String time = simpleDateFormat.format(new Date());
		Random random = new Random();
		int num = random.nextInt(999999) + 1;
		messengerid.setTagValue(time + num);
		timestamp.setTagValue(time);
		// digest 由時間戳+密碼+body原文  然後進行md5加密  
		StringBuffer buffer = new StringBuffer();
		buffer.append(time);
		buffer.append(ConstantValue.AGENTER_PASSWORD);
		buffer.append(body);
		String v = DigestUtils.md5Hex(buffer.toString());
		digest.setTagValue(v);
		try {
			serializer.startTag(null, "header");

			agenterid.serializerLeaf(serializer);
			source.serializerLeaf(serializer);
			compress.serializerLeaf(serializer);

			messengerid.serializerLeaf(serializer);
			timestamp.serializerLeaf(serializer);
			digest.serializerLeaf(serializer);

			transactiontype.serializerLeaf(serializer);
			username.serializerLeaf(serializer);

			serializer.endTag(null, "header");
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
	public Leaf getTransactiontype() {
		return transactiontype;
	}
	public Leaf getUsername() {
		return username;
	}

}

header寫完,下面開始寫body部分了,首先考慮一下這個element的寫法,看代碼,看到最後你就知道爲什麼這麼寫了。

package com.jrrjw.caipiao.net.protocal;

import org.xmlpull.v1.XmlSerializer;

/**
 * 存放葉子的集合,做成抽象類,由子類自定義實現
 * @author Idea
 *
 */
public abstract class Element {
	
		/**
		 * 每個element都需要序列化
		 * @param serializer
		 */
		public abstract void serializerElement(XmlSerializer serializer);
		/**
		 * 每一個Element都需要唯一標示符
		 * @return
		 */
		public abstract String getTransactionType();

}

下面開始寫body

package com.jrrjw.caipiao.net.protocal;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.xmlpull.v1.XmlSerializer;

import android.util.Xml;

import com.jrrjw.caipiao.ConstantValue;
import com.jrrjw.caipiao.utils.DES;

public class Body {

	private List<Element> elements = new ArrayList<Element>();

	public void serializerBody(XmlSerializer serializer) {

		try {
			serializer.startTag(null, "body");
			serializer.startTag(null, "elements");
			if (elements != null) {
				for (Element element : elements) {
					element.serializerElement(serializer);
				}
			}
			serializer.endTag(null, "elements");
			serializer.endTag(null, "body");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public List<Element> getElements() {
		return elements;
	}

	/**
	 * 獲取body裏面的DES加密數據,爲了安全需要加密,這段可以在JNI裏實現,這裏簡單起見
	 * @return
	 */
	public String getBodyInsideDESInfo()
	{
		// 加密數據
		String wholeBody = getWholeBody();
		String orgDesInfo=StringUtils.substringBetween(wholeBody, "<body>", "</body>");
		
		// 加密
		// 加密調試——2天
		// ①加密算法實現不同
		// ②加密的原始數據不同
		
		DES des=new DES();
		return des.authcode(orgDesInfo, "DECODE", ConstantValue.DES_PASSWORD);
	}
	
	/**
	 * 由於需求說明,這個方法的目的是在header裏提供一個body的未加密數據在digest中
	 * @return
	 */
	public String getWholeBody() {
		try {
			// 這裏不能用XmlSerializer serializer 至於爲什麼自己考慮一下就明白了
			XmlSerializer temp = Xml.newSerializer();
			StringWriter writer = new StringWriter();
			temp.setOutput(writer);
			temp.startTag(null, "body");
			temp.startTag(null, "elements");
			if (elements != null) {
				for (Element element : elements) {
					element.serializerElement(temp);
				}
			}
			temp.endTag(null, "elements");
			temp.endTag(null, "body");
			//因爲這個temp並沒有startDocument 所以他不會自動寫閉合標籤,也就不會自動關閉,所以要手動刷一下
			temp.flush();
			return writer.toString();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

}

最後寫Message

package com.jrrjw.caipiao.net.protocal;

import java.io.StringWriter;

import org.xmlpull.v1.XmlSerializer;

import android.util.Xml;

import com.jrrjw.caipiao.ConstantValue;

/**
 * 這裏實現整個xml的框架
 * @author Idea
 *
 */
public class Messager {
	private Header header = new Header();
	private Body body = new Body();
	
	public Header getHeader() {
		return header;
	}

	public Body getBody() {
		return body;
	}

	/**
	 * 序列化協議
	 */
	public void serializerMessage(XmlSerializer serializer) {
		try {
			// <message version="1.0">
			serializer.startTag(null, "message");
			// MUST follow a call to startTag() immediately
			serializer.attribute(null, "version", "1.0");

			header.serializerHeader(serializer, body.getWholeBody());// 獲取完整的body
//			body.serializerBody(serializer);這裏就沒有調用這個方法,
			serializer.startTag(null, "body");
			serializer.text(body.getBodyInsideDESInfo());
			serializer.endTag(null, "body");

			serializer.endTag(null, "message");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 獲取請求的xml文件
	 * 
	 * @return
	 */
	public String getXml(Element element) {
		if (element == null) {
			throw new IllegalArgumentException("element is null");
		}
		// 請求標示需要設置,請求內容需要設置
		header.getTransactiontype().setTagValue(element.getTransactionType());
		body.getElements().add(element);

		// 序列化
		XmlSerializer serializer = Xml.newSerializer();
		StringWriter writer = new StringWriter();
		// This method can only be called just after setOutput
		try {
			serializer.setOutput(writer);
			serializer.startDocument(ConstantValue.ENCONDING, null);
			this.serializerMessage(serializer);
			serializer.endDocument();

			return writer.toString();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

}

xml已經基本抽象出來可以作爲通用了,那麼我們怎麼用呢。

package com.jrrjw.caipiao.net.protocal.element;

import org.xmlpull.v1.XmlSerializer;

import com.jrrjw.caipiao.net.protocal.Element;
import com.jrrjw.caipiao.net.protocal.Leaf;

/**
 * 這個類的目的時定義body體的內容
 * @author Idea
 *
 */
public class TestElement extends Element{
	//可以賦值,這樣做更好
	private Leaf lotteryid = new Leaf("lotteryid");
	private Leaf issues = new Leaf("issues","1");
	public Leaf getLotteryid() {
		return lotteryid;
	}
//	public Leaf getIssues() {
//		return issues;
//	}
	@Override
	public void serializerElement(XmlSerializer serializer) {
		try {
			serializer.startTag(null, "element");
			lotteryid.serializerLeaf(serializer);
			issues.serializerLeaf(serializer);
			serializer.endTag(null, "element");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	@Override
	public String getTransactionType() {
		return "121212";
	}
	
	

}

最後看看測試結果


package com.jrrjw.caipiao.test;

import android.test.AndroidTestCase;

import com.jrrjw.caipiao.net.protocal.Messager;
import com.jrrjw.caipiao.net.protocal.element.TestElement;

public class XmlTest extends AndroidTestCase{
	
	public void test1(){
		
		TestElement element  = new TestElement();
		element.getLotteryid().setTagValue("118");
		Messager messager = new Messager();
		messager.getHeader().getUsername().setTagValue("idea");
		String value = messager.getXml(element);
		System.out.println(value);
	}

}
這裏用的android測試需要加上

<instrumentation android:targetPackage="com.jrrjw.caipiao" android:name="android.test.InstrumentationTestRunner" />
 <uses-library android:name="android.test.runner" />

這裏只是把發送的內容抽象了出來,也可以把接收的內容抽象到一起,其實很簡單,這裏就不寫了。


用到的des加密工具類

package com.jrrjw.caipiao.utils;


import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

public class DES {

	public DES() {
	}

	public static DES getInstance(String key) throws NoSuchPaddingException,
			NoSuchAlgorithmException {
		return getInstance(getKeyByStr(key));
	}

	public static DES getInstance(byte key[]) throws NoSuchPaddingException,
			NoSuchAlgorithmException {
		DES des = new DES();
		if (des.key == null) {
			SecretKeySpec spec = new SecretKeySpec(key, "DES");
			des.key = spec;
		}
		des.cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
		return des;
	}

	public byte[] encrypt(byte b[]) throws InvalidKeyException,
			BadPaddingException, IllegalBlockSizeException,
			IllegalStateException {
		byte byteFina[] = null;
		cipher.init(1, key);
		byteFina = cipher.doFinal(b);
		return byteFina;
	}

	public byte[] decrypt(byte b[]) throws InvalidKeyException,
			BadPaddingException, IllegalBlockSizeException,
			IllegalStateException {
		byte byteFina[] = null;
		cipher.init(2, key);
		byteFina = cipher.doFinal(b);
		return byteFina;
	}

	public static byte[] getKeyByStr(String str) {
		byte bRet[] = new byte[str.length() / 2];
		for (int i = 0; i < str.length() / 2; i++) {
			Integer itg = new Integer(16 * getChrInt(str.charAt(2 * i))
					+ getChrInt(str.charAt(2 * i + 1)));
			bRet[i] = itg.byteValue();
		}
		return bRet;
	}

	private static int getChrInt(char chr) {
		int iRet = 0;
		if (chr == "0".charAt(0))
			iRet = 0;
		if (chr == "1".charAt(0))
			iRet = 1;
		if (chr == "2".charAt(0))
			iRet = 2;
		if (chr == "3".charAt(0))
			iRet = 3;
		if (chr == "4".charAt(0))
			iRet = 4;
		if (chr == "5".charAt(0))
			iRet = 5;
		if (chr == "6".charAt(0))
			iRet = 6;
		if (chr == "7".charAt(0))
			iRet = 7;
		if (chr == "8".charAt(0))
			iRet = 8;
		if (chr == "9".charAt(0))
			iRet = 9;
		if (chr == "A".charAt(0))
			iRet = 10;
		if (chr == "B".charAt(0))
			iRet = 11;
		if (chr == "C".charAt(0))
			iRet = 12;
		if (chr == "D".charAt(0))
			iRet = 13;
		if (chr == "E".charAt(0))
			iRet = 14;
		if (chr == "F".charAt(0))
			iRet = 15;
		return iRet;
	}

	private Key key;
	private Cipher cipher;

	/**
	 * @interpret 進行base64加密操作
	 * @param text
	 * @param keyString
	 * @return String
	 */
	public String encrypt(String text, String keyString) {
		String body = null;

		try {
			DES des = DES.getInstance(keyString);

			byte[] b = des.encrypt(text.getBytes("UTF8"));
			body = new String(Base64.encodeBase64(b));
		} catch (Exception ex) {

		}
		return body;
	}

	/**
	 * @interpret 進行base64進行解密
	 * @param text
	 * @param keyString
	 * @return String
	 */
	public String decrypt(String text, String keyString) {
		String body = null;
		try {
			DES des = DES.getInstance(keyString);

			byte[] b = Base64.decodeBase64(text.getBytes());

			body = new String(des.decrypt(b), "UTF8");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return body;
	}
	/**
	 * 
	 * @param content  內容
	 * @param operation 加密或解密
	 * @param key 使用到的密鑰:固定長度
	 * @return
	 */
	public String authcode(String content, String operation, String key){
		
		String encontent = null;
		if (operation != null && operation.equals("DECODE")) {
			encontent = encrypt(content,key);
		} else if (operation != null && operation.equals("ENCODE")) {
			encontent = decrypt(content,key);
		}
		return encontent;
	}

	public static void main(String[] args) {
		DES des = new DES();
		System.out.println(des.authcode("wewweewewew=","DECODE","0102030405060708"));//加密
		System.out.println(des.authcode("d8fPhfd9JkW99p8aqhtVIA==","ENCODE","0102030405060708"));//解密
	}
}


用到的jar包

commons-codec.jar

commons-lang3-3.0-beta.jar


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章