Android 推送集成華為,小米,友盟


公司的 app 一直使用的是極光推送,最近反饋比較多的是推送消息收不到,看來需要找新的推送服務了,在國內目前手機品牌占有率比較多的是華為和小米,且這兩家都有自己的推送服務,同時一個合作的友商說他們使用的是友盟推送,推送率還不錯,那么就測試這三個推送服務了。

按照集成的難易程度排序

友盟推送

接入步驟

官網有提供了視頻和文檔,很詳細,而且很簡單。

小米推送

接入步驟

  1. 在小米推送運營平台創建應用,地址點這里, 獲取到 AppID , AppKey

  2. 把從小米下載的 jar 放到 libs 下

  3. 在 AndroidManifest.xml 中添加權限

     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <uses-permission android:name="android.permission.GET_TASKS" />
     <uses-permission android:name="android.permission.VIBRATE"/>
     <permission android:name="com.xxx.xxx.permission.MIPUSH_RECEIVE" android:protectionLevel="signature" />
     <uses-permission android:name="com.xxx.xxx.permission.MIPUSH_RECEIVE" />
    
  4. 配置推送服務需要的service和receiver

     <service
       android:enabled="true"
       android:process=":pushservice"
       android:name="com.xiaomi.push.service.XMPushService"/>
     <service
       android:name="com.xiaomi.push.service.XMJobService"
       android:enabled="true"
       android:exported="false"
       android:permission="android.permission.BIND_JOB_SERVICE"
       android:process=":pushservice" />
     <!--注:此service必須在3.0.1版本以后(包括3.0.1版本)加入-->
     <service
       android:enabled="true"
       android:exported="true"
       android:name="com.xiaomi.mipush.sdk.PushMessageHandler" /> 
     <service android:enabled="true"
       android:name="com.xiaomi.mipush.sdk.MessageHandleService" /> 
     <!--注:此service必須在2.2.5版本以后(包括2.2.5版本)加入-->
     <receiver
       android:exported="true"
       android:name="com.xiaomi.push.service.receivers.NetworkStatusReceiver" >
       <intent-filter>
         <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
         <category android:name="android.intent.category.DEFAULT" />
       </intent-filter>
     </receiver>
     <receiver
       android:exported="false"
       android:process=":pushservice"
       android:name="com.xiaomi.push.service.receivers.PingReceiver" >
       <intent-filter>
         <action android:name="com.xiaomi.push.PING_TIMER" />
       </intent-filter>
     </receiver>
    
  5. 自定義一個BroadcastReceiver類

     public class MiMessageReceiver extends PushMessageReceiver {
         private static final String TAG = "MiMessageReceiver";
     
         private String mRegId;
         private String mTopic;
         private String mAlias;
         private String mAccount;
         private String mStartTime;
         private String mEndTime;
     
         @Override
         public void onNotificationMessageClicked(Context context, MiPushMessage message) {
             Log.v(MyApplication.TAG,
                     "onNotificationMessageClicked is called. " + message.toString());
     
             if (!TextUtils.isEmpty(message.getTopic())) {
                 mTopic = message.getTopic();
                 Log.e(TAG, mTopic);
             } else if (!TextUtils.isEmpty(message.getAlias())) {
                 mAlias = message.getAlias();
                 Log.e(TAG, mAlias);
             }
         }
     
         @Override
         public void onNotificationMessageArrived(Context context, MiPushMessage message) {
             Log.v(MyApplication.TAG,
                     "onNotificationMessageArrived is called. " + message.toString());
     
             String log = "Arrived a notification message. Content is " + message.getContent();
     
             if (!TextUtils.isEmpty(message.getTopic())) {
                 mTopic = message.getTopic();
             } else if (!TextUtils.isEmpty(message.getAlias())) {
                 mAlias = message.getAlias();
             }
     
         }
     
         @Override
         public void onCommandResult(Context context, MiPushCommandMessage message) {
             Log.v(TAG,
                     "onCommandResult is called. " + message.toString());
             String command = message.getCommand();
             List<String> arguments = message.getCommandArguments();
             String cmdArg1 = ((arguments != null && arguments.size() > 0) ? arguments.get(0) : null);
             String cmdArg2 = ((arguments != null && arguments.size() > 1) ? arguments.get(1) : null);
             String log;
             if (MiPushClient.COMMAND_REGISTER.equals(command)) {
                 if (message.getResultCode() == ErrorCode.SUCCESS) {
                     mRegId = cmdArg1;
                     Log.e(TAG, "Register push success.");
                 } else {
                     Log.e(TAG, "Register push fail.");
                 }
             } else {
                 log = message.getReason();
             }
         }
     
         @Override
         public void onReceiveRegisterResult(Context context, MiPushCommandMessage message) {
             Log.v(TAG,
                     "onReceiveRegisterResult is called. " + message.toString());
             String command = message.getCommand();
             List<String> arguments = message.getCommandArguments();
             String cmdArg1 = ((arguments != null && arguments.size() > 0) ? arguments.get(0) : null);
             if (MiPushClient.COMMAND_REGISTER.equals(command)) {
                 if (message.getResultCode() == ErrorCode.SUCCESS) {
                     mRegId = cmdArg1;
                     Log.e(TAG, "Register push success.");
                 } else {
                     Log.e(TAG, "Register push fail.");
                 }
             }
         }
     
     }
    
  6. 在 AndroidManifest.xml 中注冊該廣播

     <receiver
         android:name=".MiMessageReceiver"
         android:exported="true">
         <intent-filter>
             <action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE"/>
         </intent-filter>
         <intent-filter>
             <action android:name="com.xiaomi.mipush.MESSAGE_ARRIVED"/>
         </intent-filter>
         <intent-filter>
             <action android:name="com.xiaomi.mipush.ERROR"/>
         </intent-filter>
     </receiver>
    
  7. 在 Application 中初始化推送服務

    private void initMiPush() {
        //初始化push推送服務
        if (shouldInit()) {
            MiPushClient.registerPush(this, MI_APP_ID, MI_APP_KEY);
        }
        //打開Log
        LoggerInterface newLogger = new LoggerInterface() {
    
            @Override
            public void setTag(String tag) {
                // ignore
            }
    
            @Override
            public void log(String content, Throwable t) {
                Log.d(TAG, content, t);
            }
    
            @Override
            public void log(String content) {
                Log.d(TAG, content);
            }
        };
        Logger.setLogger(this, newLogger);
    }
    
    
    private boolean shouldInit() {
        ActivityManager am = ((ActivityManager) getSystemService(Context.ACTIVITY_SERVICE));
        List<ActivityManager.RunningAppProcessInfo> processInfos = am.getRunningAppProcesses();
        String mainProcessName = getPackageName();
        int myPid = android.os.Process.myPid();
        for (ActivityManager.RunningAppProcessInfo info : processInfos) {
            if (info.pid == myPid && mainProcessName.equals(info.processName)) {
                return true;
            }
        }
        return false;
    }
    

華為推送

接入步驟(HMS-SDK版本:2.4.0.300):

  1. 把從華為 Push 推送官網下載的 aar ,位於:...\HMS-2.4.0.300\HWHMS-SDK-v2.4.0.300\libs\HMS-SDK-2.4.0.300.aar,放到工程目錄的 aars 下(沒有該目錄的新建一個)

  2. 在 AndroidManifest.xml 文件的 Application 節點添加: meta-data 和自定義的 Receiver,如下(其中 meta-data 中的 appId 是在網頁上創建應用的 id,我的是一個 8 位的數字):

     <meta-data
         android:name="com.huawei.hms.client.appid"
         android:value="appId">
     </meta-data>
    
     <!-- 第三方相關 :接收Push消息(注冊、Push消息、Push連接狀態)廣播 -->
     <receiver android:name=".HuaweiPushReceiver">
         <intent-filter>
    
             <!-- 必須,用於接收token -->
             <action android:name="com.huawei.android.push.intent.REGISTRATION"/>
             <!-- 必須,用於接收消息 -->
             <action android:name="com.huawei.android.push.intent.RECEIVE"/>
             <!-- 可選,用於點擊通知欄或通知欄上的按鈕后觸發onEvent回調 -->
             <action android:name="com.huawei.android.push.intent.CLICK"/>
             <!-- 可選,查看push通道是否連接,不查看則不需要 -->
             <action android:name="com.huawei.intent.action.PUSH_STATE"/>
         </intent-filter>
    
         <meta-data
             android:name="CS_cloud_ablitity"
             android:value="@string/hwpush_ability_value"/>
     </receiver>
    
  3. 在 AndroidManifest.xml 文件中添加權限

     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    
  4. 在 MainActivity 或者 BaseAcitivty 中初始化華為 Push ,放在 onCreate() 中初始化華為 Push

     private void initHuaweiPush(Context context) {
         HuaweiIdSignInOptions options = new HuaweiIdSignInOptions.Builder(HuaweiIdSignInOptions.DEFAULT_SIGN_IN)
                 .build();
         mClient = new HuaweiApiClient.Builder(context)
                 .addApi(HuaweiPush.PUSH_API)
                 .addConnectionCallbacks(new HuaweiApiClient.ConnectionCallbacks() {
                     @Override
                     public void onConnected() {
                         getToken();
                         runOnUiThread(new Runnable() {
                             @Override
                             public void run() {
                                 tv.setText("HUAWEI onConnected, IsConnected: " + mClient.isConnected());
    
                             }
                         });
                         Log.e(TAG, "HUAWEI onConnected, IsConnected: " + mClient.isConnected());
    
                     }
    
                     @Override
                     public void onConnectionSuspended(final int i) {
                         runOnUiThread(new Runnable() {
                             @Override
                             public void run() {
                                 tv.setText("HUAWEI onConnectionSuspended, cause: " + i + ", IsConnected:" +
                                         " " +
                                         mClient.isConnected());
                             }
                         });
                         Log.e(TAG, "HUAWEI onConnectionSuspended, cause: " + i + ", IsConnected:" +
                                 " " +
                                 mClient.isConnected());
                     }
                 })
                 .addOnConnectionFailedListener(new HuaweiApiClient.OnConnectionFailedListener() {
                     @Override
                     public void onConnectionFailed(@NonNull final ConnectionResult
                                                            connectionResult) {
                         runOnUiThread(new Runnable() {
                             @Override
                             public void run() {
                                 tv.setText("HUAWEI onConnectionFailed, ErrorCode: " + connectionResult.getErrorCode());
                             }
                         });
                         Log.e(TAG, "HUAWEI onConnectionFailed, ErrorCode: " + connectionResult.getErrorCode());
                     }
                 })
                 .build();
         mClient.connect();
     }
    
     @Override
     protected void onStart() {
         super.onStart();
         mClient.connect();
     }
    
    
     private void getToken() {
         if (!isConnected()) {
             tv.setText("get token failed, HMS is disconnect.");
             return;
         }
    
         // 同步調用方式,不會返回token,通過廣播的形式返回。
         new Thread(new Runnable() {
             @Override
             public void run() {
                 PendingResult<TokenResult> token = HuaweiPush.HuaweiPushApi.getToken(mClient);
                 token.await();
             }
         }).start();
     }
    
    
     public boolean isConnected() {
         if (mClient != null && mClient.isConnected()) {
             return true;
         } else {
             return false;
         }
     }
    
  5. 新建 HuaweiPushReceiver ,繼承 PushReceiver,重寫 onToken() ,onPushMsg(),onEvent(),onPushState(),在 onToken() 中可以獲取到 token。

     public class HuaweiPushReceiver extends PushReceiver {
         private static final String TAG = "Huawei PushReceiver";
     
         @Override
         public void onToken(Context context, String token, Bundle extras) {
             String belongId = extras.getString("belongId");
             String content = "get token and belongId successful, token = " + token + ",belongId = " + belongId;
             Log.d(TAG, content);
         }
     
         @Override
         public boolean onPushMsg(Context context, byte[] msg, Bundle bundle) {
             try {
                 String content = "-------Receive a Push pass-by message: " + new String(msg, "UTF-8");
                 Log.d(TAG, content);
             } catch (Exception e) {
                 e.printStackTrace();
             }
             return false;
         }
     
         public void onEvent(Context context, PushReceiver.Event event, Bundle extras) {
             if (Event.NOTIFICATION_OPENED.equals(event) || Event.NOTIFICATION_CLICK_BTN.equals(event)) {
                 int notifyId = extras.getInt(BOUND_KEY.pushNotifyId, 0);
                 if (0 != notifyId) {
                     NotificationManager manager = (NotificationManager) context
                             .getSystemService(Context.NOTIFICATION_SERVICE);
                     manager.cancel(notifyId);
                 }
                 String content = "--------receive extented notification message: " + extras.getString
                         (BOUND_KEY.pushMsgKey);
                 Log.d(TAG, content);
             }
             super.onEvent(context, event, extras);
         }
     
         @Override
         public void onPushState(Context context, boolean pushState) {
             try {
                 String content = "---------The current push status: " + (pushState ? "Connected" :
                         "Disconnected");
                 Log.d(TAG, content);
             } catch (Exception e) {
                 e.printStackTrace();
             }
         }
     }
    
  6. 獲取到 token 以后就連接成功了。可以測試推送了。

非華為手機使用華為推送需要安裝-華為移動服務.apk

各個版本EMUI對push的支持情況
華為手機上:
Emui3.0上,Push廣播有很大概率被限制,如: Mate7 3.0版本,榮耀6plus,P7 3.0版本,4X, 4A等。
Emui3.1上,Push廣播基本不被限制,但個別型號機型存在問題,如:榮耀5x等。
Emui4.0及以上,Push廣播有較高概率被限制,不被限制的機型如:榮耀暢玩4C,榮耀暢玩4X,Mate S,P8 MAX等。
Emui4.1 , ROM升級到了最新版本的(80%已升),通知消息不走廣播,不會被限制,透傳消息走廣播,會被限制。
Emui5.0以上 ,通知消息不走廣播,不會被限制,透傳消息走廣播,會被限制。
如廣播被限制,需要將應用設為開機啟動項。所以對於及時性或到達率要求非常高的應用,我們建議應用要考慮替代方案。
非華為手機:
第三方手機(如:小米、OPPO、三星等),由於rom的限制,需要將應用 設為開機啟動項。

測試設備

  • Samsung S4(Android 5.0)
  • HTC D820u(Android 6.0)
  • Huawei P8(Android 6.0)
  • Xiaomi Note(Android 7.0)
  • Samsung S7(Android 7.0)
  • LG Nexus 6(Android 7.0)
  • Huawei Mate8(Android 7.0)
  • Huawei Mate9(Android 7.0)

測試數據

三家推送比較

  1. 華為推送在非華為手機上必須安裝華為移動服務,這點比較的坑,用戶可能不會同意安裝的,那就沒得玩了,只適合在華為手機上使用。
  2. 華為推送還得區別 Emui 的版本,這玩意也不是個小坑,雖然官方QQ群公告說 Emui 5.0 以后都可以收到消息推送,但是不知道靠譜不靠譜
  3. 友盟推送的話,在測試階段,發現三星S7(Android 7.0),三星S4(Android 5.0),華為P8(Android 6.0)無法獲取到 token,沒有 token ,那就推送不了了。
  4. 小米推送還可以,但是在三星 S4(Android 5.0) 上無法接收到推送。
  5. 綜上,app 需要集成華為推送和小米推送比較的靠譜點,針對華為手機使用華為推送,其他手機使用小米推送。
  6. 使用中的大坑,在華為手機上假如華為移動服務不是最新版本或者被卸載了,推送服務無法使用。必須在 app 中提示用戶更新或安裝,真是一個大坑,關鍵是更新界面丑的要死,還會出現更新失敗的情況,坑人呀。
  7. 針對小米推送,在非小米設備上會出現重啟以后無法獲得推送,因為是重啟以后,小米的 XMPushService 沒有起來,目前采用的辦法是監聽手機啟動廣播,然后啟動小米的 XMPushService。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM