協議的封裝和抽取這塊比較復雜,序列化和反序列化,自己做了一點總結,希望大家多多指點。
一.為什么要進行協議封裝
以往的處理方式:利用XmlSerializer一點點編寫協議序列化代碼
存在問題(假設我們有100個請求需要處理):
1、需要將協議中的請求分配給不同的組員進行處理,也就是大部分組員需要學習協議。
2、學習情況檢驗,是不是所有的組員都已經很好的理解了協議(開協議研討會)。
3、進入協議開發階段,類似的代碼需要出現100次,由於不同人員處理,過程中很容易出現錯誤且抽取工作不統一。
4、開發過程中協議修改了,這就需要所有的組員停下手中工作,更改各自編寫的請求代碼(開會布置)。
這種情況下,就會讓我們的開發進程減慢,我們的解決辦法是,對協議進行封裝和抽取。
二.協議封裝的幾個原則
1.節點對象化
節點對象: Leaf 、 Header
Oelement、Element(請求及回復)、 Body
Message
2.節點序列化
•序列化原則:自己管自己
•序列化范圍:只有請求的節點涉及序列化
•序列化順序:Leaf、Header、Element、Body、Message
•Message節點提供請求入口
•Message節點提供對外的xml文件數據提供方法
3.請求接口(抽象)化
Element接口(抽象)化,所有請求必須實現該接口或繼承該抽象類,同時實現定義的獲取請求類型標示和請求序列化的方法。以獲取當前銷售期信息為例說明
4.協議通用化
•協議格式:xml、json
•協議壓縮:wbxml (用於將xml文件進行壓縮)、二進制流
•新協議制定和已有協議
下面就以一個彩票客戶端的選彩協議為例來說明:
信息發送 (查詢指定玩法可銷售期信息)
發送消息的協議如下:
<?xml version=”1.0” encoding=”utf-8”?> <message version="1.0"> <header> <agenterid>889931</agenterid> <source>ivr</source> <compress>DES</compress> <messengerid>20091113101533000001</messengerid> <timestamp>20091113101533</timestamp> <digest>7ec8582632678032d25866bd4bce114f</digest> <transactiontype>12002</transactiontype> <username>13200000000</username> </header> <body> <elements> <element> <lotteryid>118</lotteryid> <issues>1</issues> </element> </elements> </body> </message>
三.開始封裝,對其中一個葉子節點的序列化.
package cn.piaoye.lottery.protocol; import org.apache.commons.lang3.StringUtils; import org.xmlpull.v1.XmlSerializer; /** * 葉子節點 * 標簽名字 * 值 * @author piao * */ public class Leaf { private String tagName; private String value; public Leaf(String tagName, String value) { super(); this.tagName = tagName; this.value = value; } public Leaf(String tagName) { super(); this.tagName = tagName; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } //<agenterid>889931</agenterid> 在這里我們要調用序列化方法,把葉子節點給序列化 /** * 序列化葉子 */ public void serializer(XmlSerializer serializer){ try { serializer.startTag(null,tagName); if(StringUtils.isBlank(value)){ value="";//屏蔽value為空的情況 } serializer.text(value); serializer.endTag(null,tagName); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
四.頭信息的封裝
package cn.piaoye.lottery.protocol; import java.text.DecimalFormat; 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 cn.piaoye.lottery.ConstantValue; /** * 頭信息進行封裝 * @author piao * */ public class Header { //八個葉子 private Leaf agenterid=new Leaf("agenterid", ConstantValue.AGENTERID); private Leaf source=new Leaf("source", ConstantValue.SOURCE); private Leaf compress=new Leaf("compress", ConstantValue.COMPRESS); private Leaf messengerid=new Leaf("messengerid"); private Leaf timestamp=new Leaf("timestamp"); private Leaf digest=new Leaf("digest"); private Leaf transactiontype=new Leaf("transactiontype"); private Leaf username=new Leaf("username"); /** * header部分序列化 */ public void serializer(XmlSerializer serializer,String body){ //第一個葉子節點 /** * messengerid:當前的這個xml的標示 值:時間戳:yyyyMMddHHmmss +六位的隨機數 處理方式:SimpleDateFormat DecimalFormat */ SimpleDateFormat dateFormat=new SimpleDateFormat("yyyyMMddHHmmss"); String time=dateFormat.format(new Date()); //獲取隨機數 Random random=new Random(); int num=random.nextInt(999999)+1;//1 //格式化隨機數 DecimalFormat decimalFormat=new DecimalFormat("000000"); String randomNum = decimalFormat.format(num); messengerid.setValue(time+randomNum); timestamp.setValue(time); //MD5的摘要信息 //時間戳+密碼+body部分信息。 String md5Info=time+ConstantValue.AGENTER_PASSWORD+body; String md5Hex=DigestUtils.md5Hex(md5Info); digest.setValue(md5Hex); try { serializer.startTag(null, "header"); //葉子 agenterid.serializer(serializer); source.serializer(serializer); compress.serializer(serializer); messengerid.serializer(serializer); timestamp.serializer(serializer); digest.serializer(serializer); transactiontype.serializer(serializer); username.serializer(serializer); serializer.endTag(null, "header"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } //這個我們處理不了,就給他暴露出去 public Leaf getTransactiontype() { return transactiontype; } public Leaf getUsername() { return username; } }
package cn.piaoye.lottery; public interface ConstantValue { String AGENTERID="1000002";//子代理商的標示 String SOURCE="ivr";//信息來源 String COMPRESS="DES";//body部分的加密方式 /** * 子代理商的密鑰 */ String AGENTER_PASSWORD = "9ab62a694d8bf6ced1fab6acd48d02f8"; /** * des加密用密鑰 */ String DES_PASSWORD = "9b2648fcdfbad80f"; }
對封裝的協議進行測試:
public void testHeader() { XmlSerializer serializer = Xml.newSerializer(); try { StringWriter writer = new StringWriter(); serializer.setOutput(writer); serializer.startDocument("utf-8", null); serializer.startTag(null, "message"); Header header=new Header(); header.serializer(serializer, ""); // body serializer.startTag(null, "body"); serializer.endTag(null, "body"); serializer.endTag(null, "message"); serializer.endDocument(); Log.i("XmlTest", writer.toString()); } catch (Exception e) { e.printStackTrace(); } }
生成的xml文件為:
<?xml version='1.0' encoding='utf-8'?> <message> <header> <agenterid> 1000002 </agenterid> <source>ivr</source> <compress> DES </compress> <messengerid> 20121224163229736485 </messengerid> <timestamp> 20121224163229 </timestamp> <digest> a6bcb107c326f21006233dd67d9fc038 </digest> <transactiontype> </transactiontype> <username> </username> </header> <body /> </message>