最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

IM通訊協(xié)議專(zhuān)題學(xué)習(xí)(六):手把手教你如何在Android上從零使用Protobuf

2023-03-09 12:05 作者:nickkckckck  | 我要投稿

1、前言

最近我負(fù)責(zé)的 LiveChat 客服聊天系統(tǒng)到了自研階段,任務(wù)類(lèi)似于做一個(gè)騰訊云IM這樣的通信層SDK。在和后臺(tái)進(jìn)行技術(shù)選型討論后,確定了數(shù)據(jù)傳輸層協(xié)議格式使用 Protobuf。

本文基于我對(duì)Protobuf在Android端的實(shí)際使用心得,手把手教你如何在Android端IM產(chǎn)品中使用Protobuf,希望對(duì)你有幫助。

學(xué)習(xí)交流:

- 移動(dòng)端IM開(kāi)發(fā)入門(mén)文章:《新手入門(mén)一篇就夠:從零開(kāi)發(fā)移動(dòng)端IM》

- 開(kāi)源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK(備用地址點(diǎn)此)

(本文已同步發(fā)布于:http://www.52im.net/thread-4135-1-1.html)

2、系列文章

本文是系列文章中的第?6?篇,總目錄如下:

  • 《IM通訊協(xié)議專(zhuān)題學(xué)習(xí)(一):Protobuf從入門(mén)到精通,一篇就夠!》

  • 《IM通訊協(xié)議專(zhuān)題學(xué)習(xí)(二):快速理解Protobuf的背景、原理、使用、優(yōu)缺點(diǎn)》

  • 《IM通訊協(xié)議專(zhuān)題學(xué)習(xí)(三):由淺入深,從根上理解Protobuf的編解碼原理》

  • 《IM通訊協(xié)議專(zhuān)題學(xué)習(xí)(四):從Base64到Protobuf,詳解Protobuf的數(shù)據(jù)編碼原理》

  • 《IM通訊協(xié)議專(zhuān)題學(xué)習(xí)(五):Protobuf到底比JSON快幾倍?全方位實(shí)測(cè)!》

  • 《IM通訊協(xié)議專(zhuān)題學(xué)習(xí)(六):手把手教你如何在Android上從零使用Protobuf》(* 本文)

  • 《IM通訊協(xié)議專(zhuān)題學(xué)習(xí)(七):手把手教你如何在NodeJS中從零使用Protobuf》

  • 《IM通訊協(xié)議專(zhuān)題學(xué)習(xí)(八):金蝶隨手記團(tuán)隊(duì)的Protobuf應(yīng)用實(shí)踐(原理篇)》

  • 《IM通訊協(xié)議專(zhuān)題學(xué)習(xí)(九):手把手教你如何在iOS上從零使用Protobuf》

3、Protobuf 介紹

Protobuf的全稱(chēng)是Protocol Buffers,它是 Google 推出的一種與平臺(tái)無(wú)關(guān)、語(yǔ)言無(wú)關(guān)、可擴(kuò)展的輕便高效的序列化數(shù)據(jù)存儲(chǔ)格式,類(lèi)似于我們常用的 xml 和 json。

一個(gè)疑問(wèn):既然有了 XML 和 JSON,Google 為啥還要推出 Protobuf 呢?

存在即是合理,Protobuf 的優(yōu)勢(shì)用兩個(gè)字總結(jié)就是:小、快。

相同的數(shù)據(jù)內(nèi)容,用 Protobuf 序列化后的大小是 JSON 的十分之一,是 XML 格式的二十分之一,而且性能是他們的 5~100 倍。

通常情況下,我們使用 XML 或者 JSON 進(jìn)行數(shù)據(jù)通信是沒(méi)什么問(wèn)題的,但是在性能優(yōu)化的場(chǎng)景下,如果有辦法壓縮數(shù)據(jù)量、提高傳輸效率,顯然會(huì)給用戶(hù)帶來(lái)更快更流暢的體驗(yàn)。因此我在做 LiveChat 自研技術(shù)選型時(shí),Protobuf 成為了我們進(jìn)行數(shù)據(jù)傳輸協(xié)議格式的第一選擇。

4、Protobuf 環(huán)境配置

4.1概述

介紹兩種安裝 Protobuf 的方式:

  • 1)Github 下載 Protobuf 并安裝;

  • 2)brew 安裝。

4.2Github 下載 Protobuf 并安裝

Protobuf 版本盡量保持前后臺(tái)一致,這里是后臺(tái)和我約定的一個(gè)版本(點(diǎn)此下載)。

1)根據(jù)自己的系統(tǒng)選擇相應(yīng)的 zip 包:

以我下載的為例,解壓后結(jié)構(gòu)如下:

可以看到 bin 目錄下有個(gè) protoc 的可執(zhí)行文件。

我們給它配置一下環(huán)境變量就可以使用了(以我的為例):

# protobuf 環(huán)境變量

exportPROTOBUF_HOME=/Users/zhouying/Downloads/protoc-3.19.2-osx-x86_64

exportPATH=${PATH}:${PROTOBUF_HOME}/bin

配置好后,使用?protoc --version?命令驗(yàn)證:

可以看到打印出了版本,證明我們安裝成功了。

4.3brew 安裝

直接使用以下命令就可以一鍵安裝或卸載:

//一鍵安裝 protobuf

brew installprotobuf

?

//一鍵卸載 protobuf

brew uninstall protobuf

PS:這種方式只適用于 Mac 系統(tǒng),而且安裝的 protobuf 為最新版本,因此如果想要使用指定的版本,建議使用上小節(jié)里的Github下載安裝這種方式。

5、使用 IDE 創(chuàng)建 Protobuf 文件

這里我使用的 IDE 是 AndroidStudio。如果你還在用Eclipse+ADT開(kāi)發(fā)Android程序,那我也不知道說(shuō)啥好。

5.1創(chuàng)建一個(gè)以 .proto 為后綴的文件

如下圖:

可以看到這個(gè)文件前面的 icon 是個(gè)文本類(lèi)型的,而且你在編寫(xiě)代碼時(shí),沒(méi)有任何提示,關(guān)鍵字也沒(méi)高亮顯示,此時(shí)我們需要集成一個(gè)插件來(lái)支持 Protobuf 的編寫(xiě)。

5.2集成 Protocol Buffers 插件

當(dāng)我們集成這個(gè)插件后,可以看到文件 icon 和代碼高亮的變化,而且還會(huì)有語(yǔ)法提示(如下圖所示)。

ok,接下來(lái)我們就可以愉快的編寫(xiě) Protobuf 了,但是要怎么寫(xiě)呢?你是不是還不會(huì) Protobuf 的語(yǔ)法,接著往下看。

6、Protobuf 語(yǔ)法基礎(chǔ)

關(guān)于 Protobuf 的語(yǔ)法有很多,限于篇幅,我們這里只講一些常用的(如果有興趣進(jìn)一步學(xué)習(xí)的,可以詳讀本系列的前幾篇,比如《Protobuf從入門(mén)到精通,一篇就夠!》、《快速理解Protobuf的背景、原理、使用、優(yōu)缺點(diǎn)》)。

6.1Protobuf 常用關(guān)鍵字介紹

PS:在 Proto3 中不支持 required (必須)字段。

6.2Protobuf 基本數(shù)據(jù)類(lèi)型

可變長(zhǎng)度編碼和固定長(zhǎng)度編碼區(qū)別:

//例如說(shuō)我在 Java 里面進(jìn)行如下定義:

inta = 1;

//因?yàn)?int 類(lèi)型占 4 個(gè)字節(jié),1個(gè)字節(jié)占 8 位,我們把 1 的字節(jié)占位給列出來(lái):

00000000000000000000000000000001

//可以看到 1 的前面 3 個(gè)字節(jié)占位都是 0,在 Protobuf 里面是可以去掉的,于是就變成了:

00000001

//因此 1 在 Protobuf 里面就只占用了一個(gè)字節(jié),節(jié)省了空間

上面這種就是可變長(zhǎng)度編碼。而固定長(zhǎng)度編碼就是即使前面的字節(jié)占位是 0,也不能去掉,我就是要占這么多字節(jié)。

基本數(shù)據(jù)類(lèi)型默認(rèn)值:

6.3消息類(lèi)型定義

在上面創(chuàng)建的 Protobuf 文件中,定義一個(gè)學(xué)生,學(xué)生有姓名,年齡,郵箱和課程。

我們來(lái)實(shí)現(xiàn)一下它:

//指定 Protobuf 版本

syntax = "proto3";

?

//指定包名

package erdai;

?

//定義一個(gè)學(xué)生的消息類(lèi)

message Student{

??//姓名

??string name = 1;

??//年齡

??int32 age = 2;

??//郵箱

??string email = 3;

??//課程

??repeated string course = 4; //相當(dāng)于 Java 的 List<String>

}

有幾個(gè)地方需要注意:

  • 1)一個(gè) Protobuf 文件里面可以添加多個(gè)消息類(lèi),也可以進(jìn)行嵌套;

  • 2)上面的 1,2,3,4 并不是給字段賦值,而是給每個(gè)字段定義一個(gè)唯一的編號(hào)(這些編號(hào)用于二進(jìn)制格式中標(biāo)識(shí)你的字段,并且在使用你的消息類(lèi)型后不應(yīng)更改);

  • 3)1-15 的字段編號(hào)只占一個(gè)字節(jié)進(jìn)行編碼,16-2047 的字段編號(hào)占兩個(gè)字節(jié),包括字段編號(hào)和字段類(lèi)型,因此建議更多的使用 1-15 的字段編號(hào);

  • 4)可以指定最小字段編號(hào)為 1,最大字段編號(hào)為 2^29 - 1 或 536870911(另外不能使用 19000-19999 的標(biāo)識(shí)號(hào),因?yàn)?protobuf 協(xié)議實(shí)現(xiàn)對(duì)這些進(jìn)行了預(yù)留,同樣也不能使用任何以前保留(reserved) 的字段編號(hào))。

6.4Protobuf 枚舉類(lèi)型

message Weather{

??int32 query = 1;

?

??//季節(jié)

??enum Season{

????//允許對(duì)枚舉常量設(shè)置別名

????option allow_alias = true;

????//枚舉里面的 = 操作是對(duì)常量進(jìn)行賦值操作

????//春

????SPRING = 0;

????//夏

????SUMMER = 1;

????//秋 如果不設(shè)置別名,不允許存在兩個(gè)相同的值

????FALL = 2;

????AUTUMN = 2;

????//冬

????WINTER = 3;

??}

?

??//對(duì) season 進(jìn)行編號(hào)

??Season season = 2;

}

有幾個(gè)地方需要注意:

  • 1)定義枚舉類(lèi)型使用 enum 關(guān)鍵字;

  • 2)枚舉類(lèi)型第一個(gè)字段的值為必須 0,否則編譯會(huì)報(bào)錯(cuò);

  • 3)枚舉常量值必須在 32 位整型值的范圍內(nèi)(因?yàn)?enum 值是使用可變編碼方式的,對(duì)負(fù)數(shù)不夠高效,因此不推薦在 enum 中使用負(fù)數(shù));

  • 4)枚舉里面的 = 操作是對(duì)常量進(jìn)行賦值操作,而枚舉外面的 = 則是對(duì)當(dāng)前字段進(jìn)行編號(hào)。

6.5Protobuf 集合

repeated string list = 1; //類(lèi)似 Java 的 List<String>

map<string,string> = 2; //類(lèi)似 Java 的 Map<String,String>

有幾個(gè)地方需要注意:

  • 1)Protobuf 中定義集合,就是在你定義好的屬性前面加 repeated 關(guān)鍵字;

  • 2)Protobuf 中定義 map 和 Java 類(lèi)似,只不過(guò) map 是小寫(xiě)的。

6.6reserved 保留字段

當(dāng)我們使用 reserved 關(guān)鍵字指定一個(gè)字段編號(hào)或字段名為保留字段后,我們就不能去使用它,否則編譯器會(huì)報(bào)錯(cuò)(如下圖所示)。

ok,簡(jiǎn)單的介紹了 Protobuf 語(yǔ)法后,接下來(lái)我們使用 AndroidStudio 將 Protobuf 文件轉(zhuǎn)換為對(duì)應(yīng)的 Java 文件來(lái)進(jìn)行開(kāi)發(fā)。

7、Protobuf 轉(zhuǎn) Java 文件

首先我們要明白一點(diǎn):Protobuf 是一種與平臺(tái),語(yǔ)言無(wú)關(guān)的數(shù)據(jù)存儲(chǔ)格式,因此我們要在其它語(yǔ)言如:Java,Kotlin,Dart 等語(yǔ)言中使用它,則必須將 Protobuf 文件轉(zhuǎn)換為對(duì)應(yīng)平臺(tái)的語(yǔ)言文件去使用。

這里我們以轉(zhuǎn) Java 文件為例,介紹兩種轉(zhuǎn)換的方式:

  • 1)集成 Protobuf 插件轉(zhuǎn)換;

  • 2)使用 protoc 命令行轉(zhuǎn)換。

在使用這兩種方式轉(zhuǎn)換前,我們需要先集成 protobuf-java 這個(gè)第三方庫(kù),因?yàn)槲覀冝D(zhuǎn)換的 Java 文件中會(huì)使用這個(gè)庫(kù)里面的功能。

在Android Studio的工作配置中集成 protobuf-java:

implementation 'com.google.protobuf:protobuf-java:3.19.2'

最新版本可查看此鏈接:https://github.com/protocolbuffers/protobuf/tree/main/java

7.1集成 Protobuf 插件轉(zhuǎn)換

插件鏈接是:https://github.com/google/protobuf-gradle-plugin

第一步:在項(xiàng)目的根 build.gradle 文件中,添加如下依賴(lài):

buildscript {

????repositories {

????????gradlePluginPortal()

????}

????dependencies {

????????classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.19'

????}

}

第二步:在 app 的 build.gradle 文件中應(yīng)用插件:

//1、如果使用的是 apply 方式,Android 插件必須在 Protobuf 插件之前,如下:

apply plugin: 'com.android.application'

apply plugin: 'com.google.protobuf'

?

//2、如果使用的是 Gradle DSL 方式,則與順序無(wú)關(guān)

plugins {

????id'com.google.protobuf'

????id'com.android.application'

}

第三步:在 app 的 build.gradle 文件中配置 protobuf 編譯任務(wù):

android {

????//...

}

?

protobuf {

????//配置 protoc 編譯器

????protoc {

????????artifact = 'com.google.protobuf:protoc:3.19.2'

????}

????//配置生成目錄,編譯后會(huì)在 build 的目錄下生成對(duì)應(yīng)的java文件

????generateProtoTasks {

????????all().each { task ->

????????????task.builtins {

????????????????remove java

????????????}

????????????task.builtins {

????????????????java {}

????????????}

????????}

????}

}

第四步:指定 protobuf 文件所在路徑:

android {

????//...

?

????sourceSets {

????????main {

????????????//實(shí)際測(cè)試指不指定無(wú)所謂,不影響 Java 文件生成

????????????proto {

????????????????srcDir 'src/main/proto'

????????????}

????????}

????}

}

經(jīng)過(guò)上面 4 步,我們的插件就算配置完成了,接下來(lái)我們測(cè)試一下。

以我們上面那個(gè) student.proto 為例,里面的內(nèi)容如下:

//指定 Protobuf 版本

syntax = "proto3";

?

//指定包名

package erdai;

?

//定義一個(gè)學(xué)生的消息類(lèi)

message Student{

??//姓名

??string name = 1;

??//年齡

??int32 age = 2;

??//郵箱

??string email = 3;

??//課程

??repeated string course = 4;

?

}

?

//定義一個(gè)天氣的消息類(lèi)

message Weather{

??int32 query = 1;

?

??//季節(jié)

??enum Season{

????option allow_alias = true;

????//春

????SPRING = 0;

????//夏

????SUMMER = 1;

????//秋

????FALL = 2;

????AUTUMN = 2;

????//冬

????WINTER = 3;

??}

?

??Season season = 2;

}

編譯后,會(huì)在 app/build/generated/source/proto/ 目錄下生成如下文件:

這種方式會(huì)自動(dòng)給我們生成對(duì)應(yīng)的 Java 類(lèi),就是集成的時(shí)候麻煩一點(diǎn)。

7.2使用 protoc 命令行轉(zhuǎn)換

執(zhí)行如下命令:

protoc --java_out=${"你要生成的 Java 文件目錄"} ${"Protobuf 文件位置"}

?

//以我的為例:

protoc --java_out=./app/src/main/java ./app/src/main/proto/student.proto

生成的文件:

這種方式的優(yōu)點(diǎn)是使用非常簡(jiǎn)單,但是每一次創(chuàng)建新的 Protobuf 文件都需要執(zhí)行一下命令。

7.3生成的 Java 文件介紹

關(guān)于這個(gè)類(lèi)的內(nèi)容我簡(jiǎn)單介紹下,大家也可以生成后自行看源碼(如下圖)。

接下來(lái)簡(jiǎn)單進(jìn)行說(shuō)明。

1)成的類(lèi) StudentOuterClass 有 4 個(gè)子類(lèi):

Student (實(shí)現(xiàn)了 StudentOrBuilder 接口)

Weather (實(shí)現(xiàn)了 WeatherOrBuilder 接口)

StudentOrBuilder (接口)

WeatherOrBuilder (接口)

2)Student 是 StudentOuterClass 的靜態(tài)內(nèi)部類(lèi),構(gòu)造方法私有化,需通過(guò) Builder 模式創(chuàng)建對(duì)象。另外 Student 提供了屬性的 get 方法,無(wú) set 方法,Builder 作為 Student 的靜態(tài)內(nèi)部類(lèi)提供了屬性的 get,set,clear方法,clear 就是清空某個(gè)屬性回到初始值。

3)Weather 同 2。

8、Protobuf 的使用

接下來(lái)我們對(duì)剛才生成的 Java 類(lèi)進(jìn)行使用。

8.1基本調(diào)用

fun main(){

????//構(gòu)建 Protobuf 對(duì)象

????val student = StudentOuterClass.Student.newBuilder()

????????.setName("erdai")

????????.setAge(18)

????????.setEmail("erdai666@qq.com")

????????.addAllCourse(mutableListOf("Math", "English", "Computer"))

????????.build()

????println(student)

????println()

????println(StudentOuterClass.Weather.Season.SPRING.number)

????println(StudentOuterClass.Weather.Season.SUMMER.number)

????println(StudentOuterClass.Weather.Season.AUTUMN.number)

????println(StudentOuterClass.Weather.Season.WINTER.number)

}

?

//打印結(jié)果

name: "erdai"

age: 18

email: "erdai666@qq.com"

course: "Math"

course: "English"

course: "Computer"

?

0

1

2

3

8.2序列化和反序列化

fun main(){

????//1、構(gòu)建 Protobuf 對(duì)象

????val student = StudentOuterClass.Student.newBuilder()

????????.setName("erdai")

????????.setAge(18)

????????.setEmail("erdai666@qq.com")

????????.addAllCourse(mutableListOf("Math", "English", "Computer"))

????????.build()

????//2、序列化并返回一個(gè)包含其原始字節(jié)的字節(jié)數(shù)組

????val byteArray: ByteArray = student.toByteArray()

????//3、反序列化從字節(jié)數(shù)組中解析消息

????val parseStudent: StudentOuterClass.Student = StudentOuterClass.Student.parseFrom(byteArray)

}

9、Protobuf 和 JSON 相互轉(zhuǎn)換

首先我們需要添加一個(gè)第三方依賴(lài):

implementation 'com.google.protobuf:protobuf-java-util:3.19.2'

Protobuf 轉(zhuǎn) JSON:

fun main(){

????//1、構(gòu)建 Protobuf 對(duì)象

????val student = StudentOuterClass.Student.newBuilder()

????????.setName("erdai")

????????.setAge(18)

????????.setEmail("erdai666@qq.com")

????????.addAllCourse(mutableListOf("Math", "English", "Computer"))

????????.build()

????//2、Protobuf 轉(zhuǎn) Json

????val json = JsonFormat.printer().print(student)

????//3、打印 json

????println(json)

}

?

//打印結(jié)果

{

??"name": "erdai",

??"age": 18,

??"email": "erdai666@qq.com",

??"course": ["Math", "English", "Computer"]

}

JSON 轉(zhuǎn) Protobuf:

fun main(){

????//1、構(gòu)建 Protobuf 對(duì)象

????val student = StudentOuterClass.Student.newBuilder()

????????.setName("erdai")

????????.setAge(18)

????????.setEmail("erdai666@qq.com")

????????.addAllCourse(mutableListOf("Math", "English", "Computer"))

????????.build()

????//2、Protobuf 對(duì)象轉(zhuǎn) Json

????val json = JsonFormat.printer().print(student)

????//3、Json 轉(zhuǎn) Protobuf 對(duì)象

????val builder = StudentOuterClass.Student.newBuilder()

????JsonFormat.parser().ignoringUnknownFields().merge(json,builder)

????//4、打印 Protobuf 對(duì)象

????println(builder.build())

}

?

//打印結(jié)果

name: "erdai"

age: 18

email: "erdai666@qq.com"

course: "Math"

course: "English"

course: "Computer"

10、Protobuf 和 Java 對(duì)象相互轉(zhuǎn)換

這個(gè)實(shí)則是在前述的基礎(chǔ)上,當(dāng)我們將 Protobuf 轉(zhuǎn)換為 JSON 后,在把 JSON 轉(zhuǎn)換為對(duì)應(yīng)的 Java 對(duì)象。

這里我們先引入 Gson庫(kù):

implementation 'com.google.code.gson:gson:2.8.9'

PS:類(lèi)似于fastjson,Gson是Google出品的著名JSON解析庫(kù)。

先定義一個(gè)接收當(dāng)前 Json 的數(shù)據(jù)模型:

data class MyStudent(

????varname: String? = "",

????varage: Int = 0,

????varemail: String? = "",

????varcourse: MutableList<String>? = null

)

最后進(jìn)行轉(zhuǎn)換:

fun main(){

????//1、構(gòu)建 Protobuf 對(duì)象

????val student = StudentOuterClass.Student.newBuilder()

????????.setName("erdai")

????????.setAge(18)

????????.setEmail("erdai666@qq.com")

????????.addAllCourse(mutableListOf("Math", "English", "Computer"))

????????.build()

????//2、將 Protobuf 轉(zhuǎn)換為 Json

????val json = JsonFormat.printer().print(student)

?

????//3、將 Json 轉(zhuǎn)換為 Java Bean 對(duì)象

????val myStudent = Gson().fromJson(json,MyStudent::class.java)

????println(myStudent)

}

?

//打印結(jié)果

MyStudent(name=erdai, age=18, email=erdai666@qq.com, course=[Math, English, Computer])

11、本文小結(jié)

這里我小結(jié)一下關(guān)于 Protobuf 的開(kāi)發(fā)流程:

  • 1)一般都是后臺(tái)定義好 Protobuf;

  • 2)然后給到我們客戶(hù)端;

  • 3)客戶(hù)端在根據(jù) Protobuf 生成對(duì)應(yīng)平臺(tái)的代碼進(jìn)行使用即可。

因此,上面我講的這些內(nèi)容如果你都掌握了,基本上就能滿足Android端使用Protobuff進(jìn)行日常開(kāi)發(fā)了。

好了,本篇文章到這里就結(jié)束了,如果有啥問(wèn)題,歡迎評(píng)論區(qū)一起交流。

12、參考資料

[1]?Protobuf 官方開(kāi)發(fā)者指南(中文譯版)

[2]?Protobuf官方手冊(cè)

[3]?Protobuf從入門(mén)到精通,一篇就夠!

[4]?如何選擇即時(shí)通訊應(yīng)用的數(shù)據(jù)傳輸格式

[5]?強(qiáng)列建議將Protobuf作為你的即時(shí)通訊應(yīng)用數(shù)據(jù)傳輸格式

[6]?APP與后臺(tái)通信數(shù)據(jù)格式的演進(jìn):從文本協(xié)議到二進(jìn)制協(xié)議

[7]?面試必考,史上最通俗大小端字節(jié)序詳解

[8]?移動(dòng)端IM開(kāi)發(fā)需要面對(duì)的技術(shù)問(wèn)題(含通信協(xié)議選擇)

[9]?簡(jiǎn)述移動(dòng)端IM開(kāi)發(fā)的那些坑:架構(gòu)設(shè)計(jì)、通信協(xié)議和客戶(hù)端

[10]?理論聯(lián)系實(shí)際:一套典型的IM通信協(xié)議設(shè)計(jì)詳解

[11]?58到家實(shí)時(shí)消息系統(tǒng)的協(xié)議設(shè)計(jì)等技術(shù)實(shí)踐分享

[12]?金蝶隨手記團(tuán)隊(duì)的Protobuf應(yīng)用實(shí)踐(原理篇)

[13]?新手入門(mén)一篇就夠:從零開(kāi)發(fā)移動(dòng)端IM

(本文已同步發(fā)布于:http://www.52im.net/thread-4135-1-1.html)


IM通訊協(xié)議專(zhuān)題學(xué)習(xí)(六):手把手教你如何在Android上從零使用Protobuf的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
西畴县| 汽车| 岚皋县| 天台县| 建水县| 海原县| 盐源县| 兰坪| 西华县| 松溪县| 开封市| 玉龙| 陇西县| 确山县| 会理县| 若尔盖县| 静宁县| 阿荣旗| 绍兴市| 甘洛县| 独山县| 靖边县| 株洲市| 通江县| 阜康市| 沽源县| 四平市| 绍兴县| 太康县| 万山特区| 萍乡市| 蒲江县| 德阳市| 邵阳县| 加查县| 南充市| 新泰市| 扬中市| 铁岭县| 靖远县| 临桂县|