APNs入門(mén)學(xué)習(xí)和使用

這篇文章費(fèi)了我好多心血啊闭树,這都是在我測(cè)試了一堆失敗的代碼,看了大量的博客之后荒澡,把其中最有用报辱,最精華的部分提取出來(lái)的集成,也是我艱辛的踩坑歷程单山,滿滿的干貨鞍帧!可能是我太渣了米奸,這些東西以前都沒(méi)接觸過(guò)昼接,所以下面會(huì)有很多很基礎(chǔ)的東西,大神切莫見(jiàn)怪悴晰。慢睡。。

HTTP/2

  • 看過(guò)這些之后铡溪,應(yīng)該對(duì)http/2協(xié)議有了一些最初的了解漂辐,知道大體是怎么回事,其實(shí)和http1/1差不多棕硫,區(qū)別點(diǎn)主要是以下幾點(diǎn):
  1. 二進(jìn)制分幀:每個(gè)請(qǐng)求分成多個(gè)幀進(jìn)行傳送髓涯,都送到之后再進(jìn)行拼裝
  2. 多路復(fù)用:二進(jìn)制分幀之后的一個(gè)好處,多個(gè)請(qǐng)求共享同一個(gè)tcp連接哈扮,節(jié)約連接數(shù)量纬纪,提高連接利用率
  3. 服務(wù)器推送:推測(cè)客戶(hù)端之后可能要的數(shù)據(jù)提前推送
  4. 頭部壓縮:兩端各維護(hù)一個(gè)頭部表蚓再,每次請(qǐng)求只傳送頭部不同的部分,減少傳輸冗余資源
  5. ALPN應(yīng)用層協(xié)議協(xié)商:和http1/1的兼容協(xié)商機(jī)制包各,這個(gè)下面會(huì)有關(guān)于java的相關(guān)說(shuō)明
  6. 支持異步編程对途,非阻塞,提高效率
  • 看了這些算是了解一些大概吧髓棋,很多東西到了實(shí)際使用中再來(lái)慢慢體會(huì)

OkHttp學(xué)習(xí)和使用

這東西好久之前就用過(guò),這次算是復(fù)習(xí)和提升以下惶洲,以前就是當(dāng)個(gè)http客戶(hù)端模擬使用按声,沒(méi)處理過(guò)cookie、證書(shū)什么的恬吕,這次由于下面要做的事情的需要签则,就做了這些測(cè)試:

用的很爽啊,鏈?zhǔn)骄幊填砹稀PI設(shè)計(jì)易于理解渐裂、sample眾多、直接搬磚钠惩。柒凉。。

APNs

github上找了很多項(xiàng)目來(lái)實(shí)驗(yàn)啊篓跛,最后還是Pushy這個(gè)最滿意膝捞,也最實(shí)用,并且也由它又發(fā)現(xiàn)了新世界愧沟,開(kāi)啟新世界蔬咬,新的征程開(kāi)始~
先來(lái)點(diǎn)介紹文吧~ 這些介紹看完,相信你也對(duì)APNs新版的協(xié)議有了比較清楚的了解沐寺。

Pushy神器來(lái)啦~下面的APNs調(diào)用都用這個(gè)項(xiàng)目來(lái)作為底層支撐林艘。

額外收獲

你可能會(huì)覺(jué)得上面的廢話好多,好多東西好像不需要了解混坞,直接使用Pushy就行了唄狐援,其實(shí)我也不敢說(shuō)上面那些東西是否真的對(duì)下面的有用,但是知道這些原理拔第,對(duì)后面發(fā)生的異常才能心中有數(shù)咕村,至少在我的踩坑過(guò)程中也是深有體會(huì)的,其實(shí)上面那些東西不是我一開(kāi)始就都看完的蚊俺,是在我踩坑的過(guò)程中一步步補(bǔ)充的懈涛,每個(gè)人的只是學(xué)習(xí)順序也許有所不同,你可以根據(jù)自己的情況合理安排~
我之所以會(huì)事先看這些東西泳猬,原因也是因?yàn)閔ttp/2批钠、APNs宇植、IOS推送、TLS等這些東西我真的不是很了解埋心,會(huì)有一種恐懼之心指郁,算是我自己的一個(gè)知識(shí)補(bǔ)充吧,所以對(duì)于對(duì)這些知識(shí)掌握很好的大神其實(shí)上面那些基礎(chǔ)是完全可以不看的~
下面開(kāi)始開(kāi)發(fā):

環(huán)境配置:

在Pushy的README.md中詳細(xì)說(shuō)明了Pushy所需的環(huán)境拷呆,我個(gè)人由于感激這篇文章在我踩坑過(guò)程中的作用闲坎,因此特別的把它翻譯出來(lái)Pushy README.md中文翻譯本
因?yàn)镻ushy本身依賴(lài)了其他類(lèi)庫(kù)茬斧,為了方便腰懂,也由于我是是使用Maven管理和構(gòu)件項(xiàng)目,我下面的教程完全都在Maven下進(jìn)行部署和開(kāi)發(fā)项秉,請(qǐng)悉知:

環(huán)境說(shuō)明:

必須JDK7以上版本绣溜,這個(gè)Pushy README.md中文翻譯本上面詳細(xì)說(shuō)明了。

  1. JDK8+Tomcat9 M11:部署成功
  2. JDK7+Tomcat7:部署成功
  3. JDK7+Tomcat9 M11:Tomcat啟動(dòng)失敗娄蔼,原因不明怖喻,我從Tomcat啟動(dòng)失敗的報(bào)錯(cuò)中認(rèn)為可能是Tomcat9 M11中調(diào)用了JDK8中特有的API,導(dǎo)致在JDK7中啟動(dòng)失敗岁诉。

所以你部署環(huán)境的時(shí)候這個(gè)要注意锚沸,特別是部署到服務(wù)器當(dāng)中的時(shí)候。

步驟:

  1. 添加Pushy依賴(lài):

<dependency>
<groupId>com.relayrides</groupId>
<artifactId>pushy</artifactId>
<version>0.8.1</version>
</dependency>


>2. 添加native SSL provider依賴(lài)涕癣,注意版本哦:
>```xml
<dependency>
     <groupId>io.netty</groupId>
     <artifactId>netty-tcnative-boringssl-static</artifactId>
     <version>1.1.33.Fork22</version>
</dependency>

這個(gè)是ALPN協(xié)議協(xié)商的實(shí)現(xiàn)依賴(lài)包咒吐,在Pushy README.md中文翻譯本有詳細(xì)說(shuō)明的。到這一步属划,你的普通Java程序就能跑起來(lái)向APNs服務(wù)器發(fā)起一個(gè)推送了~~

  1. 添加alpn-boot依賴(lài)(Tomcat中所需):
    這一步和上面的不太一樣恬叹,因?yàn)閖ar包需要添加到bootclasspath中,和普通的classpath不太一樣同眯,在JVM啟動(dòng)參數(shù)添加如下:
    -Xbootclasspath/p:/Users/coselding/Downloads/alpn-boot-8.1.9.v20160720.jar
    p:后面的部分就是你下載下來(lái)的alpn-boot的jar包的本地地址绽昼,這個(gè)jar包的下載地址是這個(gè)http://mvnrepository.com/artifact/org.mortbay.jetty.alpn/alpn-boot
    原因:
  1. 這種方式添加的jar包會(huì)替換JVM底層運(yùn)行的相關(guān)API,你可以理解為加載優(yōu)先級(jí)更高的jar须蜗,但是這種方式加載的jar是和平臺(tái)相關(guān)的硅确,所以你下載的jar包要選擇和你平臺(tái)相匹配的才行哦~~,當(dāng)然明肮,這里的這個(gè)jar其實(shí)已經(jīng)是linux菱农、win、macOS全平臺(tái)都具有的了柿估,它會(huì)根據(jù)平臺(tái)加載相應(yīng)的那個(gè)組件循未。
  2. 加載這個(gè)jar的理由是,我們上面加載的netty-tcnative-boringssl-static這個(gè)依賴(lài)秫舌,和Tomcat內(nèi)部的tcnative實(shí)現(xiàn)相沖突了的妖,所以要用這個(gè)jar包要進(jìn)行適配绣檬,具體的底層原理這里不研究,你只要記住嫂粟,如果你使用Tomcat娇未,就要加上步驟3的這個(gè)依賴(lài),Jetty實(shí)測(cè)不需要這個(gè)依賴(lài)星虹。
  1. 添加從Apple開(kāi)發(fā)者平臺(tái)申請(qǐng)到的app證書(shū)文件到項(xiàng)目資源目錄下
  1. 開(kāi)始編碼:創(chuàng)建ApnsClient對(duì)象實(shí)例:根據(jù)證書(shū)和證書(shū)密碼創(chuàng)建和APNs服務(wù)器的連接對(duì)象
ApnsClient apnsClient = new ApnsClientBuilder().setClientCredentials(new File("/path/to/p12-file"), "p12-file-password").build();
  1. 等待和APNs的連接成功(HTTP/2是異步的零抬,但是這里連接沒(méi)成功后續(xù)步驟無(wú)法繼續(xù),所以需要等待):

Future<Void> connectFuture = apnsClient.connect(ApnsClient.PRODUCTION_APNS_HOST);
connectFuture.await();

>鏈接地址有`DEVELOPMENT_APNS_HOST`和`PRODUCTION_APNS_HOST`兩個(gè)宽涌,你要確認(rèn)你拿到的證書(shū)是否支持開(kāi)發(fā)者模式連接開(kāi)發(fā)者服務(wù)器媚值,我拿到的證書(shū)就是不支持的,需要直接連接正式服務(wù)器护糖,這是我踩的坑。

>7. 封裝推送消息內(nèi)容體:
```java
ApnsPayloadBuilder payloadBuilder = new ApnsPayloadBuilder();
        payloadBuilder.setAlertBody("alert-message-body");

ApnsPayloadBuilder這個(gè)類(lèi)可以好好看看嚼松,就是這個(gè)類(lèi)封裝推送消息體嫡良,攜帶了APNs推送能發(fā)送的各個(gè)字段,比如顯示按鈕献酗、通知消息標(biāo)題寝受、消息體、圖片名罕偎、消息聲音文件名等很澄,還由于APNs對(duì)每個(gè)消息最大長(zhǎng)度限制為4K,因此還對(duì)過(guò)長(zhǎng)的消息進(jìn)行了智能化地截取工作颜及。最后這這個(gè)類(lèi)會(huì)被序列化為json串甩苛,就像如下的

{
"aps" : {
"category" : "NEW_MESSAGE_CATEGORY"
"alert" : {
"body" : "Acme message received from Johnny Appleseed",
},
"badge" : 3,
"sound" : “chime.aiff"
},
"acme-account" : "jane.appleseed@apple.com",
"acme-message" : "message123456"
}

>如果你要自己封裝json也行,只要最后的json中有apple規(guī)定的那些鍵值就行俏站,而額外的讯蒲,你也可以自定義地添加一些自己業(yè)務(wù)所需的其他鍵值方便客戶(hù)端接收到推送消息之后進(jìn)行處理。
>######智能截取4K長(zhǎng)度:
```java
String payload = payloadBuilder.buildWithDefaultMaximumLength();
  1. 封裝消息體:

SimpleApnsPushNotification pushNotification = new SimpleApnsPushNotification(token, "com.example.AppName", payload);

>其中token是每個(gè)設(shè)備生成的token串經(jīng)過(guò)如下代碼加工后得到的肄扎,相當(dāng)于是設(shè)備的唯一id:
>```java
String token = TokenUtil.sanitizeTokenString("<device token>");

"com.example.AppName":這個(gè)是你的證書(shū)簽名墨林,必須保證證書(shū)簽名、證書(shū)犯祠、證書(shū)密碼旭等、產(chǎn)生token的app簽名全部一致,不然就會(huì)報(bào)錯(cuò)衡载。

你的整個(gè)推送消息體封裝好之后搔耕,在網(wǎng)絡(luò)http/2傳輸過(guò)程中的最終傳輸?shù)臄?shù)據(jù)格式如下,主要包括headers和body data:

HEADERS
- END_STREAM
+ END_HEADERS
:method = POST
:scheme = https
:path = /3/device/00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0
host = api.development.push.apple.com
apns-id = eabeae54-14a8-11e5-b60b-1697f925ec7b
apns-expiration = 0
apns-priority = 10
apns-topic = <MyAppTopic>
DATA
+ END_STREAM
{ "aps" : { "alert" : "Hello" } }


>9. 發(fā)送消息推送:
>```java
Future<PushNotificationResponse<SimpleApnsPushNotification>> sendNotificationFuture = apnsClient.sendNotification(pushNotification);

這是一個(gè)異步阻塞方法痰娱,調(diào)用之后推送通知會(huì)放到內(nèi)部消息隊(duì)列度迂,等待APNs接收到消息并反饋的時(shí)候才能通過(guò)下面的方法得到響應(yīng)藤乙,否則下面這個(gè)方法就會(huì)阻塞著,上線產(chǎn)品建議寫(xiě)成異步回調(diào)的方式:

PushNotificationResponse<SimpleApnsPushNotification> pushNotificationResponse = sendNotificationFuture.get();


>10. 接收APNs服務(wù)器響應(yīng):
>```java
pushNotificationResponse.isAccepted();

以下方法獲取APNs服務(wù)器拒絕消息的相關(guān)響應(yīng)信息:

pushNotificationResponse.getRejectionReason();//獲取拒絕原因
pushNotificationResponse.getTokenInvalidationTimestamp();//獲取token失效時(shí)間


>11. 連接斷開(kāi)重連:
>```java
apnsClient.getReconnectionFuture().await();
  1. 關(guān)閉連接惭墓,釋放資源:

Future<Void> disconnectFuture = apnsClient.disconnect();
disconnectFuture.await();


>* 踩坑記錄:Pushy項(xiàng)目中依賴(lài)了gson坛梁,如下:
>```xml
<dependency>
     <groupId>com.google.code.gson</groupId>
     <artifactId>gson</artifactId>
     <version>2.6.2</version>
</dependency>

如果你添加了如下的gson:

<dependency>
<groupId>com.google</groupId>
<artifactId>gson</artifactId>
<version>1.3</version>
</dependency>

>那你的com.google的gson就會(huì)和Pushy中的gson沖突,然后出現(xiàn)未知的錯(cuò)誤腊凶。划咐。。知道就行钧萍,具體和這兩個(gè)gson的差別有關(guān)褐缠,我沒(méi)了解。风瘦。队魏。這種坑也只有我這種人品的能踩到。万搔。胡桨。最好是兩個(gè)夠不添加,反正Pushy在Maven中就會(huì)自動(dòng)依賴(lài)引入了瞬雹,何必多此一舉昧谊。

# 消息包裝實(shí)戰(zhàn)
我本人沒(méi)接觸過(guò)iOS開(kāi)發(fā),因此對(duì)這個(gè)消息體那些字段有些什么用不是搞得很清楚酗捌,只是大概知道圖標(biāo)呢诬、按鈕顯示、聲音等意思胖缤,但是具體到iOS設(shè)備接收到之后會(huì)有什么樣的消息體現(xiàn)尚镰,我不是很清楚,但是懂得的人看了下面我的測(cè)試樣例哪廓,我相信也能很快包裝出自己想要的消息~
##### 消息json中的`aps字段`一看就知道是apple推送到設(shè)備之后的專(zhuān)屬識(shí)別字段钓猬,在該字段下的每個(gè)子字段分別有相應(yīng)的作用和意義,再來(lái)就是`自定義字段`撩独,在json中會(huì)和aps同一級(jí)別目錄下展示敞曹,這個(gè)是開(kāi)發(fā)者自己知道的字段,在客戶(hù)端自行解析和提取使用综膀。

>* 消息包裝代碼展示:
>```java
ApnsPayloadBuilder payloadBuilder = new ApnsPayloadBuilder();
//體現(xiàn)在aps的category字段
payloadBuilder.setCategoryName("category");
//體現(xiàn)在aps的content-available字段
payloadBuilder.setContentAvailable(true);
//彈出窗消息圖標(biāo)澳迫,aps的alert字段的launch-image字段
payloadBuilder.setLaunchImageFileName("icon.icon");
//以下為兩種彈出窗的消息封裝模式
//彈出窗消息封裝1
payloadBuilder.setAlertBody("Example!");//aps的alert字段的body字段
payloadBuilder.setAlertSubtitle("AlertSubtitle");//aps的alert字段的title字段
payloadBuilder.setAlertTitle("AlertTitle");//aps的alert字段的subtitle字段
//彈出窗消息封裝2
payloadBuilder.setLocalizedActionButtonKey("LocalizedActionButtonKey");//aps的alert字段的action-loc-key字段
payloadBuilder.setLocalizedAlertMessage("LocalizedAlertMessage");//aps的alert字段的loc-key字段
payloadBuilder.setLocalizedAlertSubtitle("LocalizedAlertSubtitle");//aps的alert字段的subtitle-loc-key字段
payloadBuilder.setLocalizedAlertTitle("LocalizedAlertTitle");//aps的alert字段的title-loc-key字段
//消息通知聲音,aps的sound字段
payloadBuilder.setSoundFileName("sound.wav");
//aps的badge字段
payloadBuilder.setBadgeNumber(1);
//aps的mutable-content字段
payloadBuilder.setMutableContent(true);
//自定義鍵值對(duì)剧劝,其中value是Object橄登,可以支持多層的json字串,這個(gè)根據(jù)業(yè)務(wù)需求而定
payloadBuilder.addCustomProperty("name","value");
//是否顯示動(dòng)作按鈕,這個(gè)沒(méi)在json中體現(xiàn)啊拢锹,可能在header中體現(xiàn)吧谣妻,沒(méi)研究
payloadBuilder.setShowActionButton(true);
String payload = payloadBuilder.buildWithDefaultMaximumLength();
String token = TokenUtil.sanitizeTokenString("aa1e3286fcf87a68f9e8be642d9661c4a4537e34fe4abab68a9681ced773c18f");
System.out.println("payload = " + payload);
System.out.println("token = " + token);
SimpleApnsPushNotification pushNotification = new SimpleApnsPushNotification(token, "cn.geili.KoudaiGouwu", payload);
System.out.println(pushNotification.toString());
  • 其中彈出窗消息封裝有兩種,如下所示
  • 彈出窗消息封裝1卒稳,json展示:

{
"aps": {
"category": "category",
"content-available": 1,
"alert": {
"body": "Example!",
"launch-image": "icon.icon",
"title": "AlertTitle",
"subtitle": "AlertSubtitle"
},
"sound": "sound.wav",
"badge": 1,
"mutable-content": 1
},
"name": "value"
}

>* 彈出窗消息封裝2蹋半,json展示:
>```javascript
{
    "aps": {
        "category": "category",
        "content-available": 1,
        "alert": {
            "launch-image": "icon.icon",
            "action-loc-key": "LocalizedActionButtonKey",
            "loc-key": "LocalizedAlertMessage",
            "subtitle-loc-key": "LocalizedAlertSubtitle",
            "title-loc-key": "LocalizedAlertTitle"
        },
        "sound": "sound.wav",
        "badge": 1,
        "mutable-content": 1
    },
    "name": "value"
}

結(jié)語(yǔ)

教程完結(jié),有了這個(gè)教程充坑,差不多就可以在生產(chǎn)環(huán)境中部署新版的APNs推送服務(wù)了减江,你只需要將以上的教程代碼進(jìn)行相應(yīng)的封裝,根據(jù)業(yè)務(wù)場(chǎng)景對(duì)消息體json也進(jìn)行相應(yīng)的封裝捻爷,剩余的事情都交給這個(gè)Pushy框架即可辈灼,內(nèi)部對(duì)消息隊(duì)列、失敗重傳等都進(jìn)行了處理也榄,不過(guò)為了更好地開(kāi)發(fā)出高性能高并發(fā)的推送服務(wù)器巡莹,最好還是對(duì)內(nèi)部原理深入理解,特別是Netty內(nèi)部細(xì)節(jié)(這個(gè)可是Pushy底層的網(wǎng)絡(luò)支持和IO框架)甜紫。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末降宅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子棵介,更是在濱河造成了極大的恐慌,老刑警劉巖吧史,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邮辽,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡贸营,警方通過(guò)查閱死者的電腦和手機(jī)吨述,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)钞脂,“玉大人揣云,你說(shuō)我怎么就攤上這事”校” “怎么了邓夕?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)阎毅。 經(jīng)常有香客問(wèn)我焚刚,道長(zhǎng),這世上最難降的妖魔是什么扇调? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任矿咕,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘碳柱。我一直安慰自己捡絮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布莲镣。 她就那樣靜靜地躺著福稳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪剥悟。 梳的紋絲不亂的頭發(fā)上灵寺,一...
    開(kāi)封第一講書(shū)人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音区岗,去河邊找鬼略板。 笑死,一個(gè)胖子當(dāng)著我的面吹牛慈缔,可吹牛的內(nèi)容都是我干的叮称。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼藐鹤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼瓤檐!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起娱节,我...
    開(kāi)封第一講書(shū)人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤挠蛉,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后肄满,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體谴古,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年稠歉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掰担。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡怒炸,死狀恐怖带饱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情阅羹,我是刑警寧澤勺疼,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站捏鱼,受9級(jí)特大地震影響恢口,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜穷躁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一耕肩、第九天 我趴在偏房一處隱蔽的房頂上張望因妇。 院中可真熱鬧,春花似錦猿诸、人聲如沸婚被。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)址芯。三九已至,卻和暖如春窜觉,著一層夾襖步出監(jiān)牢的瞬間谷炸,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工禀挫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留旬陡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓语婴,卻偏偏與公主長(zhǎng)得像描孟,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子砰左,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 點(diǎn)擊查看原文 Web SDK 開(kāi)發(fā)手冊(cè) SDK 概述 網(wǎng)易云信 SDK 為 Web 應(yīng)用提供一個(gè)完善的 IM 系統(tǒng)...
    layjoy閱讀 13,758評(píng)論 0 15
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理匿醒,服務(wù)發(fā)現(xiàn),斷路器缠导,智...
    卡卡羅2017閱讀 134,652評(píng)論 18 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,079評(píng)論 25 707
  • 本文的大部分內(nèi)容是對(duì)蘋(píng)果關(guān)于APNs官方文檔的翻譯以及整理廉羔。 一、設(shè)備token和消息的生命周期 關(guān)于設(shè)備toke...
    EA88閱讀 17,519評(píng)論 14 44
  • 洗澡時(shí)冒出個(gè)想法僻造,寫(xiě)本書(shū)憋他。 內(nèi)容寫(xiě)什么呢,在沒(méi)寫(xiě)之前嫡意,我腦子里想的是源源不斷的收入举瑰。好像不管寫(xiě)什么捣辆,都能產(chǎn)生巨大的...
    時(shí)汝佳閱讀 407評(píng)論 0 0