該篇Blog和上一篇(C++實(shí)例)基本相同,,只是面向于我們團(tuán)隊(duì)中的Java工程師,,畢竟我們項(xiàng)目的前端部分是基于Android開(kāi)發(fā)的,而且我們研發(fā)團(tuán)隊(duì)中目前主要使用的開(kāi)發(fā)語(yǔ)言就是C++,、Java和Python,,其中Python主要用于編寫各種工具程序,。然而為了保證該篇Blog的完整性和獨(dú)立性,我仍然會(huì)將上一篇Blog中已經(jīng)出現(xiàn)的內(nèi)容再一次贅述,,同時(shí)對(duì)于Java中特有的部分也會(huì)著重介紹,。 1 //用于修改LogonReqMessage消息字段的輔助Builder接口,。 2 //該接口會(huì)為消息中的每個(gè)字段均提供getter和setter方法。 3 public interface LogonReqMessageOrBuilder 4 extends com.google.protobuf.MessageLiteOrBuilder { 5 6 // required int64 acctID = 1; 7 boolean hasAcctID(); 8 long getAcctID(); 9 10 // required string passwd = 2; 11 boolean hasPasswd(); 12 String getPasswd(); 13 } 14 //該類為final類,,即不可以在被子類化了,。這一點(diǎn)在Protocol Buffer的官方文檔中給予了明確 15 //的說(shuō)明,因?yàn)樽宇惢瘜?huì)破壞序列化和反序列化的過(guò)程,。 16 public static final class LogonReqMessage extends 17 com.google.protobuf.GeneratedMessageLite 18 implements LogonReqMessageOrBuilder { 19 20 // Use LogonReqMessage.newBuilder() to construct. 21 // 由于所有構(gòu)造函數(shù)均為私有方法,,由此可見(jiàn),我們不能直接new LogonReqMessage的對(duì)象 22 // 實(shí)例,,而是只能通過(guò)與其對(duì)應(yīng)Builder來(lái)構(gòu)造,或是直接通過(guò)反序列化的方式生成,。 23 private LogonReqMessage(Builder builder) { 24 super(builder); 25 } 26 //該靜態(tài)方法為該類Builder接口的工廠方法,。返回的Builder實(shí)現(xiàn)類在完成各個(gè)字段的 27 //初始化后,,通過(guò)build()方法返回與其對(duì)應(yīng)的消息實(shí)現(xiàn)類,即LogonReqMessage,。 28 public static Builder newBuilder() { return Builder.create(); } 29 //通過(guò)該類的對(duì)象獲取與其對(duì)應(yīng)的Builder類對(duì)象,,一般用于通過(guò)Builder類完成消息字段的修改。 30 public Builder toBuilder() { return newBuilder(this); } 31 32 private LogonReqMessage(boolean noInit) {} 33 //判斷當(dāng)前對(duì)象的所有字段是否都已經(jīng)被初始化,。 34 public final boolean isInitialized() { 35 ... ... 36 } 37 //獲取已經(jīng)被初始化后的對(duì)象序列化時(shí)所占用的字節(jié)空間,。 38 public int getSerializedSize() { 39 ... ... 40 } 41 //從內(nèi)存中飯序列化LogonReqMessage對(duì)象。 42 //Protocol Buffer中還提供其他一些接口方法,,用于從不同的數(shù)據(jù)源反序列化對(duì)象,。 43 public static com.lsk.lyphone.LYPhoneMessage.LogonReqMessage parseFrom(byte[] data) 44 throws com.google.protobuf.InvalidProtocolBufferException { 45 return newBuilder().mergeFrom(data).buildParsed(); 46 } 47 //功能和上一個(gè)函數(shù)相同,只是輸入源改為InputStream接口,。 48 public static com.lsk.lyphone.LYPhoneMessage.LogonReqMessage parseFrom(java.io.InputStream input) 49 throws java.io.IOException { 50 return newBuilder().mergeFrom(input).buildParsed(); 51 } 52 53 // required int64 acctID = 1; 54 // 下面的靜態(tài)變量對(duì)應(yīng)于該字段在.proto中定義的標(biāo)簽號(hào),。該變量的命名規(guī)則為:字段(全部大寫) + _FIELD_NUMBER。 55 public static final int ACCTID_FIELD_NUMBER = 1; 56 public boolean hasAcctID() { 57 return ((bitField0_ & 0x00000001) == 0x00000001); 58 } 59 public long getAcctID() { 60 return acctID_; 61 } 62 63 // required string passwd = 2; 64 public static final int PASSWD_FIELD_NUMBER = 2; 65 public boolean hasPasswd() { 66 return ((bitField0_ & 0x00000002) == 0x00000002); 67 } 68 public String getPasswd() { 69 ... ... 70 } 71 //每一個(gè)Message類都會(huì)包含一個(gè)靜態(tài)內(nèi)部類,,即與之對(duì)應(yīng)的Builder類,。上面代碼中所涉及的Builder類均為該內(nèi)部類。 72 public static final class Builder extends 73 com.google.protobuf.GeneratedMessageLite.Builder< 74 com.lsk.lyphone.LYPhoneMessage.LogonReqMessage, Builder> 75 implements com.lsk.lyphone.LYPhoneMessage.LogonReqMessageOrBuilder { 76 //清空當(dāng)前對(duì)象中的所有設(shè)置,。調(diào)用該函數(shù)之后,,本例中的hasAcctID和hasPasswd都會(huì)返回false。 77 public Builder clear() { 78 super.clear(); 79 acctID_ = 0L; 80 bitField0_ = (bitField0_ & ~0x00000001); 81 passwd_ = ""; 82 bitField0_ = (bitField0_ & ~0x00000002); 83 return this; 84 } 85 //克隆出一個(gè)Builder對(duì)象,。 86 public Builder clone() { 87 return create().mergeFrom(buildPartial()); 88 } 89 public com.lsk.lyphone.LYPhoneMessage.LogonReqMessage build() { 90 com.lsk.lyphone.LYPhoneMessage.LogonReqMessage result = buildPartial(); 91 if (!result.isInitialized()) { 92 throw newUninitializedMessageException(result); 93 } 94 return result; 95 } 96 // Builder類中修改外部消息類的方法,。 97 // required int64 acctID = 1; 98 public boolean hasAcctID() { 99 return ((bitField0_ & 0x00000001) == 0x00000001); 100 } 101 public long getAcctID() { 102 return acctID_; 103 } 104 //設(shè)置AcctID字段,該函數(shù)調(diào)用后hasAcctID函數(shù)將返回true,。 105 //這里之所以讓返回值為Builder對(duì)象,,就是可以讓調(diào)用者在一條代碼中方便的連續(xù)修改多個(gè)字段, 106 //如:myMessage.setAcctID(100).setPasswd("MyName"); 107 public Builder setAcctID(long value) { 108 bitField0_ |= 0x00000001; 109 acctID_ = value; 110 return this; 111 } 112 //清空AcctID字段,,該函數(shù)調(diào)用后hasAcctID函數(shù)返回false,。 113 //這里之所以讓返回值為Builder對(duì)象,就是可以讓調(diào)用者在一條代碼中方便的連續(xù)清空多個(gè)字段,, 114 //如:myMessage.clearAcctID().clearPasswd(); 115 public Builder clearAcctID() { 116 bitField0_ = (bitField0_ & ~0x00000001); 117 acctID_ = 0L; 118 return this; 119 } 120 121 // required string passwd = 2; 122 public boolean hasPasswd() { 123 return ((bitField0_ & 0x00000002) == 0x00000002); 124 } 125 public String getPasswd() { 126 ... ... 127 } 128 public Builder setPasswd(String value) { 129 ... ... 130 } 131 public Builder clearPasswd() { 132 bitField0_ = (bitField0_ & ~0x00000002); 133 passwd_ = getDefaultInstance().getPasswd(); 134 return this; 135 } 136 void setPasswd(com.google.protobuf.ByteString value) { 137 bitField0_ |= 0x00000002; 138 passwd_ = value; 139 } 140 } 141 } 在上面生成的代碼中并沒(méi)有列出與序列化相關(guān)的函數(shù),,這部分代碼基本都是在父類中實(shí)現(xiàn)的,我們將在下面的例子中給出一些最基本的用法,,有興趣的開(kāi)發(fā)者可以直接看Protocol Buffer中的源碼,,這部分代碼比較通俗易懂。 1 private static void testSimpleMessage() { 2 System.out.println("==================This is simple message.================"); 3 //如前所述,,不能直接構(gòu)造該消息類對(duì)象,只能通過(guò)他的內(nèi)部Builder類構(gòu)造并完成所有字段的初始化,。 4 LogonReqMessage.Builder logonReqBuilder = LogonReqMessage.newBuilder(); 5 logonReqBuilder.setAcctID(20); 6 logonReqBuilder.setPasswd("Hello World"); 7 //builder對(duì)象初始化完畢后,,再通過(guò)build方法生成與之對(duì)應(yīng)的消息類對(duì)象,。 8 LogonReqMessage logonReq = logonReqBuilder.build(); 9 int length = logonReq.getSerializedSize(); 10 System.out.println("The result length is " + length); 11 //直接序列化到內(nèi)存中,之后可對(duì)該內(nèi)存進(jìn)行二次加工后再寫到本地文件或發(fā)送到遠(yuǎn)端,,如加密,。 12 byte[] buf = logonReq.toByteArray(); 13 14 try { 15 LogonReqMessage logonReq2 = LogonReqMessage.parseFrom(buf); 16 System.out.println("acctID = " + logonReq2.getAcctID() + "\tpassword = " + logonReq2.getPasswd()); 17 } catch (InvalidProtocolBufferException e) { 18 e.printStackTrace(); 19 } 20 //需要說(shuō)明的是,文件中的內(nèi)容是由之前C++實(shí)例代碼寫入的,,這里這樣寫主要是一種驗(yàn)證。 21 System.out.println("Reading data from local file generated by C++"); 22 try { 23 LogonReqMessage logonReq3 = LogonReqMessage.parseFrom(new FileInputStream("C:/Mine/LogonReq.dat")); 24 System.out.println("acctID = " + logonReq3.getAcctID() + "\tpassword = " + logonReq3.getPasswd()); 25 } catch (FileNotFoundException e) { 26 e.printStackTrace(); 27 } catch (IOException e) { 28 e.printStackTrace(); 29 } 30 } 三,、嵌套message生成的Java代碼,。 1 public static final class LogonRespMessage extends 2 com.google.protobuf.GeneratedMessageLite 3 implements LogonRespMessageOrBuilder { 4 5 //Message類的通用性函數(shù)定義,。 6 ... ... 7 8 // required .LoginResult logonResult = 1; 9 public static final int LOGONRESULT_FIELD_NUMBER = 1; 10 public boolean hasLogonResult() { 11 return ((bitField0_ & 0x00000001) == 0x00000001); 12 } 13 public com.lsk.lyphone.LYPhoneMessage.LoginResult getLogonResult() { 14 return logonResult_; 15 } 16 17 // required .UserInfo userInfo = 2; 18 public static final int USERINFO_FIELD_NUMBER = 2; 19 public boolean hasUserInfo() { 20 return ((bitField0_ & 0x00000002) == 0x00000002); 21 } 22 public com.lsk.lyphone.LYPhoneMessage.UserInfo getUserInfo() { 23 return userInfo_; 24 } 25 //Message類的通用性函數(shù)定義,。可參照上一小節(jié)中的代碼和注釋,。 26 ... ... 27 28 public static final class Builder extends 29 com.google.protobuf.GeneratedMessageLite.Builder< 30 com.lsk.lyphone.LYPhoneMessage.LogonRespMessage, Builder> 31 implements com.lsk.lyphone.LYPhoneMessage.LogonRespMessageOrBuilder { 32 33 //一些適用于絕大多數(shù)Builder對(duì)象的通用性方法,。 34 ... ... 35 36 //當(dāng)前示例中Builder生成的代碼和上一小節(jié)中生成的代碼非常類似,這里就不一一贅述了,。 37 //和前面的例子相比一個(gè)重要的差別是setUserInfo函數(shù)多提供了一種函數(shù)簽名,,其參數(shù)為 38 //UserInfo類的Builder對(duì)象。這樣調(diào)用者在使用時(shí)可以直接將Builder對(duì)象作為參數(shù)傳入,。 39 public Builder setUserInfo(com.lsk.lyphone.LYPhoneMessage.UserInfo.Builder builderForValue) { 40 userInfo_ = builderForValue.build(); 41 bitField0_ |= 0x00000002; 42 return this; 43 } 44 } 45 } 下面是讀寫LogonRespMessage對(duì)象的Java測(cè)試代碼和說(shuō)明性注釋,。 1 private static void testNestedMessage() { 2 System.out.println("==================This is nested message.================"); 3 LogonRespMessage.Builder logonRespBuilder = LogonRespMessage.newBuilder(); 4 logonRespBuilder.setLogonResult(LoginResult.LOGON_RESULT_SUCCESS); 5 UserInfo.Builder userInfo = UserInfo.newBuilder(); 6 userInfo.setAcctID(200); 7 userInfo.setName("Tester"); 8 userInfo.setStatus(UserStatus.OFFLINE); 9 //這里也可以直接傳遞userInfo對(duì)象作為參數(shù)。因?yàn)長(zhǎng)ogonRespBuilder類提供了setUserInfo的方法重載,。 10 logonRespBuilder.setUserInfo(userInfo.build()); 11 LogonRespMessage logonResp = logonRespBuilder.build(); 12 int length = logonResp.getSerializedSize(); 13 System.out.println("The result length is " + length); 14 byte[] buf = logonResp.toByteArray(); 15 16 try { 17 LogonRespMessage logonResp2 = LogonRespMessage.parseFrom(buf); 18 UserInfo userInfo2 = logonResp2.getUserInfo(); 19 System.out.println("LogonResult = " + logonResp2.getLogonResult().toString() + " acctID = " 20 + userInfo2.getAcctID() + " name = " + userInfo2.getName() + " status = " + userInfo2.getStatus().toString()); 21 } catch (InvalidProtocolBufferException e) { 22 e.printStackTrace(); 23 } 24 System.out.println("Reading data from local file generated by C++"); 25 try { 26 LogonRespMessage logonResp3 = LogonRespMessage.parseFrom(new FileInputStream("C:/Mine/LogonResp.dat")); 27 UserInfo userInfo3 = logonResp3.getUserInfo(); 28 System.out.println("LogonResult = " + logonResp3.getLogonResult().toString() + " acctID = " 29 + userInfo3.getAcctID() + " name = " + userInfo3.getName() + " status = " + userInfo3.getStatus().toString()); 30 } catch (FileNotFoundException e) { 31 e.printStackTrace(); 32 } catch (IOException e) { 33 e.printStackTrace(); 34 } 35 } 四,、repeated嵌套message生成的Java代碼。 1 public static final class RetrieveBuddiesResp extends 2 com.google.protobuf.GeneratedMessageLite 3 implements RetrieveBuddiesRespOrBuilder { 4 //這里均為Protocol Buffer生成的通用性代碼,。 5 ... ... 6 // repeated .BuddyInfo buddiesInfo = 2; 7 public static final int BUDDIESINFO_FIELD_NUMBER = 2; 8 //對(duì)于repeated類型的字段,,均返回類型參數(shù)為字段類型的泛型容器對(duì)象,。 9 public java.util.List<com.lsk.lyphone.LYPhoneMessage.BuddyInfo> getBuddiesInfoList() { 10 return buddiesInfo_; 11 } 12 public java.util.List<? extends com.lsk.lyphone.LYPhoneMessage.BuddyInfoOrBuilder> getBuddiesInfoOrBuilderList() { 13 return buddiesInfo_; 14 } 15 public int getBuddiesInfoCount() { 16 return buddiesInfo_.size(); 17 } 18 public com.lsk.lyphone.LYPhoneMessage.BuddyInfo getBuddiesInfo(int index) { 19 return buddiesInfo_.get(index); 20 } 21 public com.lsk.lyphone.LYPhoneMessage.BuddyInfoOrBuilder getBuddiesInfoOrBuilder(int index) { 22 return buddiesInfo_.get(index); 23 } 24 25 //這里仍有一些Protocol Buffer生成的通用性代碼。 26 ... ... 27 28 public static final class Builder extends 29 com.google.protobuf.GeneratedMessageLite.Builder< 30 com.lsk.lyphone.LYPhoneMessage.RetrieveBuddiesResp, Builder> 31 implements com.lsk.lyphone.LYPhoneMessage.RetrieveBuddiesRespOrBuilder { 32 33 //這里僅列出和操作repeated字段相關(guān)的方法,,其他的方法和前面的例子基本一致,。 34 // repeated .BuddyInfo buddiesInfo = 2; 35 //本來(lái)打算給出比較詳細(xì)的說(shuō)明,但是看到Google為每個(gè)函數(shù)的命名之后就放棄這個(gè)想法,, 36 //這樣一來(lái)不僅可以避免畫蛇添足,,而且也節(jié)省了時(shí)間。:) 37 public java.util.List<com.lsk.lyphone.LYPhoneMessage.BuddyInfo> getBuddiesInfoList() { 38 return java.util.Collections.unmodifiableList(buddiesInfo_); 39 } 40 public int getBuddiesInfoCount() { 41 return buddiesInfo_.size(); 42 } 43 public com.lsk.lyphone.LYPhoneMessage.BuddyInfo getBuddiesInfo(int index) { 44 return buddiesInfo_.get(index); 45 } 46 public Builder setBuddiesInfo(int index, com.lsk.lyphone.LYPhoneMessage.BuddyInfo value) { 47 ... ... 48 } 49 public Builder setBuddiesInfo(int index, com.lsk.lyphone.LYPhoneMessage.BuddyInfo.Builder builderForValue) { 50 ... ... 51 } 52 public Builder addBuddiesInfo(com.lsk.lyphone.LYPhoneMessage.BuddyInfo value) { 53 ... ... 54 } 55 public Builder addBuddiesInfo(int index, com.lsk.lyphone.LYPhoneMessage.BuddyInfo value) { 56 ... ... 57 } 58 public Builder addBuddiesInfo(com.lsk.lyphone.LYPhoneMessage.BuddyInfo.Builder builderForValue) { 59 ... ... 60 } 61 public Builder addBuddiesInfo( 62 int index, com.lsk.lyphone.LYPhoneMessage.BuddyInfo.Builder builderForValue) { 63 ... ... 64 } 65 public Builder addAllBuddiesInfo( 66 java.lang.Iterable<? extends com.lsk.lyphone.LYPhoneMessage.BuddyInfo> values) { 67 ... ... 68 } 69 public Builder clearBuddiesInfo() { 70 ... ... 71 } 72 public Builder removeBuddiesInfo(int index) { 73 ... ... 74 } 75 } 76 } 下面是讀寫RetrieveBuddiesResp對(duì)象的Java測(cè)試代碼和說(shuō)明性注釋,。 1 private static void testRepeatedMessage() { 2 System.out.println("==================This is repeated message.================"); 3 RetrieveBuddiesResp.Builder retrieveBuddiesBuilder = RetrieveBuddiesResp.newBuilder(); 4 retrieveBuddiesBuilder.setBuddiesCnt(2); 5 BuddyInfo.Builder buddyInfoBuilder = BuddyInfo.newBuilder(); 6 buddyInfoBuilder.setGroupID(20); 7 UserInfo.Builder userInfoBuilder = UserInfo.newBuilder(); 8 userInfoBuilder.setAcctID(200); 9 userInfoBuilder.setName("user1"); 10 userInfoBuilder.setStatus(UserStatus.OFFLINE); 11 buddyInfoBuilder.setUserInfo(userInfoBuilder.build()); 12 retrieveBuddiesBuilder.addBuddiesInfo(buddyInfoBuilder.build()); 13 14 buddyInfoBuilder = BuddyInfo.newBuilder(); 15 buddyInfoBuilder.setGroupID(21); 16 userInfoBuilder = UserInfo.newBuilder(); 17 userInfoBuilder.setAcctID(201); 18 userInfoBuilder.setName("user2"); 19 userInfoBuilder.setStatus(UserStatus.ONLINE); 20 buddyInfoBuilder.setUserInfo(userInfoBuilder); 21 retrieveBuddiesBuilder.addBuddiesInfo(buddyInfoBuilder); 22 RetrieveBuddiesResp buddiesResp = retrieveBuddiesBuilder.build(); 23 24 int length = buddiesResp.getSerializedSize(); 25 System.out.println("The result length is " + length); 26 byte[] buf = buddiesResp.toByteArray(); 27 28 try { 29 RetrieveBuddiesResp buddiesResp2 = RetrieveBuddiesResp.parseFrom(buf); 30 System.out.println("BuddiesCount = " + buddiesResp2.getBuddiesCnt()); 31 System.out.println("Repeated Size = " + buddiesResp2.getBuddiesInfoCount()); 32 for (int i = 0; i < buddiesResp2.getBuddiesInfoCount(); ++i) { 33 BuddyInfo buddyInfo = buddiesResp2.getBuddiesInfo(i); 34 UserInfo userInfo = buddyInfo.getUserInfo(); 35 System.out.println("GroupID = " + buddyInfo.getGroupID() + " UserInfo.acctID = " + userInfo.getAcctID() 36 + " UserInfo.name = " + userInfo.getName() + " UserInfo.status = " + userInfo.getStatus()); 37 } 38 39 } catch (InvalidProtocolBufferException e) { 40 e.printStackTrace(); 41 } 42 System.out.println("Reading data from local file generated by C++"); 43 try { 44 RetrieveBuddiesResp buddiesResp3 = RetrieveBuddiesResp.parseFrom(new FileInputStream("C:/Mine/RetrieveBuddiesResp.dat")); 45 System.out.println("BuddiesCount = " + buddiesResp3.getBuddiesCnt()); 46 System.out.println("Repeated Size = " + buddiesResp3.getBuddiesInfoCount()); 47 List<BuddyInfo> buddiesInfo = buddiesResp3.getBuddiesInfoList(); 48 for (BuddyInfo buddyInfo : buddiesInfo) { 49 UserInfo userInfo = buddyInfo.getUserInfo(); 50 System.out.println("GroupID = " + buddyInfo.getGroupID() + " UserInfo.acctID = " + userInfo.getAcctID() 51 + " UserInfo.name = " + userInfo.getName() + " UserInfo.status = " + userInfo.getStatus()); 52 } 53 } catch (FileNotFoundException e) { 54 e.printStackTrace(); 55 } catch (IOException e) { 56 e.printStackTrace(); 57 } 58 } 對(duì)于Java而言,,我們可以通過(guò)Maven工具生成兩個(gè)jar包,其中一個(gè)是protobuf-java-2.4.1.jar,,主要用于optimize_for選項(xiàng)為非LITE_RUNTIME的情況,,而另一個(gè)protobuf-java-2.4.1-lite.jar文件則恰恰與之相反。另外,,我通過(guò)Beyond Compare工具對(duì)這兩個(gè)jar包進(jìn)行了二進(jìn)制比較后發(fā)現(xiàn),,他們是完全相同的。這里之所以仍以LITE版本為例,,主要還是因?yàn)楹椭耙黄狟log(C++實(shí)例)想匹配,。 |
|
來(lái)自: 開(kāi)心豆豆2010 > 《java》