使用場景:
在自有用戶體系中判沟,實現(xiàn)用戶A給用戶B發(fā)送消息耿芹,通知事務(wù)的處理結(jié)果。
舉個例子:添加好友挪哄。
用戶A發(fā)送好友請求給用戶B吧秕,用戶B收到請求后處理并將處理結(jié)果發(fā)送給用戶A。
需求分析
- 選擇一個消息推送平臺:
國內(nèi)推送平臺:極光中燥、個推等寇甸,國外有GCM(Google Cloud Message)。 - 在自己的服務(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)容。
- 令牌: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方法即可。