在如今這個資料多如牛毛、牛人多於資料的社會,通過代碼來做出某個東西已經漸漸不是很難的事情了,稍微有點難題只要一上各種專業論壇、各種技術羣組,大家便會討論的熱火朝天,想不解決都難。技術我們可以學會,但是思想卻很難,同樣的一本書在不同的人看完之後都有不同的體會和感悟,那些牛人的思想都是在經歷了無數浩浩的代碼之後才體現出來(所以我們佩服那些公佈自己心血的大牛,這種奉獻精神是我們需要學習的),如何能快速的領悟牛人們的設計思想,對於像我們這樣的菜鳥而言無疑是巨大的困難,正是有了大牛們的無私奉獻,我們的學習註定會事半功倍,我們不需要魔鬼般代碼的訓練也可以領會高深的思想,使我們受益無窮。
本着學習的態度,且爲了記錄自己的感悟,在此分析一下開發過的一個聊天應用的設計思想,方便以後複習回顧。
通信協議UDP,關於這個在此便不多闡述。
通過下面這個簡單的圖,來分析一下是如何設計的:
由上圖可以看出,整個系統分爲7大模塊,視圖、消息處理器、消息解析器、UDP服務、解析控制器、處理控制器,消息體。
其中整個流程順序:
1、首先我們在Activity中註冊一個消息處理器,該處理器用來處理接收到的消息,然後將結果顯示在Activity中;
2、發送消息的時候,我們要將自己的信息封裝成一個消息包
3、該消息包要通過udp傳輸的話,還需要經過特定的解析器進行解析,得到一段傳輸協議,當然解析器中有編碼肯定也有解碼,且必須規則一樣。
4、將上面得到的一段傳輸協議在此處理分析
5、調用解析控制器
6、解析控制器會通過協議中的一些標誌,調用相應的解析器來進行解碼得到響應的消息包。
7、調用處理控制器,同時將上面得到的響應消息包傳過來
8、通過響應消息包中的一些標誌,來判斷調用相應的處理器來進行處理,得到信息然後顯示。
注意:
1、消息msg有很多種,比如有向網段發送的詢問誰在線的消息包、有響應誰在線的消息包、有聊天消息包、有個人信息查看請求消息包等等等等。
例如,誰在線消息包WhoOnlinePackage:
public class WhoOnlinePackage extends MsgPackage{
private String mac ;
private String nickName;
private int portrait;
@Override
public Integer getMsgCode() {
return MsgCode.WHOONLINE;
}
public WhoOnlinePackage(String nickName, String mac,int portrait) {
this.mac = mac;
this.nickName = nickName;
this.portrait = portrait;
}
public String getMac() {
return mac;
}
public void setMac(String mac) {
this.mac = mac;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public int getPortrait() {
return portrait;
}
public void setPortrait(int portrait) {
this.portrait = portrait;
}
public WhoOnlinePackage() {}
}
2、消息包有多少種,就有多少種解析器,每個解析器針對不同的msg做不同的編碼和解碼工作。
例如:誰在線消息包解析器:
public class WhoOnlinePackageParser extends MsgPackageParser<WhoOnlinePackage>{
@Override
public WhoOnlinePackage decode(PackageInputStream in) throws IOException {
return new WhoOnlinePackage(in.readString(10, CHARSET),MsgPackageUtils.getLongMacToString(in.readLong()),in.readInt());
}
@Override
public void encode(WhoOnlinePackage obj, PackageOutputStream out) throws IOException {
out.writeString(obj.getNickName(), 10);
out.writeLong(MsgPackageUtils.getStringMacToLong(obj.getMac()));
out.writeInt(obj.getPortrait());
}
}
可以看到解析器中,編碼和解碼都是根據傳輸協議的規則來進行相應的處理。
3、同樣有多少種解析器,就有多少種處理器。處理器根據解析器解析出來的消息包然後進一步進行處理,得到結果進行顯示。
例如,在Activity中的處理器的實現:
@Override
public void onMsgReceived(MsgPackage msgPackage) {
switch (msgPackage.getMsgCode()) {
case MsgCode.WHOONLINE:
WhoOnlinePackage wpck = (WhoOnlinePackage)msgPackage; //得到別人詢問誰在線的消息包
ImOnlinePackage ipck = new ImOnlinePackage(preferences.getString("nick", DicqConstant.LOCALNAME),preferences.getString("mac",DicqConstant.DEFAULTMAC),preferences.getInt("portrait",getLocalInfo().getPortrait())); //迴應他我在線的消息包,同時把本地信息封裝
ipck.addTargetIp(wpck.getFromIp());
sendMsgPackage(ipck, wpck.handlerTag);
int wteamid = -1;
Team wteam = service.getTeamByMac(wpck.getMac());
if(wteam==null){
try {
wteamid = service.find("weimingming").getTeamId();
} catch (Exception e) {
e.printStackTrace();
}
}else{
wteamid = wteam.getTeamId();
}
UserInfo wuser = new UserInfo();
wuser.setTeamid(wteamid);
wuser.setUserip(wpck.getFromIp());
wuser.setUsermac(wpck.getMac());
wuser.setUsername(wpck.getNickName());
wuser.setPortrait(wpck.getPortrait());
wuser.setPersonsign("哥只是個傳說"); //將信息封裝成user對象
service.saveUserMac(wpck.getMac(), wteamid);
addUserToList(wuser);
if(!wpck.getMac().equalsIgnoreCase(DicqConstant.DEFAULTMAC)){
Toast.makeText(TeamMainActivity.this, wuser.getUsername()+"上線了!", Toast.LENGTH_LONG).show();
}
adapter.setData(service.getTeamList(), userList);
adapter.notifyDataSetChanged(); //刷新,顯示
break;
4、解析控制器和處理控制器不用關注所有的解析器和處理器,通過一個標誌來關注自己感興趣的解析和處理器。這個解析在Activity的時候就已經註冊的。
例如:註冊解析器,Activity中:
@Override
public void onServiceConnected() {
register(MsgCode.WHOONLINE, -1); //註冊詢問誰在線解析器
register(MsgCode.IONLINE,-1); //註冊響應誰在線解析器
register(MsgCode.SINGLECHAT,-1); //註冊單聊解析器
register(MsgCode.IMGOAWAY,-1); //註冊離線解析器
}
解析控制器:
**
* 消息解析控制器
*
*/
public abstract class MsgPackageParserController {
/**
* 解析消息
* @param buf
* @param offset
* @param length
* @return
*/
public abstract MsgPackage messageDecode(PackageInputStream in) throws IOException;
/**
* 編碼消息
* @param command
* @return
*/
public abstract void messageEncode(MsgPackage command,PackageOutputStream out) throws IOException;
}
處理控制器:
/**
* 消息處理控制器
*
*/
public abstract class MsgPackageProcessor {
/**
* 註冊消息處理器
* @param handler
* @param tag 處理器的標識,該標識起着維護不同設備同種消息的會話作用
*/
public abstract void registMessageHandler(MsgPackageHandler<MsgPackage> handler,Integer tag);
/**
* 註銷消息處理器
* @param msgCode 處理器感興趣的消息類型
* @param tag 處理器的標識,該標識起着維護不同設備同種消息的會話作用
*/
public abstract void unRegistMessageHandler(Integer msgCode,Integer tag);
/**
* 處理消息
* @param msgPackage
*/
public abstract void processMessage(MsgPackage msgPackage);
}
5、傳輸協議很重要,這點根據個人理解不同制定不同的傳輸協議
6、udp server負責調度的作用,當消息來臨時,調用解析控制器來生成一個相應的解析器解析消息得到一個消息包,然後交給處理控制器來生成一個相應的處理器處理解析器生成的消息包。關鍵代碼如下:
/*
* 消息循環
*/
@Override
public void run() {
isrun=true;
while(isrun && !socket.isClosed()){
byte[] buf=new byte[MAXBUFFSIZE];
DatagramPacket packet=new DatagramPacket(buf, MAXBUFFSIZE);
try {
socket.receive(packet);
processUnresolvedMessage(buf,0,packet.getLength(),packet.getAddress());
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 解析從遠程發到的消息,並處理
* 消息解析控制器:解析消息
* 消息處理控制器:處理消息
*/
@Override
public void processUnresolvedMessage(byte[] msgPackage,int offset,int length,InetAddress fromIp) {
MsgPackage m=null;
try {
m=messageParser.messageDecode(new PackageInputStream(msgPackage, offset, length)); //解析成消息包
} catch (Exception e) {
Log.e(TAG, "command decode exception", e);
}
if(m!=null){
m.setFromIp(fromIp);
messageProcessor.processMessage(m); //交給處理器處理
return;
}
Log.w(TAG, "Unresolved Message:from "+fromIp+" Data:"+Arrays.toString(msgPackage));
}
暫時先總結到這裏,日後想到在更新吧。