【友盟推送】java使用友盟API推送消息

一、前言

      推送是一個很友好的功能,各種app基本都有主動推送消息的功能,小編最近在項目中也使用了主動推送的功能,借用了第三方——友盟。

      下面小編就向大家介紹一下友盟相關的推送API.

二、友盟

      在介紹api之前,先向大家介紹一下友盟。

      友盟是一個第三方的公司,主要提供了App 移動統計、Web 網站統計、Push 消息推送、Share 社會化分享、AppTrack 移動廣告監測、Finplus 金融行業數據解決方案。

      本次小編使用了友盟的Push 消息推送。

在這裏插入圖片描述

三、Push 消息推送

基本說明

      本文所描述的API接口均基於HTTP REST協議,若無特殊說明接口均使用UTF-8編碼,消息體參數以及返回結果均採用JSON格式。

      注意:使用API前需要在Web後臺的App應用信息頁面獲取 Appkey 和 App Master Secret,同時在Web後臺添加服務器IP地址做IP白名單安全驗證或關閉IP白名單。如下圖:

在這裏插入圖片描述

名稱解釋

      這裏需要了解一下推送先關的名詞:

  • Appkey:應用唯一標識。友盟消息推送服務提供的appkey和友盟統計分析平臺使用的同一套appkey。

  • App Master Secret:服務器祕鑰,用於服務器端調用API請求時對發送內容做簽名驗證。

  • Device Token:友盟消息推送服務對設備的唯一標識。Android的device_token是44位字符串,iOS的device_token是64位。

  • Alias:開發者自有賬號,開發者可以在SDK中調用setAlias(alias, alias_type)接口將alias+alias_type與device_token做綁定,之後開發者就可以根據自有業務邏輯篩選出alias進行消息推送。

  • 通知-Android(notification):消息送達到用戶設備後,由友盟SDK接管處理並在通知欄上1顯示通知內容。

  • 消息-Android(message):消息送達到用戶設備後,消息內容透傳給應用自身進行解析處理。

  • 通知-iOS:和APNs定義一致。

  • 靜默推送-iOS:和APNs定義一致。

  • 測試模式:在廣播、組播等大規模發送消息的情況下,爲了防止開發者誤將測試消息大面積發給線上用戶,特增加了測試模式。 測試模式下,只會將消息發送給測試設備。測試設備需要到Web後臺上手工添加。

  • 測試模式-Android:Android的測試設備是正式設備的一個子集

  • 測試模式-iOS: iOS的測試模式對應APNs的開發環境(sandbox), 正式模式對應APNs的生產環境(prod),測試設備和正式設備完全隔離。

  • 簽名: 爲了保證調用API的請求是合法者發送且參數沒有被篡改,需要在調用API時對發送的所有內容進行簽名。簽名附加在調用地址後面,簽名的計算方式參見附錄K。

  • 推送類型:單播(unicast)、列播(listcast)、自定義播(customizedcast且不帶file_id)統稱爲單播類消息,Web後臺不會展示此類消息詳細信息,僅展示前一天的彙總數據;廣播(broadcast)、文件播(filecast)、組播(groupcast)、自定義播(customizedcast且file_id不爲空)統稱爲任務類消息,任務類消息支持API查詢、撤銷操作,Web後臺會展示任務類消息詳細信息。

  • 單播(unicast):向指定的設備發送消息。

  • 列播(listcast):向指定的一批設備發送消息。

  • 廣播(broadcast):向安裝該App的所有設備發送消息。

  • 組播(groupcast)::向滿足特定條件的設備集合發送消息,例如: “特定版本”、”特定地域”等。

  • 文件播(filecast):開發者將批量的device_token或者alias存放到文件,通過文件ID進行消息發送。

  • 自定義播(customizedcast):開發者通過自有的alias進行推送,可以針對單個或者一批alias進行推送,也可以將alias存放到文件進行發送。

發送限制

      爲了提供更加穩定高效的推送環境,生產環境下有以下發送限制(測試環境無任何限制):

  • 廣播(broadcast)默認每天可推送10次

  • 組播(groupcast)默認每分鐘可推送5次

  • 文件播(filecast)默認每小時可推送300次

  • 自定義播(customizedcast, 且file_id不爲空)默認每小時可推送300次

  • 單播類消息暫無推送限制

入參

      android和ios的入參是不一樣的:

      Android:

{
    "appkey":"xx",        // 必填,應用唯一標識
    "timestamp":"xx",    // 必填,時間戳,10位或者13位均可,時間戳有效期爲10分鐘
    "type":"xx",        // 必填,消息發送類型,其值可以爲: 
                        //   unicast-單播
                        //   listcast-列播,要求不超過500個device_token
                        //   filecast-文件播,多個device_token可通過文件形式批量發送
                        //   broadcast-廣播
                        //   groupcast-組播,按照filter篩選用戶羣, 請參照filter參數
                        //   customizedcast,通過alias進行推送,包括以下兩種case:
                        //     - alias: 對單個或者多個alias進行推送
                        //     - file_id: 將alias存放到文件後,根據file_id來推送
    "device_tokens":"xx",    // 當type=unicast時, 必填, 表示指定的單個設備
                            // 當type=listcast時, 必填, 要求不超過500個, 以英文逗號分隔
    "alias_type": "xx",    // 當type=customizedcast時, 必填
                        // alias的類型, alias_type可由開發者自定義, 開發者在SDK中
                        // 調用setAlias(alias, alias_type)時所設置的alias_type
    "alias":"xx",        // 當type=customizedcast時, 選填(此參數和file_id二選一)
                        // 開發者填寫自己的alias, 要求不超過500個alias, 多個alias以英文逗號間隔
                        // 在SDK中調用setAlias(alias, alias_type)時所設置的alias
    "file_id":"xx",    // 當type=filecast時,必填,file內容爲多條device_token,以回車符分割
                    // 當type=customizedcast時,選填(此參數和alias二選一)
                    //   file內容爲多條alias,以回車符分隔。注意同一個文件內的alias所對應
                    //   的alias_type必須和接口參數alias_type一致。
                    // 使用文件播需要先調用文件上傳接口獲取file_id,參照"文件上傳"
    "filter":{},    // 當type=groupcast時,必填,用戶篩選條件,如用戶標籤、渠道等,參考附錄G。
    "payload": {    // 必填,JSON格式,具體消息內容(Android最大爲1840B)
        "display_type":"xx",    // 必填,消息類型: notification(通知)、message(消息)
        "body": {    // 必填,消息體。
                // 當display_type=message時,body的內容只需填寫custom字段。
                // 當display_type=notification時,body包含如下參數:
            // 通知展現內容:
            "ticker":"xx",    // 必填,通知欄提示文字
            "title":"xx",    // 必填,通知標題
            "text":"xx",    // 必填,通知文字描述 

            // 自定義通知圖標:
            "icon":"xx",    // 可選,狀態欄圖標ID,R.drawable.[smallIcon],
            // 如果沒有,默認使用應用圖標。
            // 圖片要求爲24*24dp的圖標,或24*24px放在drawable-mdpi下。
            // 注意四周各留1個dp的空白像素
            "largeIcon":"xx",    // 可選,通知欄拉開後左側圖標ID,R.drawable.[largeIcon],
            // 圖片要求爲64*64dp的圖標,
            // 可設計一張64*64px放在drawable-mdpi下,
            // 注意圖片四周留空,不至於顯示太擁擠
            "img": "xx",    // 可選,通知欄大圖標的URL鏈接。該字段的優先級大於largeIcon。
                            // 該字段要求以http或者https開頭。

            // 自定義通知聲音:
            "sound": "xx",    // 可選,通知聲音,R.raw.[sound]。
                            // 如果該字段爲空,採用SDK默認的聲音,即res/raw/下的
                            // umeng_push_notification_default_sound聲音文件。如果
                            // SDK默認聲音文件不存在,則使用系統默認Notification提示音。

            // 自定義通知樣式:
            "builder_id": xx,    // 可選,默認爲0,用於標識該通知採用的樣式。使用該參數時,
                                // 開發者必須在SDK裏面實現自定義通知欄樣式。

            // 通知到達設備後的提醒方式,注意,"true/false"爲字符串
            "play_vibrate":"true/false",    // 可選,收到通知是否震動,默認爲"true"
            "play_lights":"true/false",        // 可選,收到通知是否閃燈,默認爲"true"
            "play_sound":"true/false",        // 可選,收到通知是否發出聲音,默認爲"true"

            // 點擊"通知"的後續行爲,默認爲打開app。
            "after_open": "xx",    // 可選,默認爲"go_app",值可以爲:
                                //   "go_app": 打開應用
                                //   "go_url": 跳轉到URL
                                //   "go_activity": 打開特定的activity
                                //   "go_custom": 用戶自定義內容。
            "url": "xx",    // 當after_open=go_url時,必填。
                            // 通知欄點擊後跳轉的URL,要求以http或者https開頭
            "activity":"xx",    // 當after_open=go_activity時,必填。
                                // 通知欄點擊後打開的Activity
            "custom":"xx"/{}    // 當display_type=message時, 必填
                                // 當display_type=notification且
                                // after_open=go_custom時,必填
                                // 用戶自定義內容,可以爲字符串或者JSON格式。
        },
        extra:{    // 可選,JSON格式,用戶自定義key-value。只對"通知"
                // (display_type=notification)生效。
                // 可以配合通知到達後,打開App/URL/Activity使用。
            "key1": "value1",
            "key2": "value2",
            ...
        }
    },
    "policy":{    // 可選,發送策略
        "start_time":"xx",    // 可選,定時發送時,若不填寫表示立即發送。
                            // 定時發送時間不能小於當前時間
                            // 格式: "yyyy-MM-dd HH:mm:ss"。 
                            // 注意,start_time只對任務類消息生效。
        "expire_time":"xx",    // 可選,消息過期時間,其值不可小於發送時間或者
                            // start_time(如果填寫了的話),
                            // 如果不填寫此參數,默認爲3天后過期。格式同start_time
        "max_send_num": xx,    // 可選,發送限速,每秒發送的最大條數。最小值1000
                            // 開發者發送的消息如果有請求自己服務器的資源,可以考慮此參數。
        "out_biz_no": "xx"    // 可選,開發者對消息的唯一標識,服務器會根據這個標識避免重複發送。
                            // 有些情況下(例如網絡異常)開發者可能會重複調用API導致
                            // 消息多次下發到客戶端。如果需要處理這種情況,可以考慮此參數。
                            // 注意, out_biz_no只對任務類消息生效。
    },
    "production_mode":"true/false",    // 可選,正式/測試模式。默認爲true
                                    // 測試模式只會將消息發給測試設備。測試設備需要到web上添加。
                                    // Android: 測試設備屬於正式設備的一個子集。
    "description": "xx",    // 可選,發送消息描述,建議填寫。  
    //系統彈窗,只有display_type=notification生效
    "mipush": "true/false",    // 可選,默認爲false。當爲true時,表示MIUI、EMUI、Flyme系統設備離線轉爲系統下發
    "mi_activity": "xx",    // 可選,mipush值爲true時生效,表示走系統通道時打開指定頁面acitivity的完整包路徑。
}

      ios:

{
  "appkey":"xx",    // 必填,應用唯一標識
  "timestamp":"xx", // 必填,時間戳,10位或者13位均可,時間戳有效期爲10分鐘
  "type":"xx",      // 必填,消息發送類型,其值可以爲: 
                    //   unicast-單播
                    //   listcast-列播,要求不超過500個device_token
                    //   filecast-文件播,多個device_token可通過文件形式批量發送
                    //   broadcast-廣播
                    //   groupcast-組播,按照filter篩選用戶羣, 請參照filter參數
                    //   customizedcast,通過alias進行推送,包括以下兩種case:
                    //     - alias: 對單個或者多個alias進行推送
                    //     - file_id: 將alias存放到文件後,根據file_id來推送
  "device_tokens":"xx", // 當type=unicast時, 必填, 表示指定的單個設備
                        // 當type=listcast時, 必填, 要求不超過500個, 以英文逗號分隔
  "alias_type": "xx", // 當type=customizedcast時, 必填
                      // alias的類型, alias_type可由開發者自定義, 開發者在SDK中
                      // 調用setAlias(alias, alias_type)時所設置的alias_type
  "alias":"xx", // 當type=customizedcast時, 選填(此參數和file_id二選一)
                // 開發者填寫自己的alias, 要求不超過500個alias, 多個alias以英文逗號間隔
                // 在SDK中調用setAlias(alias, alias_type)時所設置的alias
  "file_id":"xx", // 當type=filecast時,必填,file內容爲多條device_token,以回車符分割
                  // 當type=customizedcast時,選填(此參數和alias二選一)
                  //   file內容爲多條alias,以回車符分隔。注意同一個文件內的alias所對應
                  //   的alias_type必須和接口參數alias_type一致。
                  // 使用文件播需要先調用文件上傳接口獲取file_id,參照"2.4文件上傳接口"
  "filter":{}, // 當type=groupcast時,必填,用戶篩選條件,如用戶標籤、渠道等,參考附錄G。
  "payload":   // 必填,JSON格式,具體消息內容(iOS最大爲2012B)
  {
    "aps":      // 必填,嚴格按照APNs定義來填寫
    {
        "alert":""/{ // 當content-available=1時(靜默推送),可選; 否則必填。
                     // 可爲JSON類型和字符串類型
            "title":"title",
            "subtitle":"subtitle",
            "body":"body"
        }                   
        "badge": xx,           // 可選        
        "sound": "xx",         // 可選         
        "content-available":1  // 可選,代表靜默推送     
        "category": "xx",      // 可選,注意: ios8才支持該字段。
    },
    "key1":"value1",       // 可選,用戶自定義內容, "d","p"爲友盟保留字段,
                           // key不可以是"d","p"
    "key2":"value2",       
    ...
  },
  "policy":               // 可選,發送策略
  {
      "start_time":"xx",   // 可選,定時發送時間,若不填寫表示立即發送。
                           // 定時發送時間不能小於當前時間
                           // 格式: "yyyy-MM-dd HH:mm:ss"。 
                           // 注意,start_time只對任務生效。
      "expire_time":"xx",  // 可選,消息過期時間,其值不可小於發送時間或者
                           // start_time(如果填寫了的話), 
                           // 如果不填寫此參數,默認爲3天后過期。格式同start_time
      "out_biz_no": "xx"   // 可選,開發者對消息的唯一標識,服務器會根據這個標識避免重複發送。
                           // 有些情況下(例如網絡異常)開發者可能會重複調用API導致
                           // 消息多次下發到客戶端。如果需要處理這種情況,可以考慮此參數。
                           // 注意,out_biz_no只對任務生效。
      "apns_collapse_id": "xx" // 可選,多條帶有相同apns_collapse_id的消息,iOS設備僅展示
                               // 最新的一條,字段長度不得超過64bytes
  },
  "production_mode":"true/false" // 可選,正式/測試模式。默認爲true
                                 // 測試模式只會將消息發給測試設備。測試設備需要到web上添加。
  "description": "xx"      // 可選,發送消息描述,建議填寫。     
}

出參

{
    "ret":"SUCCESS/FAIL",
    "data": {
        // 當"ret"爲"SUCCESS"時,包含如下參數:
        // 單播類消息(type爲unicast、listcast、customizedcast且不帶file_id)返回:
        "msg_id":"xx"

        // 任務類消息(type爲broadcast、groupcast、filecast、customizedcast且file_id不爲空)返回:
        "task_id":"xx"

        // 當"ret"爲"FAIL"時,包含如下參數:
        "error_code":"xx",    // 錯誤碼,詳見附錄I
        "error_msg":"xx"    // 錯誤信息
    }
}

單播Ucast推送

      pushUnicastByUserInfo:

 /**
     * 根據用戶信息推送單播類消息-王雷-2018年12月21日14:34:523
     *
     * @param unicastPushNotificationBO 單播推送BO
     * @return 推送結果
     */
    @Override
    public BusiSystemResponse<UnicastPushNotificationBO> pushUnicastByUserInfo(
            UnicastPushNotificationBO unicastPushNotificationBO) {
        BusiSystemResponse<UnicastPushNotificationBO> ret = new BusiSystemResponse<>();
        if (unicastPushNotificationBO == null) {
            DushuLogger.info("參數校驗失敗,接收到空對象unicastPushNotificationBO[null]");
            ret.setStatus(CommonResponseCodeEnum.PARAM_ERROR.getCode());
            ret.setData(null);
            return ret;
        }
        if (StringUtils.isBlank(unicastPushNotificationBO.getUserId())
                || StringUtils.isBlank(unicastPushNotificationBO.getTicker())
                || StringUtils.isBlank(unicastPushNotificationBO.getTitle())
                || StringUtils.isBlank(unicastPushNotificationBO.getText())) {
            DushuLogger.info("參數校驗失敗,unicastPushNotificationBO[{}]", unicastPushNotificationBO.toString());
            ret.setStatus(CommonResponseCodeEnum.REQUIRED_PARAMETER_MISSING.getCode());
            ret.setData(null);
            return ret;
        }

        List<String> userIdList = new ArrayList<>();
        userIdList.add(unicastPushNotificationBO.getUserId());

        try {
            // 根據userid獲取devicetoken
            String listDeviceToken = this.listDeviceToken(userIdList);
            if (StringUtils.isBlank(listDeviceToken)) {
                ret.setStatus(CommonResponseCodeEnum.DATA_NOT_EXIST.getCode());
                ret.setData(null);
                return ret;
            }
            unicastPushNotificationBO.setDeviceToken(listDeviceToken);
        } catch (Exception e) {
            DushuLogger.error("根據用戶id獲取token信息失敗", e);
            ret.setStatus(CommonResponseCodeEnum.ERROR.getCode());
            ret.setData(null);
            return ret;
        }

        try {
            // 根據設備token推送單播類消息
            String deviceToken = unicastPushNotificationBO.getDeviceToken();
            String ticker = unicastPushNotificationBO.getTicker();
            String title = unicastPushNotificationBO.getTitle();
            String text = unicastPushNotificationBO.getText();
            String deviceType = StringUtil.getDeviceTypeNameByDeviceToken(deviceToken);
            Map<String, String> map = unicastPushNotificationBO.getJumpMap();
            if (StringUtils.equalsIgnoreCase(PushConstant.ANDROID_DEVICE_TYPE, deviceType)) {
                RpcManager.getDushuThreadPoolExecutor().execute(new ThreadTask("sendAndroidUnicastPushMessageAsync") {
                    @Override
                    public void execute() {
                        pushUnicastAndroid(unicastPushNotificationBO);
                    }
                });
            }

            if (StringUtils.equalsIgnoreCase(PushConstant.IOS_DEVICE_TYPE, deviceType)) {
                RpcManager.getDushuThreadPoolExecutor().execute(new ThreadTask("sendIOSUnicastPushMessageAsync") {

                    @Override
                    public void execute() {
                        pushUnicastIos(unicastPushNotificationBO);
                    }
                });
            }
            ret.setStatus(CommonResponseCodeEnum.SUCCESS.getCode());

        } catch (Exception e) {
            DushuLogger.error("根據用戶信息推送單播類消息失敗", e);
            ret.setStatus(CommonResponseCodeEnum.ERROR.getCode());
            ret.setData(null);
            return ret;
        }
        return ret;
    }

      pushUnicastAndroid:

 /**
     * 推送安卓單播消息 - 王雷   -2019-3-20 09:29:43
     * @param unicastPushNotificationBO
     */
    private void pushUnicastAndroid(UnicastPushNotificationBO unicastPushNotificationBO) {
        String deviceToken = unicastPushNotificationBO.getDeviceToken();
        String ticker = unicastPushNotificationBO.getTicker();
        String title = unicastPushNotificationBO.getTitle();
        String text = unicastPushNotificationBO.getText();
        Map<String, String> map = unicastPushNotificationBO.getJumpMap();

        try {
            boolean productionMode = unicastPushNotificationBO.isProductionMode();
            AndroidUnicast unicast = new AndroidUnicast(PushConstant.ANDROID_APP_KEY,
                    PushConstant.ANDROID_APP_MASTER_SECRET);
            unicast.setDeviceToken(deviceToken);
            unicast.setTicker(ticker);
            unicast.setTitle(title);
            unicast.setText(text);
            if (!CollectionUtil.isNullOrEmpty(map)) {
                unicast.setExtraField("extra", JsonUtil.toJSON(map));
                unicast.goCustomAfterOpen(JsonUtil.toJSON(map));
            } else {
                unicast.goAppAfterOpen();
            }
            unicast.setMiPush(true);
            unicast.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
            if (productionMode) {
                unicast.setProductionMode();
            } else {
                unicast.setTestMode();
            }
            String postUrl = SignUtils.mergeSignUrl(PushConstant.SEND,
                    SignUtils.sign(unicast, PushConstant.SEND));
            sendUnicastPush(postUrl, unicast.getPostBody());
        } catch (Exception e) {
            DushuLogger.info("推送安卓單播消息異常!unicast:{}", unicastPushNotificationBO.toString());
        }
    }

      pushUnicastIos:

/**
     * 推送IOS單播消息 - 王雷 -2019年3月20日09:30:00
     * @param unicastPushNotificationBO
     */
    private void pushUnicastIos(UnicastPushNotificationBO unicastPushNotificationBO) {
        String deviceToken = unicastPushNotificationBO.getDeviceToken();
        String ticker = unicastPushNotificationBO.getTicker();
        String title = unicastPushNotificationBO.getTitle();
        String text = unicastPushNotificationBO.getText();
        Map<String, String> map = unicastPushNotificationBO.getJumpMap();
        try {
            boolean productionMode = unicastPushNotificationBO.isProductionMode();
            IOSUnicast unicast = new IOSUnicast(PushConstant.IOS_APP_KEY,
                    PushConstant.IOS_APP_MASTER_SECRET);
            unicast.setDeviceToken(deviceToken);
            Map<String, String> mapAlert = new HashMap<>();
            mapAlert.put("title", title);
//            mapAlert.put("subtitle", ticker);
            mapAlert.put("body", text);
            unicast.setAlert(JsonUtil.toJSON(mapAlert));
            unicast.setBadge(0);
            unicast.setSound("default");
            if (!CollectionUtil.isNullOrEmpty(map)) {
                for (String key : map.keySet()) {
                    unicast.setCustomizedField(key, map.get(key));
                }
            }
            if (productionMode) {
                unicast.setProductionMode();
            } else {
                unicast.setTestMode();
            }
            String postUrl = SignUtils.mergeSignUrl(PushConstant.SEND,
                    SignUtils.sign(unicast, PushConstant.SEND));
            sendUnicastPush(postUrl, unicast.getPostBody());
        } catch (Exception e) {
            DushuLogger.error("推送IOS單播消息異常!unicast:{}", unicastPushNotificationBO.toString());
        }
    }

      這裏用到了第三方的API,已經封裝好了,詳情見github地址:

      友盟Push 消息推送第三方API

      具體的例子就列一個吧,其他的也大同小異。

四、小結

      使用友盟,感覺推送的成功率也是分手機的,有的手機推送的成功率比較高,比如華爲,有點久稍微差一點,比如vivo。還有就是要支持推送消息,點擊後,跳轉網頁,可以使用自定義的模式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章