使用AWS的SNS(Simple Notification Server)實現(xiàn)終端設(shè)備間消息傳遞功能

使用場景:

在自有用戶體系中判沟,實現(xiàn)用戶A給用戶B發(fā)送消息耿芹,通知事務(wù)的處理結(jié)果。

舉個例子:添加好友挪哄。
用戶A發(fā)送好友請求給用戶B吧秕,用戶B收到請求后處理并將處理結(jié)果發(fā)送給用戶A。

需求分析

  1. 選擇一個消息推送平臺:
    國內(nèi)推送平臺:極光中燥、個推等寇甸,國外有GCM(Google Cloud Message)。
  2. 在自己的服務(wù)器上開發(fā)一個API疗涉。
    服務(wù)器接受用戶A的請求消息拿霉,并通過推送平臺將消息傳遞給用戶B。 同理咱扣,用戶B將處理結(jié)果發(fā)送給用戶A绽淘。

通過上面的分析,我們結(jié)合服務(wù)器實現(xiàn)了功能需求闹伪。

但是沪铭,如果沒有后臺開發(fā)人員怎么辦?偏瓤?杀怠?

文章標(biāo)題說的就是這個問題的解決方案。
AWS是亞馬遜的云開發(fā)平臺厅克,類似國內(nèi)的阿里云赔退。SNS則是其中的一項服務(wù),使應(yīng)用程序证舟、用戶硕旗、終端設(shè)備能夠通過云端即時發(fā)送或者接受消息。
我們不需要再開發(fā)一個API處理用戶之間的消息傳遞女责,因為SNS已經(jīng)全部做好了漆枚。我們需要做的便是選擇一個推送平臺、定義自己的消息格式抵知,調(diào)用相關(guān)的SDK方法即可墙基。

實現(xiàn)步驟(Android+GCM)

這里默認(rèn)你已經(jīng)有了AWS開發(fā)者賬號。

1. 創(chuàng)建平臺應(yīng)用程序刷喜,得到ARN残制。

  • 進入SNS控制面板(控制臺首頁搜索SNS即可找到),依次點擊“應(yīng)用程序”-->“創(chuàng)建平臺應(yīng)用程序”


    創(chuàng)建平臺應(yīng)用程序
  • 應(yīng)用程序名稱:填寫項目名稱即可吱肌。
  • 推送通知平臺:根據(jù)自己的需求進行選擇痘拆,這里我選擇GCM仰禽,通過谷歌推送來實現(xiàn)氮墨。
  • API密鑰:在相應(yīng)的推送平臺注冊應(yīng)用后得到纺蛆。
  • 獲取GCM平臺下API密鑰
    a. 建議通過Firebase集成GCM,集成完好后规揪,我們進入Google Cloud Platform桥氏,


    Google Cloud Platform

    b. 先選擇通過Firebase創(chuàng)建的項目,然后在左側(cè)菜單欄選擇“API和服務(wù)”-->“憑據(jù)”猛铅。
    c. 復(fù)制紅框內(nèi)名稱為“Server Key”的密鑰(Firebase默認(rèn)創(chuàng)建的3個密鑰字支,分別作用于Browser、Server奸忽、Android)堕伪。
    因為我們想讓AWS來處理消息傳遞,AWS屬于Server一類栗菜。
    將獲得的密鑰粘貼到“創(chuàng)建應(yīng)用平臺程序”窗口的“API密鑰”一欄欠雌,點擊右下角“創(chuàng)建平臺應(yīng)用程序”,稍后界面顯示如下:


    得到ARN

2. 將設(shè)備終端注冊到ARN(代碼實現(xiàn))疙筹,得到終端節(jié)點富俄。
實現(xiàn)原理:集成GCM后,會有一個服務(wù)專門獲取或更新token而咆,我們只需要將這個token注冊到ARN下即可霍比。

//注冊arn
private void sendRegistrationToServer(String token) {
        // Implement this method to send token to your app server.
        // 創(chuàng)建平臺終端節(jié)點和管理設(shè)備令牌
        client.setRegion(Region.getRegion(Regions.AP_NORTHEAST_1));
        //retrieveEndpointArn方法是將保存在sp中的endpointArn取出暴备,自己實現(xiàn)一下。
        String endpointArn = retrieveEndpointArn();
        boolean updateNeeded = false;
        boolean createNeeded = ("".equals(endpointArn));
        if (createNeeded) {
            // No platform endpoint ARN is stored; need to call createEndpoint.
            endpointArn = createEndpoint(token);
            createNeeded = false;
        }
        System.out.println("Retrieving platform endpoint data...");
        // Look up the platform endpoint and make sure the data in it is current, even if
        // it was just created.
        try {
            GetEndpointAttributesRequest geaReq =
                    new GetEndpointAttributesRequest()
                            .withEndpointArn(endpointArn);
            GetEndpointAttributesResult geaRes =
                    client.getEndpointAttributes(geaReq);
            updateNeeded = !geaRes.getAttributes().get("Token").equals(token)
                    || !geaRes.getAttributes().get("Enabled").equalsIgnoreCase("true");
        } catch (NotFoundException nfe) {
            // We had a stored ARN, but the platform endpoint associated with it
            // disappeared. Recreate it.
            createNeeded = true;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        if (createNeeded) {
            createEndpoint(token);
        }
        System.out.println("updateNeeded = " + updateNeeded);
        if (updateNeeded) {
            // The platform endpoint is out of sync with the current data;
            // update the token and enable it.
            System.out.println("Updating platform endpoint " + endpointArn);
            Map attribs = new HashMap();
            attribs.put("Token", token);
            attribs.put("Enabled", "true");
            SetEndpointAttributesRequest saeReq =
                    new SetEndpointAttributesRequest()
                            .withEndpointArn(endpointArn)
                            .withAttributes(attribs);
            client.setEndpointAttributes(saeReq);
        }
    }
    /**
     * @return never null
     */
    private String createEndpoint(String token) {
        String endpointArn = null;
        try {
            System.out.println("Creating platform endpoint with token " + token);
            //創(chuàng)建endpointArn
            String arn = "自己的arn";
            CreatePlatformEndpointRequest cpeReq =
                    new CreatePlatformEndpointRequest()
                            .withPlatformApplicationArn(arn)
                            .withToken(token);
            CreatePlatformEndpointResult cpeRes = client
                    .createPlatformEndpoint(cpeReq);
            endpointArn = cpeRes.getEndpointArn();
            //訂閱主題
//String topic = "自己的主題";
// SubscribeRequest request = new SubscribeRequest()
// .withTopicArn(topic)
// .withProtocol("application")
// .withEndpoint(endpointArn);
// SubscribeResult subscribeResult = client.subscribe(request);
// Log.i(TAG, "subscribeResult: " + subscribeResult.getSubscriptionArn());
        } catch (InvalidParameterException ipe) {
            String message = ipe.getErrorMessage();
            System.out.println("Exception message: " + message);
            Pattern p = Pattern.compile(".*Endpoint (arn:aws:sns[^ ]+) already exists with the same token.*");
            Matcher m = p.matcher(message);
            if (m.matches()) {
                // The platform endpoint already exists for this token, but with
                // additional custom data that
                // createEndpoint doesn't want to overwrite. Just use the
                // existing platform endpoint.
                endpointArn = m.group(1);
            } else {
                // Rethrow the exception, the input is actually bad.
                throw ipe;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        //將endpointArn保存在sp中阁危,自己實現(xiàn)一下。
        storeEndpointArn(endpointArn);
        return endpointArn;
    }

完成以上步驟后汰瘫,在程序啟動后狂打,便可以得到如下圖所示內(nèi)容。


終端節(jié)點
  • 令牌:GCM下發(fā)的token
  • 終端節(jié)點 ARN:SNS為每一個終端生成的唯一標(biāo)識趴乡。

3. 推送消息到某一個終端節(jié)點。
在SNS控制臺中晾捏,選擇一個終端節(jié)點,點擊“發(fā)布到終端節(jié)點”哀托,在彈出的界面中惦辛,輸入消息,點擊發(fā)送即可仓手。

4. 終端間互發(fā)消息(代碼實現(xiàn))胖齐。
經(jīng)過以上3個步驟玻淑,每個用戶啟動應(yīng)用程序后呀伙,都會注冊到SNS,并生成各自特有的終端節(jié)點剿另。
那么箫锤,終端之間發(fā)送消息就簡單了雨女。
用戶A向用戶B發(fā)送消息,只需要知道用戶B的終端節(jié)點號碼即可氛堕。

    /**
     * 給指定終端發(fā)送消息
     *
     * @param endpointArn 對方的終端節(jié)點
     * @param targetId 對方的id
     * @param gcmMsg 消息體
     * @return 發(fā)送成功后返回該條消息id
     */
    public static Observable<String> sendMsgToDevice(final String endpointArn, final String targetId, final GCMMsg gcmMsg) {
        return Observable.fromCallable(new Callable<String>() {
            @Override
            public String call() throws Exception {
                String mId = SpUtil.getString(Constants.USER_ID);
                //發(fā)消息
                AmazonSNSClient client = new AmazonSNSClient(CognitoClientManager.getCredentials());
                client.setRegion(Region.getRegion(Regions.AP_NORTHEAST_1));
                PublishRequest request = new PublishRequest();
                request.setTargetArn(endpointArn);
                request.setMessageStructure("json");
                String msg = "{\n" +
                        "\"GCM\": \"{ \\\"data\\\": { " +
                        "\\\"category\\\": \\\"" + gcmMsg.getCategory() + "\\\", " +
                        "\\\"title\\\": \\\"" + gcmMsg.getTitle() + "\\\", " +
                        "\\\"message\\\": \\\"" + gcmMsg.getMessage() + "\\\", " +
                        "\\\"url\\\": \\\"" + gcmMsg.getUrl() + "\\\", " +
                        "\\\"data\\\": \\\"" + gcmMsg.getData() + "\\\" } }\"\n" +
                        "}";
                request.setMessage(msg);
                PublishResult result = client.publish(request);
                Log.i("SNS", "messageId: " + result.getMessageId());
                return result.getMessageId();
            }
        });
    }

至此,終端間互發(fā)消息的需求就完成了位喂,全部借助AWS的SNS服務(wù),無需另外開發(fā)后臺API塑崖,自己只需要調(diào)用相關(guān)的SDK方法即可。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末规婆,一起剝皮案震驚了整個濱河市蝉稳,隨后出現(xiàn)的幾起案子抒蚜,更是在濱河造成了極大的恐慌耘戚,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件收津,死亡現(xiàn)場離奇詭異,居然都是意外死亡撞秋,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門串结,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人肌割,你說我怎么就攤上這事赵抢∩Γ” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵先巴,是天一觀的道長冒冬。 經(jīng)常有香客問我伸蚯,道長简烤,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任横侦,我火速辦了婚禮,結(jié)果婚禮上枉侧,老公的妹妹穿的比我還像新娘。我一直安慰自己榨馁,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布屑柔。 她就那樣靜靜地躺著珍剑,像睡著了一般掸宛。 火紅的嫁衣襯著肌膚如雪招拙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天迫像,我揣著相機與錄音,去河邊找鬼闻妓。 笑死,一個胖子當(dāng)著我的面吹牛由缆,可吹牛的內(nèi)容都是我干的猾蒂。 我是一名探鬼主播是晨,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼罩缴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起箫章,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎檬寂,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體桶至,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年亩码,在試婚紗的時候發(fā)現(xiàn)自己被綠了野瘦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片描沟。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡鞭光,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出惰许,到底是詐尸還是另有隱情,我是刑警寧澤汹买,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站晦毙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏见妒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一盐股、第九天 我趴在偏房一處隱蔽的房頂上張望钱豁。 院中可真熱鬧疯汁,春花似錦、人聲如沸幌蚊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽塑煎。三九已至沫换,卻和暖如春最铁,著一層夾襖步出監(jiān)牢的瞬間讯赏,已是汗流浹背冷尉。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留雀哨,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓膊夹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親放刨。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355

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