極光推送學(xué)習(xí)

極光推送:

1.JPush當(dāng)前版本是1.8.2嫁蛇,其SDK的開發(fā)除了正常的功能完善和擴(kuò)展外也緊隨蘋果官方的步伐害晦,SDK在iOS8剛推出的時(shí)候跟進(jìn)更新询筏。在一定程度上來講,能夠體現(xiàn)極光推送的技術(shù)力量是比較強(qiáng)大的竖慧;

2.應(yīng)用內(nèi)消息提高了推送服務(wù)的及時(shí)性和可靠性嫌套,在、緊隨當(dāng)下追求應(yīng)用實(shí)時(shí)性的潮流圾旨,能夠提供更好的推送服務(wù)的用戶體驗(yàn)踱讨;

3.JPush除了有著完善的控制臺(tái)外,還提供了服務(wù)端的接口砍的,方便開發(fā)者的服務(wù)器調(diào)用痹筛,方便開發(fā)者對于應(yīng)用管理、整合即通過應(yīng)用服務(wù)器極光服務(wù)器推送服務(wù)器用戶設(shè)備廓鞠,簡化了服務(wù)器端的開發(fā)強(qiáng)度帚稠,也方便運(yùn)營期間發(fā)送推送消息的流程;

4.如果開發(fā)者同時(shí)需要用戶統(tǒng)計(jì)和推送功能床佳,那么這是一款不容錯(cuò)過的SDK因?yàn)镴Push提供了全面直觀的統(tǒng)計(jì)數(shù)據(jù)滋早,在控制臺(tái)中以圖表的形式呈現(xiàn),方便運(yùn)營期間的數(shù)據(jù)分析砌们;

5.當(dāng)前版本的SDK僅有5M左右的體積杆麸,嵌入應(yīng)用或者打包應(yīng)用影響都是很小的,開發(fā)者大可不必?fù)?dān)心浪感。

6.對于高級(jí)用戶還開放了富媒體推送和更多的API調(diào)用次數(shù)角溃,當(dāng)然這也需要開發(fā)者付出一定的費(fèi)用,對于有需要的開發(fā)者可以自行了解篮撑。

綜合評(píng)價(jià):

筆者在測試極光推送之前也測試過其他的推送服務(wù)减细,在項(xiàng)目中使用的也是極光推送。對比這些推送服務(wù)來講赢笨,極光是從文檔未蝌、SDK簡潔程度驮吱、集成難度、服務(wù)都較為滿意的萧吠。在筆者的應(yīng)用中有需要統(tǒng)計(jì)用戶行為的需求左冬,而極光也恰恰滿足了這種需求,帶來了極大的方便纸型∧磁椋總而言之,極光推送確實(shí)能給開發(fā)者帶來不錯(cuò)的編程體驗(yàn)狰腌。

通過筆者的測試除破,極光SDK的集成難度不大,文檔注釋清晰易懂琼腔。而且有較為強(qiáng)大的技術(shù)支持(QQ群瑰枫、論壇、博客)丹莲。所以對于有一定開發(fā)經(jīng)驗(yàn)的需要使用推送服務(wù)光坝、本地推送服務(wù)、推送統(tǒng)計(jì)的開發(fā)者是非常適用的甥材,而即使是對推送要求不高的項(xiàng)目盯另,極光SDK的開發(fā)成本也并不是很高,所以具有較為寬泛的適用范圍洲赵。

極光推送是推送服務(wù)商中較大的一個(gè)土铺,目前的用戶量也相當(dāng)龐大,其文檔板鬓、注釋悲敷、網(wǎng)站控制臺(tái)都較為完善。極光SDK對推送服務(wù)進(jìn)行高級(jí)封裝俭令,除了實(shí)現(xiàn)APNS推送以外還實(shí)現(xiàn)了基于TCP連接的應(yīng)用內(nèi)消息后德,在一定程度上提高了推送服務(wù)的可靠性。其SDK非常簡潔抄腔,簡化了集成過程瓢湃,而通過閱讀API的注釋及相關(guān)文檔控轿,開發(fā)者可以很快的掌握SDK的使用雁竞。對于有一定開發(fā)經(jīng)驗(yàn)的開發(fā)者是非常易于上手的

高送達(dá)率咖耘,時(shí)效保證:極光推送自主協(xié)議確認(rèn)推送消息的送達(dá)豺谈。開發(fā)者可以靈活的自定義推送時(shí)間

服務(wù)集成SDK簡單:簡單復(fù)制便可直接編譯運(yùn)行

推送內(nèi)容多樣性服務(wù):除了通知,還有消息沃饶、多媒體等

使你的應(yīng)用程序保持高注意力:即使用戶沒有打開應(yīng)用程序胰锌,極光推送也能夠推送通知到達(dá)用戶手機(jī)

自定義消息內(nèi)容:JPushSDK把內(nèi)容完全轉(zhuǎn)給應(yīng)用程序治泥,由開發(fā)者應(yīng)用程序去處理自定義消息

接入即可用功能:客戶端集成SDK即可享受高效?專業(yè)的推送服務(wù),操作簡單的管理后臺(tái)

多平臺(tái):同時(shí)支持?Android與iOS平臺(tái)

高安全性:傳輸信道加密,推送數(shù)據(jù)自主加密

高穩(wěn)定性筏勒,大容量移迫,高并發(fā):目前極光推送平臺(tái)支持?十億級(jí)用戶,高達(dá)20萬/秒的下行速度

省流量管行,低耗電功能強(qiáng)大:待機(jī)流量消耗20K/天厨埋,電?量消耗30mAh/天? 畢竟國外網(wǎng)絡(luò)服務(wù)在中國都頗為不完善,而且推送服務(wù)對網(wǎng)絡(luò)和服務(wù)器端有較高的要求捐顷。因此荡陷,國內(nèi)很快就興起數(shù)家像極光推送云消息推送服務(wù)的公司,是國內(nèi)做得較好的一家迅涮。

優(yōu)勢:

1)? ?開放注冊废赞,免費(fèi)向所有的開發(fā)者開放使用

2)SDK流量電量消耗很少

3)集成簡單,很快就能夠集成跑起來

4)服務(wù)器端推送支持大并發(fā)量逗柴、延遲小

友盟推送:

1.推送形式多樣:開發(fā)者可以在網(wǎng)頁設(shè)置或者采用API接入方式進(jìn)行消息推送蛹头,并可以選擇文本消息顿肺、應(yīng)用更新以及json式消息戏溺,滿足開發(fā)者不同場景下的運(yùn)營需求。

2.用戶分組靈活:開發(fā)者可以使用多個(gè)預(yù)置條件或者自定義的用戶標(biāo)簽把用戶分組屠尊,對每個(gè)分組的用戶推送更有針對性的消息旷祸,滿足開發(fā)者定向推送的需求。

3.設(shè)備能耗極低:SDK中采用了先進(jìn)的長鏈接多路復(fù)用以及其他優(yōu)化方案讼昆,將用戶設(shè)備的電量和流量消耗控制在最低水平托享,確保用戶體驗(yàn)最優(yōu)。

4.建立與用戶直接溝通的通道:不論是新品上架還是精彩活動(dòng)舉行浸赫,都可以實(shí)時(shí)的推送到用戶設(shè)備闰围,讓用戶第一時(shí)間獲取到相關(guān)信息。適時(shí)而準(zhǔn)確的消息推送既峡,可以大幅度提升用戶的活躍度和忠誠度羡榴。

功能特色

用戶分群推送

開發(fā)者可以使用多個(gè)預(yù)置條件或者自定義的用戶標(biāo)簽把用戶分群,對每個(gè)用戶群的用戶推送更有針對性的消息运敢,滿足開發(fā)者定向推送的需求校仑。

接入方式靈活

開發(fā)者可以通過網(wǎng)站W(wǎng)EB界面或者API接入方式進(jìn)行消息推送,且支持開發(fā)者提交已分群的用戶ID到友盟服務(wù)器進(jìn)行消息推送传惠,滿足開發(fā)者不同場景下的運(yùn)營需求迄沫。

服務(wù)質(zhì)量高

實(shí)現(xiàn)了同一設(shè)備多應(yīng)用共享一個(gè)長連接、智能心跳等優(yōu)化方案卦方。消息發(fā)送速度快羊瘩,長連接穩(wěn)定,設(shè)備能耗低。

優(yōu)點(diǎn):

友盟推送的SDK包很小困后,集成的時(shí)候不需要導(dǎo)入其它庫乐纸,集成后對應(yīng)用影響很小,使用方便摇予;

在網(wǎng)站的應(yīng)用信息中汽绢,可以看到推送的歷史記錄:包括發(fā)送總數(shù)、用戶打開數(shù)侧戴、打開率等等宁昭,方便統(tǒng)計(jì)

友盟推送集成是最簡單,使用也最方便酗宋,推送的渠道也是多樣的积仗。

缺點(diǎn):

但是在同網(wǎng)絡(luò)環(huán)境下,友盟的推送速度卻是比不上極光推送的蜕猫。而服務(wù)器端的api使用不太方便寂曹,需要設(shè)置一個(gè)服務(wù)器IP地址才能使用。自定義的字?jǐn)?shù)最多只有1500字

百度推送:

1.Push 服務(wù)

Push 服務(wù)初始化及綁定

Push 服務(wù)解除綁定

2.Tag 管理

創(chuàng)建 tag

刪除 tag

列出 tag

3.通知推送

4.推送效果反饋

百度有延遲回右,推送不穩(wěn)定隆圆,不人性化,很多也只是為了服務(wù)百度系的公司來用翔烁。到達(dá)率存在問題渺氧。開發(fā)者網(wǎng)站不是非常用戶友好。

百度推送蹬屹,推送Android還行侣背,在推送ios時(shí)推送內(nèi)容有限制,官方說是不能大于4k慨默,但是實(shí)際推送的內(nèi)容80個(gè)漢字以上就不行贩耐,推送不了。

優(yōu)點(diǎn):

1.1厦取、 推送及時(shí)潮太,支持推送通知、穿透消息推送蒜胖、富媒體消息推送消别。

1.2、 提供常見問題解答台谢。

1.3寻狂、 提供客服支持。

缺點(diǎn):

1.1朋沮、 與其他推送平臺(tái)相比官網(wǎng)沒有詳細(xì)的開發(fā)文檔蛇券,不方便開發(fā)者查閱缀壤。

1.2、 與其他推送平臺(tái)相比纠亚,集成困難塘慕。

建議:在官網(wǎng)放一份詳細(xì)的開發(fā)文檔,方便開發(fā)者查閱和集成蒂胞。

iOS推送簡介:

在移動(dòng)應(yīng)用中图呢,推送已經(jīng)成為不可缺少的重要功能。本文檔主要介紹有關(guān) iOS 推送的基礎(chǔ)知識(shí)骗随,并從幾個(gè)典型問題出發(fā)蛤织,分析如何解決在實(shí)現(xiàn)推送中出現(xiàn)的一些問題。

1.1 本地通知和遠(yuǎn)程通知簡介

在 iOS 設(shè)備上(模擬器無法使用推送)鸿染,系統(tǒng)收到通知后這樣處理:

在屏幕上彈出一些選項(xiàng)指蚜,或者在屏幕頂部顯示橫幅(banner)如下圖左

App 的角標(biāo)數(shù)值發(fā)生變化,具體表現(xiàn)為 App icon 右上角的小紅點(diǎn)及數(shù)字涨椒,如郵件中的紅點(diǎn)

伴隨推送消息的提示聲音

當(dāng)應(yīng)用處于前臺(tái)運(yùn)行時(shí)摊鸡,系統(tǒng)是不會(huì)在屏幕上顯示通知,但是仍會(huì)調(diào)用相應(yīng)的 API蚕冬。

只有真機(jī)可以使用推送功能免猾。

用戶可以設(shè)置每一個(gè) App 的通知權(quán)限,如下圖最后一個(gè):

用戶可以選擇關(guān)閉某個(gè)應(yīng)用的推送功能播瞳。還可以設(shè)置通知是否在通知中心顯示掸刊、通知到達(dá)時(shí)是否發(fā)出聲音免糕、通知能否改變 App 角標(biāo)以及鎖屏?xí)r是否顯示該 App 的通知赢乓,還可以設(shè)置通知到達(dá)時(shí)的提醒樣式。當(dāng)然石窑,這些都可以分別對每一個(gè)應(yīng)用進(jìn)行單獨(dú)設(shè)置牌芋。

1.1.1 本地通知

本地通知是一種基于時(shí)間的提醒方式。

本地通知最多向系統(tǒng)注冊 64 個(gè)松逊,當(dāng)超過這個(gè)數(shù)量后躺屁,最早注冊的本地通知會(huì)被丟棄。

本地通知在 iOS 設(shè)備上的顯示與遠(yuǎn)程推送通知一樣经宏。

1.1.2 遠(yuǎn)程通知

iOS App 運(yùn)行在后臺(tái)時(shí)犀暑,無法主動(dòng)進(jìn)行網(wǎng)絡(luò)連接。

遠(yuǎn)程通知可以用來提醒用戶烁兰。發(fā)送遠(yuǎn)程通知時(shí)耐亏,服務(wù)器首先需要使用推送證書與 APNs(Apple Push Notification service)建立安全連接,然后將消息傳遞給它沪斟。當(dāng) APNs 收到消息后广辰,會(huì)通過與手機(jī)之間的長連接下發(fā)到對應(yīng)的手機(jī)上,然后 iOS 彈出這條消息來提醒用戶。

用戶看通過點(diǎn)擊或滑動(dòng)通知來運(yùn)行 App择吊±罡可通過程序中相應(yīng)的方法可以獲取通知信息,然后做相應(yīng)的邏輯處理几睛。

如果不是通過通知啟動(dòng) App房轿,那么無法在程序中獲取通知信息。例如通過點(diǎn)擊應(yīng)用圖標(biāo)啟動(dòng) App所森。

推送通知都包含 Payload:一個(gè) Apple 已經(jīng)定義好的屬性列表冀续,操作系統(tǒng)根據(jù)它決定使用哪一種方式來提醒用戶。還可以在 Payload 中加入一些自定義數(shù)據(jù)必峰。

通知并不能一定到達(dá)洪唐。如果在用戶無法收到推送通知時(shí)(關(guān)機(jī)或網(wǎng)絡(luò)不可用),這種情況下 APNs 收到了多條發(fā)往這臺(tái)設(shè)備的通知吼蚁,那么只會(huì)保留最后一條凭需,最早的會(huì)被丟棄。

APNs 首先通過移動(dòng)蜂窩網(wǎng)絡(luò)發(fā)送通知肝匆,只有當(dāng)移動(dòng)蜂窩網(wǎng)絡(luò)不可用時(shí)才使用 Wi-Fi粒蜈。

1.2 通知的兩種推送環(huán)境

在使用 iOS 遠(yuǎn)程推送功能時(shí),有兩種不同的環(huán)境旗国。開發(fā)環(huán)境(Development)以及生產(chǎn)環(huán)境(Production)枯怖。

App 當(dāng)前使用的推送環(huán)境與 Xcode - Build Settings - Code Signing - Provisioning Profile 文件的模式一致。

1.2.1 證書與證書校驗(yàn)

與 APNs 之間是加密的連接能曾,因此需要使用證書來加密連接度硝。每個(gè)的推送環(huán)境有自己單獨(dú)的推送證書,即開發(fā)證書和生產(chǎn)證書寿冕。

在將證書最終轉(zhuǎn)為 pem 格式后蕊程,可通過與 APNs 連接來測試證書是否有效。

開發(fā)環(huán)境:

openssl s_client -connectgateway.sandbox.push.apple.com:2195-cert MyApnsDev.pem

生產(chǎn)環(huán)境:

openssl s_client -connectgateway.push.apple.com:2195-cert MyApnsPro.pem

當(dāng)輸入完命令回車后驼唱,終端首先會(huì)輸出很多相關(guān)信息藻茂。

當(dāng)連接建立失敗時(shí),會(huì)直接 close 掉玫恳。

當(dāng)連接建立成功時(shí)辨赐,終端會(huì)停止輸出,并等待你輸入京办,你可以隨便輸入一些字符后摁回車掀序,然后連接才會(huì)關(guān)閉。

以上命令在 Mac 下沒有問題臂港,在其他操作系統(tǒng)下需要指定服務(wù)器當(dāng)前的 CA 根證書森枪,具體可以從網(wǎng)站上下載:

entrust_2048_ca.cer 下載

下載完成之后视搏,在以上命令后面加上 -CAfile entrust2048ca.cer 即可

1.2.2 DeviceToken

通知需推送到具體某一臺(tái)設(shè)備,而 DeviceToken 就是這臺(tái)設(shè)備的標(biāo)識(shí)符县袱。在向 APNs 發(fā)送推送通知時(shí)浑娜,需要使用 DeviceToken 來指定這條通知將要到達(dá)的設(shè)備 。

每一臺(tái)設(shè)備式散,不同的推送環(huán)境下分別有一個(gè) DeviceToken筋遭。即 DeviceToken 也分開發(fā)環(huán)境和生產(chǎn)環(huán)境。

應(yīng)用在向 APNs 注冊推送通知時(shí)暴拄,會(huì)根據(jù)當(dāng)前 Xcode 工程中 target 對應(yīng)的 Provisioning Profile 決定請求對應(yīng)環(huán)境的 DeviceToken漓滔。即系統(tǒng)返回的 DeviceToken 其環(huán)境與 Provisioning Profile 的環(huán)境對應(yīng)。

一般在使用 Xcode 開發(fā)時(shí)乖篷,Provisioning Profile 為 Development 响驴,因此獲取的 DeviceToken 也是開發(fā)模式。

在 App 需要打包為 ipa 文件或者需要上傳到 AppStore 時(shí)撕蔼,會(huì)將 Provisioning Profile 文件修改為 Distribution豁鲤,這時(shí)獲取到的 DeviceToken 是生產(chǎn)模式。

1.3 在應(yīng)用程序中注冊遠(yuǎn)程推送功能

App 必須要向 APNs 請求注冊以實(shí)現(xiàn)推送功能鲸沮,在請求成功后琳骡,APNs 會(huì)返回一個(gè)設(shè)備的標(biāo)識(shí)符即 DeviceToken 給 App,服務(wù)器在推送通知的時(shí)候需要指定推送通知目的設(shè)備的 DeviceToken讼溺。在 iOS 8 以及之后楣号,注冊推送服務(wù)主要分為四個(gè)步驟:

使用registerUserNotificationSettings:注冊應(yīng)用程序想要支持的推送類型

通過調(diào)用registerForRemoteNotifications方法向 APNs 注冊推送功能

請求成功時(shí),系統(tǒng)會(huì)在應(yīng)用程序委托方法中返回 DeviceToken怒坯,請求失敗時(shí)炫狱,也會(huì)在對應(yīng)的委托方法中給出請求失敗的原因。

將 DeviceToken 上傳到服務(wù)器敬肚,服務(wù)器在推送時(shí)使用毕荐。

上述第一個(gè)步驟注冊的 API 是 iOS 8 新增的束析,因此在 iOS 7艳馒,前兩個(gè)步驟需更改為 iOS 7 中的 API。

DeviceToken 有可能會(huì)更改员寇,因此需要在程序每次啟動(dòng)時(shí)都去注冊并且上傳到你的服務(wù)器端弄慰。

注意:iOS 設(shè)備與 APNs 需要建立一條長連接,之后的推送注冊以及推送獲取都需要通過這條長連接蝶锋。

當(dāng) iOS 設(shè)備無網(wǎng)絡(luò)可用時(shí)陆爽,在向 APNs 請求注冊后,既不會(huì)有注冊成功的回調(diào)扳缕,也不會(huì)有注冊失敗的回調(diào)慌闭。如果發(fā)生這種情況别威,需要檢查設(shè)備的網(wǎng)絡(luò)連接。

開發(fā)環(huán)境和生產(chǎn)環(huán)境建立不同的長連接驴剔,且設(shè)備上要至少有一個(gè)開發(fā)環(huán)境的 App 并且注冊推送省古,才會(huì)建立開發(fā)環(huán)境下的長連接。

當(dāng)已經(jīng)注冊過推送服務(wù)后丧失,以后系統(tǒng)會(huì)立刻返回給你 DeviceToken豺妓。還有一點(diǎn)是,返回 DeviceToken 的回調(diào)方法并不一定是只有在你注冊或者再次注冊時(shí)才被系統(tǒng)調(diào)用布讹,當(dāng)DeviceToken 改變時(shí)也會(huì)被系統(tǒng)直接調(diào)用琳拭。

1.4 在程序中處理通知

我們來看一下當(dāng)系統(tǒng)傳遞本地或遠(yuǎn)程通知的時(shí)候都有哪些情況。

通知到達(dá)時(shí)描验,應(yīng)用不在前臺(tái)白嘁。

這種情況下,操作系統(tǒng)將呈現(xiàn)通知膘流,彈出一個(gè)選項(xiàng)或者在屏幕上部顯示 banner 提醒权薯,給 App 角標(biāo)設(shè)置對應(yīng)的數(shù)字,可能播放提示音睡扬,當(dāng)用戶稍微往下滑動(dòng)通知時(shí)顯示更多的 action 按鈕(如果有設(shè)置)盟蚣。

通知到達(dá)時(shí),應(yīng)用在前臺(tái)運(yùn)行卖怜。

這種情況下屎开,不會(huì)彈出 banner。系統(tǒng)會(huì)根據(jù)當(dāng)前的通知是本地通知還是遠(yuǎn)程通知马靠,調(diào)用不同的方法奄抽,并且在方法中傳遞通知的 Payload 數(shù)據(jù)。

在 iOS8 的通知中甩鳄,用戶點(diǎn)擊通知上的自定義 action 按鈕進(jìn)入應(yīng)用逞度。

這種情況下,系統(tǒng)會(huì)調(diào)用 iOS8 中對 action 按鈕新增的處理方法妙啃。

用戶點(diǎn)擊通知進(jìn)入應(yīng)用档泽。

系統(tǒng)會(huì)根據(jù)當(dāng)前的通知是本地通知還是遠(yuǎn)程通知,調(diào)用不同的方法揖赴,并且在方法中傳遞通知的 Payload 數(shù)據(jù)馆匿。

注意:只有當(dāng)通過通知進(jìn)入到 App 時(shí),才可以獲取到通知信息燥滑。

1.4.1 自定義通知提示音

你可以在 App 的 Bundle 中加入一段自定義提示音文件渐北。然后當(dāng)通知到達(dá)時(shí)可以指定播放這個(gè)文件。必須為以下幾種數(shù)據(jù)格式:

Linear PCM

MA4(IMA/ADPCM)

μLaw

aLaw

你可以將它們打包為aiff铭拧、wav或caf文件赃蛛。自定義的聲音文件時(shí)間必須小于 30 秒恃锉,如果超過了這個(gè)時(shí)間,將被系統(tǒng)聲音代替呕臂。

1.4.2 Payload

Payload 是通知的一部分淡喜,每一條推送通知都包含一個(gè) Payload。它包含了系統(tǒng)提醒用戶通知到達(dá)的方式诵闭,還可以添加自定義的數(shù)據(jù)炼团。即通知主要傳遞的數(shù)據(jù)為 Payload。

Payload 本身為 JSON 格式的字符串疏尿,它內(nèi)部必須要包含一個(gè)鍵為aps的字典瘟芝。aps 中可以包含以下字段中的一個(gè)或多個(gè):

alert:其內(nèi)容可以為字符串或者字典,如果是字符串褥琐,那么將會(huì)在通知中顯示這條內(nèi)容

badge:其值為數(shù)字锌俱,表示當(dāng)通知到達(dá)設(shè)備時(shí),應(yīng)用的角標(biāo)變?yōu)槎嗌俚谐省H绻麤]有使用這個(gè)字段贸宏,那么應(yīng)用的角標(biāo)將不會(huì)改變。設(shè)置為 0 時(shí)磕洪,會(huì)清除應(yīng)用的角標(biāo)吭练。

sound:指定通知展現(xiàn)時(shí)伴隨的提醒音文件名。如果找不到指定的文件或者值為 default析显,那么默認(rèn)的系統(tǒng)音將會(huì)被使用鲫咽。如果為空,那么將沒有聲音谷异。

content-available:此字段為 iOS 7 silent remote notification 使用分尸。不使用此功能時(shí)無需包含此字段。

推送通知跟NSNotification有所區(qū)別:

1> NSNotification是抽象的歹嘹,不可見的

2> 推送通知是可見的(能用肉眼看到)

iOS中提供了2種推送通知: 本地推送通知, 遠(yuǎn)程推送通知

1> 本地推送通知(Local Notification)

2> 遠(yuǎn)程推送通知(Remote Notification)

推送通知可以不讓在前臺(tái)運(yùn)行的app,告知app內(nèi)部發(fā)生了什么變化,比如:有新的內(nèi)容,新消息等.

推送通知的使用:

發(fā)出推送通知時(shí)箩绍,如果當(dāng)前程序正運(yùn)行在前臺(tái),那么推送通知就不會(huì)被呈現(xiàn)出來

點(diǎn)擊推送通知后尺上,默認(rèn)會(huì)自動(dòng)打開發(fā)出推送通知的app

不管app打開還是關(guān)閉材蛛,推送通知都能如期發(fā)出

推送通知有5種呈現(xiàn)效果:

1. 在屏幕頂部顯示一塊橫幅(顯示具體內(nèi)容)

2. 在屏幕中間彈出一個(gè)UIAlertView(顯示具體內(nèi)容,使用較少)

3. 在鎖屏界面顯示一塊橫幅(鎖屏狀態(tài)下,顯示具體內(nèi)容)

4. 播放音效(提醒作用)

5. 更新app圖標(biāo)的數(shù)字(說明新內(nèi)容的數(shù)量)

推送通知分為本地推送通知和遠(yuǎn)程推送通知,下面一一介紹.

本地推送通知

本地推送通知不需要服務(wù)器的支持,不需要聯(lián)網(wǎng)就可以發(fā)送通知.通常本地推送通知用于定時(shí)提醒用戶,比如清理垃圾,淘寶購物,紀(jì)念日提醒等任務(wù).

在蘋果官方給出了本地推送通知的一些屬性,以及使用.

屬性介紹:

@property(nonatomic,copy)NSDate *fireDate;// 設(shè)置本地推送的時(shí)間@property(nonatomic,copy) NSTimeZone *timeZone;// 時(shí)區(qū)(一般設(shè)置為[NSTimeZone defaultTimeZone] 尖昏,跟隨手機(jī)的時(shí)區(qū))@property(nonatomic) NSCalendarUnit repeatInterval;// 沒隔多久重復(fù)發(fā)出一次@property(nonatomic,copy)NSCalendar *repeatCalendar;// 設(shè)置日期@property(nonatomic,copy) CLRegion *region NS_AVAILABLE_IOS(8_0);// 比如某一個(gè)區(qū)域的時(shí)候發(fā)出通知@property(nonatomic,assign)BOOL regionTriggersOnce NS_AVAILABLE_IOS(8_0);// 進(jìn)入?yún)^(qū)域是否重復(fù)@property(nonatomic,copy)NSDictionary *userInfo;// 附加的額外信息@property(nonatomic,copy)NSString *alertBody;// 消息的內(nèi)容@property(nonatomic)BOOL hasAction;// 是否顯示alertAction的文字(默認(rèn)是YES)@property(nonatomic,copy)NSString *alertAction;// 設(shè)置鎖屏狀態(tài)下,顯示的一個(gè)文字@property(nonatomic,copy)NSString *alertLaunchImage;// 啟動(dòng)圖片@property(nonatomic,copy)NSString *soundName;//? UILocalNotificationDefaultSoundName@property(nonatomic)NSInteger applicationIconBadgeNumber;// 應(yīng)用圖標(biāo)右上角的提醒數(shù)字@property(nonatomic,copy)NSArray *scheduledLocalNotifications;// 獲得被調(diào)度(定制)的所有本地推送通知(已經(jīng)發(fā)出且過期的推送通知就算調(diào)度結(jié)束仰税,會(huì)自動(dòng)從這個(gè)數(shù)組中移除)

使用方法:

創(chuàng)建本地通知

UILocalNotification *localNoti = [[UILocalNotification alloc] init];

調(diào)度本地推送通知(調(diào)度完畢后,推送通知會(huì)在特地時(shí)間fireDate發(fā)出)

[[UIApplication sharedApplication]scheduleLocalNotification:localNoti];取消調(diào)度本地推送通知

-(void)cancelLocalNotification:(UILocalNotification *)notification;-(void)cancelAllLocalNotifications;

立即發(fā)出本地推送通知

-(void)presentLocalNotificationNow:(UILocalNotification *)notification;

注意:

iOS8.0以后在本地推送通知上新加了一些新功能,為了用戶體驗(yàn),以及更加人性化,如果要使用本地通知抽诉,需要得到用戶的許可.

需要在AppDelegate中添加如下代碼

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {/*? ?? UIUserNotificationTypeNone? ? = 0,? ? ? 沒有,沒有本地通知? ?? UIUserNotificationTypeBadge?? = 1 << 0, 接受圖標(biāo)右上角提醒數(shù)字? ?? UIUserNotificationTypeSound?? = 1 << 1, 接受通知時(shí)候,可以發(fā)出音效? ?? UIUserNotificationTypeAlert?? = 1 << 2, 接受提醒(橫幅/彈窗)? ?? */// iOS8需要添加請求用戶的授權(quán)if ([UIDevice currentDevice].systemVersion.floatValue >=8.0) {? ? ? ? UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil];

[application registerUserNotificationSettings:settings];

}

}

點(diǎn)擊本地推送通知

當(dāng)用戶點(diǎn)擊本地推送通知,會(huì)自動(dòng)打開app吐绵,這里有2種情況

1> app并沒有關(guān)閉迹淌,一直隱藏在后臺(tái)(運(yùn)行在后臺(tái))

讓app進(jìn)入前臺(tái)河绽,并會(huì)調(diào)用AppDelegate的下面方法(并非重新啟動(dòng)app)

-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;

1

1

2> app已經(jīng)被關(guān)閉(進(jìn)程已死)

啟動(dòng)app,啟動(dòng)完畢會(huì)調(diào)用AppDelegate的下面方法

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;

launchOptions參數(shù)通過UIApplicationLaunchOptionsLocalNotificationKey取出本地推送通知對象

若要實(shí)現(xiàn)界面的跳轉(zhuǎn),需要分清當(dāng)前應(yīng)用程序的所處狀態(tài),進(jìn)行判斷:

1> 若是當(dāng)前應(yīng)用在后臺(tái)運(yùn)行,接收到通知時(shí),要想進(jìn)行界面的跳轉(zhuǎn),可以在didReceiveLocalNotification:方法中實(shí)現(xiàn)跳轉(zhuǎn)界面的方法

2> 若是當(dāng)前的應(yīng)用程序已經(jīng)關(guān)閉,我們在前面說到,當(dāng)應(yīng)用關(guān)閉,推送通知也會(huì)如期發(fā)送.但此時(shí),是不會(huì)走didReceiveLocalNotification:方法的,那我們只有didFinishLaunchingWithOptions:方法利用,launchOptions參數(shù)通過UIApplicationLaunchOptionsLocalNotificationKey取出本地推送通知對象,實(shí)現(xiàn)跳轉(zhuǎn)的功能.

下面是演示代碼:

#import"AppDelegate.h"@interfaceAppDelegate ()@end@implementationAppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {/*? ?? UIUserNotificationTypeNone? ? = 0,? ? ? 沒有,沒有本地通知? ?? UIUserNotificationTypeBadge?? = 1 << 0, 接受圖標(biāo)右上角提醒數(shù)字? ?? UIUserNotificationTypeSound?? = 1 << 1, 接受通知時(shí)候,可以發(fā)出音效? ?? UIUserNotificationTypeAlert?? = 1 << 2, 接受提醒(橫幅/彈窗)? ?? */// iOS8需要添加請求用戶的授權(quán)if ([UIDevice currentDevice].systemVersion.floatValue >=8.0) {? ? ? ? UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil];? ? ? ? [application registerUserNotificationSettings:settings];? ? }if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]) {// 跳轉(zhuǎn)界面? ? }returnYES;}/** *? 如果應(yīng)用在后臺(tái),通過點(diǎn)擊通知的時(shí)候打開應(yīng)用會(huì)來到該代理方法 *? 如果應(yīng)用在前臺(tái),接受到本地通知就會(huì)調(diào)用該方法 * *? @param notification 通過哪一個(gè)通知來這里 */- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{if (application.applicationState == UIApplicationStateActive)return;if (application.applicationState == UIApplicationStateInactive) {// 實(shí)現(xiàn)跳轉(zhuǎn)? ? }}- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{returnYES;}#import"ViewController.h"

@interfaceViewController ()// 點(diǎn)擊按鈕之后添加通知- (IBAction)addLocalNote;@end@implementationViewController- (void)viewDidLoad {? ? [super viewDidLoad];? ? [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];}- (IBAction)addLocalNote {// 1.創(chuàng)建本地通知UILocalNotification *localNote = [[UILocalNotification alloc] init];// 設(shè)置什么時(shí)間彈出? ? localNote.fireDate = [NSDate dateWithTimeIntervalSinceNow:5];// 設(shè)置彈出的內(nèi)容? ? localNote.alertBody = @"您有新消息";// 設(shè)置鎖屏狀態(tài)下,顯示的一個(gè)文字? ? localNote.alertAction = @"快點(diǎn)打開";// 是否顯示alertAction的文字(默認(rèn)是YES)? ? localNote.hasAction =YES;// 設(shè)置音效? ? localNote.soundName = UILocalNotificationDefaultSoundName;// 應(yīng)用圖標(biāo)右上角的提醒數(shù)字? ? localNote.applicationIconBadgeNumber =1;// 設(shè)置UserInfo來傳遞信息? ? localNote.userInfo = @{@"alertBody" : localNote.alertBody, @"applicationIconBadgeNumber" : @(localNote.applicationIconBadgeNumber)};// 2.調(diào)度通知? ? [[UIApplication sharedApplication] scheduleLocalNotification:localNote];

}

遠(yuǎn)程推送通知

遠(yuǎn)程推送通知:就是通過網(wǎng)絡(luò)從遠(yuǎn)程服務(wù)器推送給客戶端的通知.

為什么需要遠(yuǎn)程推送通知唉窃?

傳統(tǒng)獲取數(shù)據(jù)的局限性

只要用戶關(guān)閉了app耙饰,就無法跟app的服務(wù)器溝通,無法從服務(wù)器上獲得最新的數(shù)據(jù)內(nèi)容

遠(yuǎn)程推送通知可以解決以上問題

不管用戶打開還是關(guān)閉app纹份,只要聯(lián)網(wǎng)了苟跪,都能接收到服務(wù)器推送的遠(yuǎn)程通知

遠(yuǎn)程推送通知的使用

所有的蘋果設(shè)備,在聯(lián)網(wǎng)狀態(tài)下蔓涧,都會(huì)與蘋果的服務(wù)器建立長連接

長連接: 只要聯(lián)網(wǎng)了件已,就一直建立連接

長連接的作用: 時(shí)間校準(zhǔn), 系統(tǒng)升級(jí), 查找我的iPhone

長連接的好處 : 數(shù)據(jù)傳輸速度快 , 數(shù)據(jù)保持最新狀態(tài)

遠(yuǎn)程推送功能機(jī)制

蘋果給iOS和Mac添加了消息推送的功能,使得我們可以通過后臺(tái)服務(wù)器給應(yīng)用程序(APP)發(fā)送消息元暴,不管APP是否正在使用篷扩,比如郵箱的來件提示功能。這項(xiàng)服務(wù)被稱為Apple Push Notification service(APNs)茉盏。里面一共涉及到四個(gè)角色:APP鉴未、設(shè)備、APNs和應(yīng)用后臺(tái)服務(wù)器(Provider)鸠姨,其中APP铜秆、后臺(tái)服務(wù)器和APNs之間使用deviceToken唯一的標(biāo)識(shí)一個(gè)用戶。

推送服務(wù)的工作流程:

APP向系統(tǒng)注冊推送服務(wù)讶迁。

設(shè)備從APNs請求deviceToken羽峰。

通過代理方法將deviceToken返回給APP。

APP將deviceToken發(fā)送給應(yīng)用后臺(tái)服務(wù)器(Provider)添瓷。

應(yīng)用后臺(tái)服務(wù)器保存deviceToken梅屉,然后在需要推送通知的時(shí)候,給APNs發(fā)送信息鳞贷,使用deviceToken標(biāo)識(shí)所要送達(dá)的客戶端坯汤。

APNs將后臺(tái)服務(wù)器發(fā)過來的數(shù)據(jù)推送到設(shè)備。

設(shè)備將消息分發(fā)給應(yīng)用程序搀愧。

在使用推送功能的時(shí)候惰聂,需要在開發(fā)者中心創(chuàng)建支持Push Notification的證書,并且將證書和私鑰用于應(yīng)用后臺(tái)服務(wù)器與APNs之間通信咱筛。

我也寫了一個(gè)比較易懂的遠(yuǎn)程推送流程圖!http://blog.csdn.net/ismilesky/article/details/48324723

遠(yuǎn)程推送近年來,都是通過第三方進(jìn)行實(shí)現(xiàn),因?yàn)榈谌酵扑偷墓δ鼙容^強(qiáng)大.

推送平臺(tái): 百度推送 , 極光推送, 騰訊信鴿推送, 個(gè)推

遠(yuǎn)程推送我們這里以極光推送(第三方)為例,進(jìn)行測試.

極光遠(yuǎn)程推送的使用

如要使用極光第三方遠(yuǎn)程推送,我們需要集成iOS SDK,需要進(jìn)行應(yīng)用的配置,和環(huán)境配置.在開發(fā)者中心有

developer.apple.com開發(fā)者賬號(hào) , iOS真機(jī)(iPhone搓幌、iPad、iPod)等迅箩。

遠(yuǎn)程推送應(yīng)用配置過程:

創(chuàng)建支持遠(yuǎn)程推送功能的App ID

申請開發(fā)者證書溉愁,并選中剛剛創(chuàng)建的App ID

下載CER文件,并導(dǎo)入鑰匙串管理

申請發(fā)布證書饲趋,并選中剛剛創(chuàng)建的App ID

下載CER文件拐揭,并導(dǎo)入鑰匙串管理

檢查App ID撤蟆,確認(rèn)證書已經(jīng)指定

這些相關(guān)配置極光推送已經(jīng)給了非常詳細(xì)的文檔資料, iOS SDK集成指南 :http://docs.jpush.io/guideline/ios_guide/, iOS SDK調(diào)試指南 :http://docs.jpush.io/client/ios_tutorials/#ios-sdk, 只需要按照流程進(jìn)行就可以.

實(shí)現(xiàn)代碼:

#define kDeviceVersion? ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0)

#import"AppDelegate.h"

#import"APService.h"

@interface AppDelegate ()

@end

@implementationAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

if (kDeviceVersion) {

//可以添加自定義categories??

? ? ? [APService registerForRemoteNotificationTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeSound |UIUserNotificationTypeAlert) categories:nil];??

? }

else {

//categories 必須為nil? ? ? ??

[APService registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound |UIRemoteNotificationTypeAlert) categories:nil];? ?

?}

#else//categories 必須為nil?

?? [APService registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound |UIRemoteNotificationTypeAlert) categories:nil];

#end

if? ? [APService setupWithOption:launchOptions]; return YES;

}

#pragma mark - 獲取device token (必須實(shí)現(xiàn))// 當(dāng)?shù)玫教O果的APNs服務(wù)器返回的DeviceToken就會(huì)被調(diào)用

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{NSLog(@"%@",deviceToken);

// Required? ? [APService registerDeviceToken:deviceToken];}#pragma mark - 獲取device token失敗

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error

{NSLog(@"function == %s? line == %d? error == %@",__FUNCTION__,__LINE__,error);}

// 接收到遠(yuǎn)程通知,觸發(fā)方法和本地通知一致 (必須實(shí)現(xiàn))

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {// Required??

? [APService handleRemoteNotification:userInfo];

}

#pragma mark - 使用后臺(tái)的遠(yuǎn)程消息推送 (必須實(shí)現(xiàn))/**?

1> 在Capabilities中打開遠(yuǎn)程推送通知

?2> 實(shí)現(xiàn)該代理方法遠(yuǎn)程消息數(shù)據(jù)格式: {"aps" : {"content-available" : 1},"content-id" : 42} 執(zhí)行completionHandler有兩個(gè)目的

?1> 系統(tǒng)會(huì)估量App消耗的電量堂污,并根據(jù)傳遞的UIBackgroundFetchResult 參數(shù)記錄新數(shù)據(jù)是否可用?

2> 調(diào)用完成的處理代碼時(shí)家肯,應(yīng)用的界面縮略圖會(huì)自動(dòng)更新 注意:接收到遠(yuǎn)程通知到執(zhí)行完網(wǎng)絡(luò)請求之間的時(shí)間不能超過30秒?

if (userInfo) {

?int contentId = [userInfo[@"content-id"] intValue];?

ViewController *vc = (ViewController *)application.keyWindow.rootViewController;?

[vc loadDataWithContentID:contentId completion:^(NSArray *dataList) {?

vc.dataList = dataList; NSLog(@"刷新數(shù)據(jù)結(jié)束");?

completionHandler(UIBackgroundFetchResultNewData); }];?

} else {?

completionHandler(UIBackgroundFetchResultNoData);?

}*/

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler

?{// IOS 7 Support Required?

?? [APService handleRemoteNotification:userInfo];?

?? [[UIApplication sharedApplication]setApplicationIconBadgeNumber:0];

// 判斷應(yīng)用程序在前臺(tái)還是后臺(tái)

if (application.applicationState == UIApplicationStateActive)

?{// 活躍狀態(tài)// 實(shí)現(xiàn)方法? ?

?}

else if (application.applicationState == UIApplicationStateInactive)

?{// 不活躍狀態(tài)// 實(shí)現(xiàn)方法? ??

}else {

// application.applicationState == UIApplicationStateBackground

// 后臺(tái)// 實(shí)現(xiàn)方法??

? }

/** 必須回調(diào) */?

?? completionHandler(UIBackgroundFetchResultNewData);

}

測試時(shí),運(yùn)行完應(yīng)用程序,要發(fā)送通知,進(jìn)行推送的測試,真機(jī)收到通知才算成功.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市盟猖,隨后出現(xiàn)的幾起案子讨衣,更是在濱河造成了極大的恐慌,老刑警劉巖式镐,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件反镇,死亡現(xiàn)場離奇詭異,居然都是意外死亡碟案,警方通過查閱死者的電腦和手機(jī)愿险,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來价说,“玉大人辆亏,你說我怎么就攤上這事”钅浚” “怎么了扮叨?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長领迈。 經(jīng)常有香客問我彻磁,道長,這世上最難降的妖魔是什么狸捅? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任衷蜓,我火速辦了婚禮,結(jié)果婚禮上尘喝,老公的妹妹穿的比我還像新娘磁浇。我一直安慰自己,他們只是感情好朽褪,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布置吓。 她就那樣靜靜地躺著,像睡著了一般缔赠。 火紅的嫁衣襯著肌膚如雪衍锚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天嗤堰,我揣著相機(jī)與錄音戴质,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛置森,可吹牛的內(nèi)容都是我干的斗埂。 我是一名探鬼主播符糊,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼凫海,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了男娄?” 一聲冷哼從身側(cè)響起行贪,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎模闲,沒想到半個(gè)月后建瘫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡尸折,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年啰脚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片实夹。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡橄浓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出亮航,到底是詐尸還是另有隱情荸实,我是刑警寧澤,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布缴淋,位于F島的核電站准给,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏重抖。R本人自食惡果不足惜露氮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望钟沛。 院中可真熱鬧畔规,春花似錦、人聲如沸讹剔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽延欠。三九已至陌兑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間由捎,已是汗流浹背兔综。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人软驰。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓涧窒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親锭亏。 傳聞我的和親對象是個(gè)殘疾皇子纠吴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351