Android集成阿里雲消息推送的方法步驟

一 創建App應用

1.1 在控制檯發(https://mhub.console.aliyun.com)的App列表頁,點擊頁面產品列表中“添加產品”的圖標即可創建一個新的產品(產品是一個集合的概念,產品下包含iOS應用、Android應用)。



然後點擊剛創建的產品,點擊“添加應用”的圖標即可添加Android或者iOS應用(目前只能創建分端應用了,個人感覺還是不分端的好)。





1.2 輸入APP基本信息並在控制檯配置應用

輸入產品的基本信息創建App時需要輸入產品的名稱,上傳產品圖標,選擇產品分類。
其中App的名稱必填,支持中文、英文字母、數字和下劃線,長度限制在4-30位。

1.創建Android應用,並填寫APP名稱和PackageName。


2. 創建iOS應用,並填寫APP名稱和BundleId





二 Android SDK 3.0配置

以下集成是官方Android SDK的快速集成適用於V3.0.0以上版本。通過Maven庫快速集成,該集成方案配置簡單,不容易出問題。 (詳情參考官方集成文檔

2.1 快速集成(遠程同步)

在Project根目錄下build.gradle文件中配置maven庫URL:

    allprojects {
        repositories {
            jcenter()
            maven {
                url 'http://maven.aliyun.com/nexus/content/repositories/releases/'
            }
        }
    }

在對應的module下的build.gradle文件中添加對應依賴:

android {
        ......
        defaultConfig {
            applicationId "com.xxx.xxx" //包名
            ......
            ndk {
                //選擇要添加的對應cpu類型的.so庫。爲了兼容cpu,查看官網SDK的libs多添加幾個
                abiFilters  'arm64-v8a', 'armeabi', 'armeabi-v7a', 'mips', 'mips64', 'x86', 'x86_64'
            }
            ......
        }
        ......
    }
    dependencies {
        ......
        compile 'com.aliyun.ams:alicloud-android-push:3.1.2'
        ......
}

注 : 如果在添加以上 abiFilter 配置之後android Studio出現以下提示:(一般不會出錯)

NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin.

則在 Project 根目錄的gradle.properties文件中添加:

android.useDeprecatedNdk=true

2.2 AndroidManifest配置

   2.2.1 appKey, appSecret配置

在AndroidManifest文件中設置appKey,appSecret:

<application android:name="*****">
 <!-- 請填寫你自己的- appKey -->
     <meta-data android:name="com.alibaba.app.appkey" android:value="*****"/> 
 <!-- 請填寫你自己的appSecret -->
     <meta-data android:name="com.alibaba.app.appsecret" android:value="****"/> 
 </application>

(百川的appkey&appSecret不能在阿里雲推送上使用否則應用會找不到對應的appKey)

  2.2.2 消息接收Receiver配置

創建消息接收Receiver,繼承自com.alibaba.sdk.android.push.MessageReceiver,並在對應回調中添加業務處理邏輯,可參考以下代碼:

    public class MyMessageReceiver extends MessageReceiver {
        // 消息接收部分的LOG_TAG
        public static final String REC_TAG = "receiver";
        @Override
        public void onNotification(Context context, String title, String summary, Map<String, String> extraMap) {
            // TODO 處理推送通知
            Log.e("MyMessageReceiver", "Receive notification, title: " + title + ", summary: " + summary + ", extraMap: " + extraMap);
        }
        @Override
        public void onMessage(Context context, CPushMessage cPushMessage) {
                Log.e("MyMessageReceiver", "onMessage, messageId: " + cPushMessage.getMessageId() + ", title: " + cPushMessage.getTitle() + ", content:" + cPushMessage.getContent());
        }
        @Override
        public void onNotificationOpened(Context context, String title, String summary, String extraMap) {
            Log.e("MyMessageReceiver", "onNotificationOpened, title: " + title + ", summary: " + summary + ", extraMap:" + extraMap);
        }
        @Override
        protected void onNotificationClickedWithNoAction(Context context, String title, String summary, String extraMap) {
            Log.e("MyMessageReceiver", "onNotificationClickedWithNoAction, title: " + title + ", summary: " + summary + ", extraMap:" + extraMap);
        }
        @Override
        protected void onNotificationReceivedInApp(Context context, String title, String summary, Map<String, String> extraMap, int openType, String openActivity, String openUrl) {
            Log.e("MyMessageReceiver", "onNotificationReceivedInApp, title: " + title + ", summary: " + summary + ", extraMap:" + extraMap + ", openType:" + openType + ", openActivity:" + openActivity + ", openUrl:" + openUrl);
        }
        @Override
        protected void onNotificationRemoved(Context context, String messageId) {
            Log.e("MyMessageReceiver", "onNotificationRemoved");
        }
    }

將該receiver添加到AndroidManifest.xml中(由於部分手機不允許監聽系統的廣播,把部分過濾器去掉即可。下面示例是已經去掉)

<!-- 消息接收監聽器 (用戶可自主擴展) -->
<receiver
	android:name=".MyMessageReceiver"
	android:exported="false"> <!-- 爲保證receiver安全,建議設置不可導出,如需對其他應用開放可通過android:permission進行限制 -->
	<intent-filter>
		<action android:name="com.alibaba.push2.action.NOTIFICATION_OPENED" />
	</intent-filter>
	<intent-filter>
		<action android:name="com.alibaba.push2.action.NOTIFICATION_REMOVED" />
	</intent-filter>
	<intent-filter>
		<action android:name="com.alibaba.sdk.android.push.RECEIVE" />
	</intent-filter>
</receiver>
在Manifest中還需要添加以下配置

<!-- V3.0.12及以上版本需配置 -->
        <service
            android:name="com.taobao.accs.internal.AccsJobService"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:process=":channel"/>
        <!-- V3.0.7及以上版本需配置 -->
        <service android:name="com.alibaba.sdk.android.push.channel.KeepChannelService"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:process=":channel" />
        <receiver android:name="com.alibaba.sdk.android.push.SystemEventReceiver"
            android:process=":channel">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_MOUNTED"/>
                <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
                <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
            </intent-filter>
        </receiver>
        <!-- V3.0.9及以上版本需配置 -->
        <activity
            android:name="com.alibaba.sdk.android.push.keeplive.PushExtActivity"
            android:configChanges="keyboardHidden|orientation|screenSize|navigation|keyboard"
            android:excludeFromRecents="true"
            android:exported="false"
            android:finishOnTaskLaunch="false"
            android:launchMode="singleInstance"
            android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"
            android:process=":channel"
            >
        </activity>

  2.2.3 Proguard配置

-keepclasseswithmembernames class ** {
    native <methods>;
}
-keepattributes Signature
-keep class sun.misc.Unsafe { *; }
-keep class com.taobao.** {*;}
-keep class com.alibaba.** {*;}
-keep class com.alipay.** {*;}
-keep class com.ut.** {*;}
-keep class com.ta.** {*;}
-keep class anet.**{*;}
-keep class anetwork.**{*;}
-keep class org.android.spdy.**{*;}
-keep class org.android.agoo.**{*;}
-keep class android.os.**{*;}
-dontwarn com.taobao.**
-dontwarn com.alibaba.**
-dontwarn com.alipay.**
-dontwarn anet.**
-dontwarn org.android.spdy.**
-dontwarn org.android.agoo.**
-dontwarn anetwork.**
-dontwarn com.ut.**
-dontwarn com.ta.**

  2.2.4 在應用中註冊和啓動推送

      以下是應用初始化的官方參考代碼:

import android.app.Application;
import android.content.Context;
import android.util.Log;
import com.alibaba.sdk.android.callback.InitResultCallback;
import com.alibaba.sdk.android.push.CloudPushService;
import com.alibaba.sdk.android.push.CommonCallback;
import com.alibaba.sdk.android.push.noonesdk.PushServiceFactory;
public class MainApplication extends Application {
    private static final String TAG = "Init";
    @Override
    public void onCreate() {
        super.onCreate();
        initCloudChannel(this);
    }
    /**
     * 初始化雲推送通道
     * @param applicationContext
     */
    private void initCloudChannel(Context applicationContext) {
        PushServiceFactory.init(applicationContext);
        CloudPushService pushService = PushServiceFactory.getCloudPushService();
        pushService.register(applicationContext, new CommonCallback() {
            @Override
            public void onSuccess(String response) {
                Log.d(TAG, "init cloudchannel success");
            }
            @Override
            public void onFailed(String errorCode, String errorMessage) {
                Log.d(TAG, "init cloudchannel failed -- errorcode:" + errorCode + " -- errorMessage:" + errorMessage);
            }
        });
    }
}

初始化成功後Log輸出在過濾添加正則MPS顯示如下:

這裏寫圖片描述

三 移動推送輔助通道配置

移動推送的輔助通道的配置主要針對小米和華爲設備(目前只能接收通知,不能直接接收消息)對其他設備也有一定的保活效果。官網有輔助通道相關概念

3.1 配置應用

註冊你的App, 得到相應的小米AppID,小米AppKey,小米AppSecert。在控制檯註冊你的App, 得到相應的小米AppID,小米AppKey,小米AppSecert。(小米開放平臺

註冊App,應用審覈通過後,能夠得到華爲的AppID和AppSecert。在控制檯App詳情中設置你的華爲AppID和AppSecert。(注意,您的app不能是草稿狀態,必須是審覈中,或者通過審覈的狀態,不然通道不會生效。請確保您在華爲控制檯激活了推送通道功能)。(華爲開發者聯盟

注(在每個開發者中心申請,小米的最好聯繫下客服會快點,小米創建應用很快,就是開發者審覈慢點;華爲的開發這審覈快,創建應用填寫基本信息也是要點時間的。)

如果你的應用是國際版的可以在FCM平臺上添加項目,以提高推送的到達率,接入前提手機必須安裝google play services, 否則註冊不成功, 大部分國內的手機是谷歌服務被剝離了:(FCM平臺需要翻牆,如果不需要國際化,則不必配置

在以上平臺申請成功後會看到:上面是小米的,下面是華爲的。

這裏寫圖片描述
這裏寫圖片描述

然後就可以在阿里雲控制檯的應用列表填寫擴展信息了


3.2 下載擴展包

將小米華爲擴展包拷貝到你項目的Lib目錄下,下載地址

如果碰到arr文件擴展包無法添加的情況,建議解壓aar將其中的jar引入,manifest中的配置複製到你當前項目即可。

將華爲小米擴展包放置到app module的libs路徑下,並在app module的build.gradle文件中添加如下配置:

repositories {
    flatDir {
        dirs 'libs' //this way we can find the .aar file in libs folder
    }
}
...
dependencies {
    ......
    compile(name: 'third-push-support-3.0.5', ext: 'aar')
}

如需配置GCM/FCM通道還需要添加Firebase SDK依賴:

    dependencies {
        ......
        compile ('com.google.firebase:firebase-messaging:9.6.1')
    }

3.3 Proguard配置

集成推送SDK的工程開啓代碼混淆,在Proguard配置的基礎上,需要添加以下輔助通道的Proguard配置。

    # 小米通道
    -keep class com.xiaomi.** {*;}
    -dontwarn com.xiaomi.**
    # 華爲通道
    -keep class com.huawei.** {*;}
    -dontwarn com.huawei.**
    # GCM/FCM通道
    -keep class com.google.firebase.**{*;}
    - dontwarn com.google.firebase.**

3.4 在應用中初始化小米華爲初始化通道

將以下代碼加入你application.onCreate()方法中初始通道。注意:輔助通道註冊需要放在推送SDK初始化代碼之後。

    // 註冊方法會自動判斷是否支持小米系統推送,如不支持會跳過註冊。
    MiPushRegister.register(applicationContext, "小米AppID", "小米AppKey");
    // 註冊方法會自動判斷是否支持華爲系統推送,如不支持會跳過註冊。
    HuaWeiRegister.register(applicationContext);
    //GCM/FCM輔助通道註冊
    GcmRegister.register(this, sendId, applicationId); //sendId/applicationId爲步驟獲得的參數

註冊方法會自動判斷是否支持小米/華爲系統推送,如不支持會跳過註冊。

3.5 在日誌中查看初始化情況

華爲通道初始化成功,可以看到以下日誌:

    11-11 22:21:33.671  30248-30324/com.xxx E/MPS:HuaWeiRegister: HuaWeiRegister checkDevice flag=true //確認是華爲的手機
    11-11 22:21:33.674  30248-30324/com.xxx E/MPS:HuaWeiRegister﹕ Register huawei push............      //開始註冊華爲手機
    11-11 22:21:33.714  29643-30328/com.xxx E/MPS:HuaWeiRegister﹕ huawei register success,token = 08657430243125472000000411000001
    11-11 22:21:33.714  29643-30328/com.xxx E/MPS:HuaWeiRegister﹕ report huaweiPushId intent...         //完成華爲註冊和信息上報

小米通道初始化成功,可以看到以下日誌:

12-09 22:20:39.710 19566-19566/com.xxx E/MPS:MiPushRegister: MiPushRegister checkDevice flag=true //確認是小米的手機
    12-09 22:20:39.712 19566-19566/com.xxx E/MPS:MiPushRegister: Register mipush.                     //開始註冊小米
    12-09 22:20:40.596 19566-19733/com.xxx E/MPS:MiPushRegister: XiaoMi register success.             //小米註冊成功 regid=d//igwEhgBGCI2TG6lWqlCesc0I6xE1wUhNCBXQ8uNOi/dDZioYXVysbrVrvRmyEVPn9nWz92D28IzYbA1RzoGDyTzYZwXKfBHEQkrey4G8=

GCM/FCM通道初始化成功,可以看到以下日誌:

05-19 19:18:44.530 19153-19177/com.xxx D/MPS:GcmRegister: token from register: eWIXLYCNP0Q:APA91bFUAgxj6XYf5okyoCBnRPw1UwITndzXrvPDgbdI2N44PYm17hFEBiNXNQJrJ8bOG_xjw3c3UPDAhzNMTLNjlAKcjUanKyLA6E3k4wEmgZuhgUT02UMmMvH2LVA1L2Z4-l-cT_Ug

初始化成功後,由於小米/華爲系統管控比較嚴格,所以應用被殺死後仍不能接收通知,需要配置配置專門的輔助彈窗。

3.6 系統輔助彈窗配置

    輔助官網已經廢除了MiPushSystemNotificationActivity(不過不影響使用),建議使用繼承AndroidPopupActivity指定打開的託管的彈窗Activity在AndroidManifest.xml中註冊需要聲明android:exported=true,還有如果在PopupActivity的onSysNoticeOpened方法中沒有指定跳轉界面,或者在onCreate中沒有設置佈局文件,點擊輔助彈窗通知後只會顯示一個空白界面。

   輔助彈窗移動端配置:
import com.alibaba.sdk.android.push.AndroidPopupActivity;
    public class PopupPushActivity extends AndroidPopupActivity{
        static final String TAG = "PopupPushActivity";
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(activity_popuppush);
        }
        /**
         * 實現通知打開回調方法,獲取通知相關信息
         * @param title     標題
         * @param summary   內容
         * @param extMap    額外參數
         */
        @Override
        protected void onSysNoticeOpened(String title, String summary, Map<String, String> extMap) {
            Log.d("onSysNoticeOpened, title: " + title + ", content: " + summary + ", extMap: " + extMap);
        }
    }

通知的指定打開界面和在指定界面獲取推送的相關信息可以參考以下兩個鏈接:

推送通知打開指定Activity時如何操作

推送通知打開指定Activity時如何獲取推送通知參數



四 Android SDK服務器配置 (當前推送對象是Android,iOS的證書配置完好的話選擇全推也是可以的)

官網服務器移動推送Demo需要添加推送的SDK依賴(本示例代碼是Java版本,如需要其他版本參看官網其他示例

  • SDK 地址
  • Demo 地址
    首先下載推送的push-openapi-java-demo,由於官網是使用IDEA開發的Demo,所以如果你使用Eclipse或者MyEclipse要以Maven的方式導入項目。

    4.1 獲取AccessKeyId和AccessKeySecret
    前往阿里雲官網控制檯獲取
    4.2 獲取appKey
    >>前往移動推動控制檯獲取 app列表->應用證書
    在開發工具中打開push-openapi-java-demo中的src/test/resources/push.properties.template文件修改配置文件名稱爲push.properties
    以下是該配置文件說明:

這裏寫圖片描述

       4.3 引入SDK依賴
在下載的SDK中找到aliyun-java-sdk-push,

如果使用IDEA開發的分別將aliyun-java-sdk-pushaliyun-java-sdk-corepush-openapi-java-demo並且給push-openapi-java-demo添加aliyun-java-sdk-push,aliyun-java-sdk-core依賴。
如果使用Eclipse(或者MyEclipse)開發,以maven項目導入aliyun-java-sdk-push,aliyun-java-sdk-corepush-openapi-java-demo然後再給push-openapi-java-demo添加aliyun-java-sdk-push,aliyun-java-sdk-core依賴。

官網上是直接使用maven添加的(但有的時候可能同步依賴不完整)

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-push</artifactId>
    <version>3.0.2</version>
</dependency>

添加依賴成功後IDEA的文件目錄如下:

這裏寫圖片描述

添加依賴成功後MyEclipse的文件目錄如下:

這裏寫圖片描述

親測都能夠實現推送測試。

 五 服務端測試給Android發送通知

如果想自己拼接url進行推送,不使用Demo可以參考以下示例代碼:將對應的access_key_id,access_key_secret,appkey換成自己在控制檯申請的即可執行main方法。

import sun.misc.BASE64Encoder;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.SignatureException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 移動推送OpenAPI調用示例
 * 以PushNoticeToAndroid接口爲例,其他接口請替換相應接口名稱及私有參數
 */
public class Main {
    //賬號AK信息請填寫(必選)
    private static String access_key_id = "";
    //賬號AK信息請填寫(必選)
    private static String access_key_secret = "";
    //賬號AppKey信息請填寫(必選)
    private static String appkey = "";
    //以下參數不需要修改
    //STS臨時授權方式訪問時該參數爲必選,使用主賬號AK和RAM子賬號AK不需要填寫
    private static String security_token = "";
    private final static String VOD_DOMAIN = "http://cloudpush.aliyuncs.com";
    private final static String ISO8601_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
    private final static String HTTP_METHOD_GET = "GET";
    private final static String HMAC_SHA1_ALGORITHM = "HmacSHA1";
    private final static String UTF_8 = "utf-8";
    private final static Logger LOG = Logger.getLogger(Main.class.getName());

    /**
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        //生成私有參數,不同API需要修改
        Map<String, String> privateParams = generatePrivateParamters();
        //生成公共參數,不需要修改
        Map<String, String> publicParams = generatePublicParamters();
        //生成OpenAPI地址,不需要修改
        String URL = generateOpenAPIURL(publicParams, privateParams);
        //發送HTTP GET 請求
        httpGet(URL);
    }

    /**
     * 移動推送OpenAPI私有參數
     * 不同API需要修改此方法中的參數
     * 推送高級接口 https://help.aliyun.com/knowledge_detail/48089.html
     *
     * @return
     */
    private static Map<String, String> generatePrivateParamters() {
        // 接口私有參數列表, 不同API請替換相應參數
        Map<String, String> privateParams = new HashMap<String, String>();
        // API名稱
        privateParams.put("Action", "Push");
        // AppKey
        privateParams.put("AppKey", appkey);
        //推送目標: DEVICE:按設備推送 ALIAS : 按別名推送 ACCOUNT:按帳號推送  TAG:按標籤推送; ALL: 廣播推送
        privateParams.put("Target", "ALL");
        //根據Target來設定
        privateParams.put("TargetValue", "ALL");
        // 消息類型 MESSAGE NOTICE
        privateParams.put("PushType", "NOTICE");
        // 設備類型 ANDROID iOS ALL
        privateParams.put("DeviceType", "ANDROID");
        // 推送的標題
        privateParams.put("Title", "title");
        // 推送的內容
        privateParams.put("Body", "body");
        //通知的提醒方式 "VIBRATE" : 震動 "SOUND" : 聲音 "BOTH" : 聲音和震動 NONE : 靜音
        privateParams.put("AndroidNotifyType", "BOTH");
        privateParams.put("AndroidNotificationBarType", "1");
        privateParams.put("AndroidNotificationBarPriority", "1");
        privateParams.put("AndroidOpenType", "URL");
        privateParams.put("AndroidOpenUrl", "http://www.aliyun.com");
        privateParams.put("AndroidMusic", "default");
        privateParams.put("AndroidPopupActivity", "com.alibaba.cloudpushdemo.bizactivity.ThirdPushPopupActivity");
        privateParams.put("AndroidPopupTitle", "PopupTitle");
        privateParams.put("AndroidPopupBody", "PopupBody");
        privateParams.put("PushTime", generateTimestamp(System.currentTimeMillis()));
        privateParams.put("ExpireTime", generateTimestamp(System.currentTimeMillis() + 12 * 3600 * 1000));
        // 離線消息是否保存,若保存, 在推送時候,用戶即使不在線,下一次上線則會收到
        privateParams.put("StoreOffline", "true");
        //設定通知的擴展屬性
        privateParams.put("ExtParameters", "{'key1':'value1','api_name':'PushRequest'}");
        return privateParams;
    }

    /**
     * 移動推送OpenAPI公共參數
     * 不需要修改
     *
     * @return
     */
    private static Map<String, String> generatePublicParamters() {
        Map<String, String> publicParams = new HashMap<String, String>();
        publicParams.put("Format", "JSON");
        publicParams.put("Version", "2016-08-01");
        publicParams.put("AccessKeyId", access_key_id);
        publicParams.put("SignatureMethod", "HMAC-SHA1");
        publicParams.put("Timestamp", generateTimestamp(System.currentTimeMillis()));
        publicParams.put("SignatureVersion", "1.0");
        publicParams.put("SignatureNonce", generateRandom());
        if (security_token != null && security_token.length() > 0) {
            publicParams.put("SecurityToken", security_token);
        }
        return publicParams;
    }

    /**
     * 生成OpenAPI地址
     *
     * @param privateParams
     * @return
     * @throws Exception
     */
    private static String generateOpenAPIURL(Map<String, String> publicParams, Map<String, String> privateParams) {
        return generateURL(VOD_DOMAIN, HTTP_METHOD_GET, publicParams, privateParams);
    }

    /**
     * @param domain        請求地址
     * @param httpMethod    HTTP請求方式GET,POST等
     * @param publicParams  公共參數
     * @param privateParams 接口的私有參數
     * @return 最後的url
     */
    private static String generateURL(String domain, String httpMethod, Map<String, String> publicParams, Map<String, String> privateParams) {
        List<String> allEncodeParams = getAllParams(publicParams, privateParams);
        String cqsString = getCQS(allEncodeParams);
        out("CanonicalizedQueryString = " + cqsString);
        String stringToSign = httpMethod + "&" + percentEncode("/") + "&" + percentEncode(cqsString);
        out("StringtoSign = " + stringToSign);
        String signature = hmacSHA1Signature(access_key_secret, stringToSign);
        out("Signature = " + signature);
        return domain + "?" + cqsString + "&" + percentEncode("Signature") + "=" + percentEncode(signature);
    }

    private static List<String> getAllParams(Map<String, String> publicParams, Map<String, String> privateParams) {
        List<String> encodeParams = new ArrayList<String>();
        List<String> publicParamsList = paramsUrlEncode(publicParams);
        List<String> privateParamsList = paramsUrlEncode(privateParams);
        encodeParams.addAll(publicParamsList);
        encodeParams.addAll(privateParamsList);
        return encodeParams;
    }

    /**
     * 將參數和值都urlEncode一下
     */
    private static List<String> paramsUrlEncode(Map<String, String> params) {
        List<String> encodeParams = new ArrayList<String>();
        if (params != null) {
            for (String key : params.keySet()) {
                String value = params.get(key);
                //將參數和值都urlEncode一下。
                String encodeKey = percentEncode(key);
                String encodeVal = percentEncode(value);
                encodeParams.add(encodeKey + "=" + encodeVal);
            }
        }
        return encodeParams;
    }

    /**
     * 參數urlEncode
     *
     * @param value
     * @return
     */
    private static String percentEncode(String value) {
        try {
            String urlEncodeOrignStr = URLEncoder.encode(value, "UTF-8");
            String plusReplaced = urlEncodeOrignStr.replace("+", "%20");
            String starReplaced = plusReplaced.replace("*", "%2A");
            String waveReplaced = starReplaced.replace("%7E", "~");
            return waveReplaced;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return value;
    }

    /**
     * 獲取CQS 的字符串
     *
     * @param allParams
     * @return
     */
    private static String getCQS(List<String> allParams) {
        ParamsComparator paramsComparator = new ParamsComparator();
        Collections.sort(allParams, paramsComparator);
        String cqString = "";
        for (int i = 0; i < allParams.size(); i++) {
            cqString += allParams.get(i);
            if (i != allParams.size() - 1) {
                cqString += "&";
            }
        }

        return cqString;
    }

    private static class ParamsComparator implements Comparator<String> {
        @Override
        public int compare(String lhs, String rhs) {
            return lhs.compareTo(rhs);
        }
    }

    private static String hmacSHA1Signature(String accessKeySecret, String stringtoSign) {
        try {
            String key = accessKeySecret + "&";
            try {
                SecretKeySpec signKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM);
                Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
                mac.init(signKey);
                byte[] rawHmac = mac.doFinal(stringtoSign.getBytes());
                //按照Base64 編碼規則把上面的 HMAC 值編碼成字符串,即得到簽名值(Signature)
                return new String(new BASE64Encoder().encode(rawHmac));
            } catch (Exception e) {
                throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
            }
        } catch (SignatureException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 生成隨機數
     *
     * @return
     */
    private static String generateRandom() {
        String signatureNonce = UUID.randomUUID().toString();
        return signatureNonce;
    }

    /**
     * 生成當前UTC時間戳
     *
     * @return
     */
    public static String generateTimestamp(long time) {
        Date date = new Date(time);
        SimpleDateFormat df = new SimpleDateFormat(ISO8601_DATE_FORMAT);
        df.setTimeZone(new SimpleTimeZone(0, "GMT"));
        return df.format(date);
    }

    private static String httpGet(String url) throws IOException {
        /*
         * Read and covert a inputStream to a String.
         * Referred this:
         * http://stackoverflow.com/questions/309424/read-convert-an-inputstream-to-a-string
         */
        out("URL = " + url);
        @SuppressWarnings("resource")
        Scanner s = new Scanner(new URL(url).openStream(), UTF_8).useDelimiter("\\A");
        try {
            String resposne = s.hasNext() ? s.next() : "true";
            out("Response = " + resposne);
            return resposne;
        } finally {
            s.close();
        }
    }

    private static void out(String newLine) {
        LOG.log(Level.INFO, newLine);
    }
}


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