Android第三方SDK集成 —— 極光推送

前言:

    本文前篇,可以幫助朋友們快速集成極光推送。本文後篇,是我自己項目實踐的一些總結和心得,應該對讀者們還是很有參考價值的,相信讀完這篇文章,你會對極光推送有更加深入的理解,而不僅僅只是會集成而已。總之呢,集成第三方SDK,都不是很難的事情,仔細閱讀文檔,一步步來,遇到Bug,慢慢解決就行,實在解決不了,可以問問客服小哥哥或者小姐姐,重要的是,你得有着解決它的決心和耐心。


《一》JPush SDK的集成

簡要介紹:
    極光推送(JPush)是一個端到端的推送服務,使得服務器端消息能夠及時地推送到終端用戶手機上,讓開發者積極地保持與用戶的連接,從而提高用戶活躍度、提高應用的留存率。

    開發者集成 JPush Android SDK 到其應用裏,JPush Android SDK 創建到 JPush Cloud 的長連接,爲 App 提供永遠在線的能力。 當開發者想要及時地推送消息到達 App 時,只需要調用 JPush API 推送,或者使用其他方便的智能推送工具,即可輕鬆與用戶交流。
JPush Android SDK 是作爲 Android Service 長期運行在後臺的,從而創建並保持長連接,保持永遠在線的能力。

假設你已經註冊了極光的賬號,登錄進來了。點擊立即體驗,創建自己的應用。

App端集成需要用到的AppKey

 

添加集成代碼,此處使用jcenter的方式集成

一、確認android studio的 Project 根目錄的主 gradle 中配置了jcenter支持。(新建project默認配置就支持)

buildscript {
    repositories {
        jcenter()
    }
    ......
}

allprojets {
    repositories {
        jcenter()
    }
}

二、在 module 的 gradle 中添加依賴和AndroidManifest的替換變量。

android {
    ......
    defaultConfig {
        applicationId "com.xxx.xxx" //JPush上註冊的包名.
        ......

        ndk {
            //選擇要添加的對應cpu類型的.so庫。
            abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a'
            // 還可以添加 'x86', 'x86_64', 'mips', 'mips64'
        }

        manifestPlaceholders = [
            JPUSH_PKGNAME : applicationId,
            JPUSH_APPKEY : "你的appkey", //JPush上註冊的包名對應的appkey.
            JPUSH_CHANNEL : "developer-default", //暫時填寫默認值即可.
        ]
        ......
    }
    ......
}

dependencies {
    ......

    compile 'cn.jiguang.sdk:jpush:3.1.1'  // 此處以JPush 3.1.1 版本爲例。
    compile 'cn.jiguang.sdk:jcore:1.1.9'  // 此處以JCore 1.1.9 版本爲例。
    ......
}

三、AndroidManifest.xml中添加

<!--Jpush配置 所需權限start-->
    <!-- Required -->
    <permission
        android:name="你的應用包名.permission.JPUSH_MESSAGE"
        android:protectionLevel="signature" />

    <!-- Required  一些系統要求的權限,如訪問網絡等-->
    <uses-permission android:name="你的應用包名.permission.JPUSH_MESSAGE" />
    <uses-permission android:name="android.permission.RECEIVE_USER_PRESENT" />
    <!--<uses-permission android:name="android.permission.INTERNET" />-->
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <!--<uses-permission android:name="android.permission.READ_PHONE_STATE" />-->
    <!--<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />-->
    <!--<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />-->
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
    <!--<uses-permission android:name="android.permission.VIBRATE" />-->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <!--<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />-->
    <!--<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />-->

    <!-- Optional for location -->
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <!-- 用於開啓 debug 版本的應用在6.0 系統上 層疊窗口權限 -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <!--<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />-->
    <!--<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />-->
    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
    <!--<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />-->
    <!--<uses-permission android:name="android.permission.GET_TASKS" />-->
    <!--Jpush配置 所需權限end-->

    <!--Jpush配置 start-->
    <application
        <!-- Rich push 核心功能 since 2.0.6-->
        <activity
            android:name="cn.jpush.android.ui.PopWinActivity"
            android:theme="@style/MyDialogStyle"
            android:exported="false">
        </activity>

        <!-- Required SDK核心功能-->
        <activity
            android:name="cn.jpush.android.ui.PushActivity"
            android:configChanges="orientation|keyboardHidden"
            android:theme="@android:style/Theme.NoTitleBar"
            android:exported="false">
            <intent-filter>
                <action android:name="cn.jpush.android.ui.PushActivity" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="你的應用包名" />
            </intent-filter>
        </activity>

        <!-- Required SDK 核心功能-->
        <!-- 可配置android:process參數將PushService放在其他進程中 -->
        <service
            android:name="cn.jpush.android.service.PushService"
            android:enabled="true"
            android:exported="false">
            <intent-filter>
                <action android:name="cn.jpush.android.intent.REGISTER" />
                <action android:name="cn.jpush.android.intent.REPORT" />
                <action android:name="cn.jpush.android.intent.PushService" />
                <action android:name="cn.jpush.android.intent.PUSH_TIME" />
            </intent-filter>
        </service>
        <!-- since 3.0.9 Required SDK 核心功能-->
        <provider
            android:authorities="你的應用包名.DataProvider"
            android:name="cn.jpush.android.service.DataProvider"
            android:exported="false"
            />

        <!-- since 1.8.0 option 可選項。用於同一設備中不同應用的JPush服務相互拉起的功能。 -->
        <!-- 若不啓用該功能可刪除該組件,將不拉起其他應用也不能被其他應用拉起 -->
        <service
            android:name="cn.jpush.android.service.DaemonService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="cn.jpush.android.intent.DaemonService" />
                <category android:name="你的應用包名" />
            </intent-filter>

        </service>
        <!-- since 3.1.0 Required SDK 核心功能-->
        <provider
            android:authorities="你的應用包名.DownloadProvider"
            android:name="cn.jpush.android.service.DownloadProvider"
            android:exported="true"
            />
        <!-- Required SDK核心功能-->
        <receiver
            android:name="cn.jpush.android.service.PushReceiver"
            android:enabled="true"
            android:exported="false">
            <intent-filter android:priority="1000">
                <action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED_PROXY" />   <!--Required  顯示通知欄 -->
                <category android:name="你的應用包名" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.USER_PRESENT" />
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            </intent-filter>
            <!-- Optional -->
            <intent-filter>
                <action android:name="android.intent.action.PACKAGE_ADDED" />
                <action android:name="android.intent.action.PACKAGE_REMOVED" />

                <data android:scheme="package" />
            </intent-filter>
        </receiver>

        <!-- Required SDK核心功能-->
        <receiver android:name="cn.jpush.android.service.AlarmReceiver" android:exported="false"/>

        <!-- User defined.  For test only  MyReceiver爲用戶自定義的廣播接收器-->
        <receiver
            android:name=".util.MyReceiver"
            android:exported="false"
            android:enabled="true">
            <intent-filter>
                <action android:name="cn.jpush.android.intent.REGISTRATION" /> <!--Required  用戶註冊SDK的intent-->
                <action android:name="cn.jpush.android.intent.MESSAGE_RECEIVED" /> <!--Required  用戶接收SDK消息的intent-->
                <action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED" /> <!--Required  用戶接收SDK通知欄信息的intent-->
                <action android:name="cn.jpush.android.intent.NOTIFICATION_OPENED" /> <!--Required  用戶打開自定義通知欄的intent-->
                <action android:name="cn.jpush.android.intent.CONNECTION" /><!-- 接收網絡變化 連接/斷開 since 1.6.3 -->
                <category android:name="你的應用包名" />
            </intent-filter>
        </receiver>

        <!-- Required  . Enable it you can get statistics data with channel -->
        <meta-data android:name="JPUSH_CHANNEL" android:value="developer-default"/>
        <meta-data android:name="JPUSH_APPKEY" android:value="應用的Appkey" /> <!--  </>值來自開發者平臺取得的AppKey-->
    </application>
        <!--Jpush配置 end-->

四、自定義一個廣播接收器MyReceiver(此處直接使用官方Demo中的MyReceiver)

/**
 * 自定義接收器
 * 
 * 如果不定義這個 Receiver,則:
 * 1) 默認用戶會打開主界面
 * 2) 接收不到自定義消息
 */
public class MyReceiver extends BroadcastReceiver {
    private static final String TAG = "JIGUANG-Example";

    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            Bundle bundle = intent.getExtras();
            Logger.d(TAG, "[MyReceiver] onReceive - " + intent.getAction() + ", extras: " + printBundle(bundle));

            if (JPushInterface.ACTION_REGISTRATION_ID.equals(intent.getAction())) {
                String regId = bundle.getString(JPushInterface.EXTRA_REGISTRATION_ID);
                Logger.d(TAG, "[MyReceiver] 接收Registration Id : " + regId);
                //send the Registration Id to your server...

            } else if (JPushInterface.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) {
                Logger.d(TAG, "[MyReceiver] 接收到推送下來的自定義消息: " + bundle.getString(JPushInterface.EXTRA_MESSAGE));
                processCustomMessage(context, bundle);

            } else if (JPushInterface.ACTION_NOTIFICATION_RECEIVED.equals(intent.getAction())) {
                Logger.d(TAG, "[MyReceiver] 接收到推送下來的通知");
                int notifactionId = bundle.getInt(JPushInterface.EXTRA_NOTIFICATION_ID);
                Logger.d(TAG, "[MyReceiver] 接收到推送下來的通知的ID: " + notifactionId);

            } else if (JPushInterface.ACTION_NOTIFICATION_OPENED.equals(intent.getAction())) {
                Logger.d(TAG, "[MyReceiver] 用戶點擊打開了通知");

                //打開自定義的Activity
                Intent i = new Intent(context, TestActivity.class);
                i.putExtras(bundle);
                //i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP );
                context.startActivity(i);

            } else if (JPushInterface.ACTION_RICHPUSH_CALLBACK.equals(intent.getAction())) {
                Logger.d(TAG, "[MyReceiver] 用戶收到到RICH PUSH CALLBACK: " + bundle.getString(JPushInterface.EXTRA_EXTRA));
                //在這裏根據 JPushInterface.EXTRA_EXTRA 的內容處理代碼,比如打開新的Activity, 打開一個網頁等..

            } else if(JPushInterface.ACTION_CONNECTION_CHANGE.equals(intent.getAction())) {
                boolean connected = intent.getBooleanExtra(JPushInterface.EXTRA_CONNECTION_CHANGE, false);
                Logger.w(TAG, "[MyReceiver]" + intent.getAction() +" connected state change to "+connected);
            } else {
                Logger.d(TAG, "[MyReceiver] Unhandled intent - " + intent.getAction());
            }
        } catch (Exception e){

        }

    }

    // 打印所有的 intent extra 數據
    private static String printBundle(Bundle bundle) {
        StringBuilder sb = new StringBuilder();
        for (String key : bundle.keySet()) {
            if (key.equals(JPushInterface.EXTRA_NOTIFICATION_ID)) {
                sb.append("\nkey:" + key + ", value:" + bundle.getInt(key));
            }else if(key.equals(JPushInterface.EXTRA_CONNECTION_CHANGE)){
                sb.append("\nkey:" + key + ", value:" + bundle.getBoolean(key));
            } else if (key.equals(JPushInterface.EXTRA_EXTRA)) {
                if (TextUtils.isEmpty(bundle.getString(JPushInterface.EXTRA_EXTRA))) {
                    Logger.i(TAG, "This message has no Extra data");
                    continue;
                }

                try {
                    JSONObject json = new JSONObject(bundle.getString(JPushInterface.EXTRA_EXTRA));
                    Iterator<String> it =  json.keys();

                    while (it.hasNext()) {
                        String myKey = it.next();
                        sb.append("\nkey:" + key + ", value: [" +
                                myKey + " - " +json.optString(myKey) + "]");
                    }
                } catch (JSONException e) {
                    Logger.e(TAG, "Get message extra JSON error!");
                }

            } else {
                sb.append("\nkey:" + key + ", value:" + bundle.getString(key));
            }
        }
        return sb.toString();
    }
    
    //send msg to MainActivity
    private void processCustomMessage(Context context, Bundle bundle) {
        if (MainActivity.isForeground) {
            String message = bundle.getString(JPushInterface.EXTRA_MESSAGE);
            String extras = bundle.getString(JPushInterface.EXTRA_EXTRA);
            Intent msgIntent = new Intent(MainActivity.MESSAGE_RECEIVED_ACTION);
            msgIntent.putExtra(MainActivity.KEY_MESSAGE, message);
            if (!ExampleUtil.isEmpty(extras)) {
                try {
                    JSONObject extraJson = new JSONObject(extras);
                    if (extraJson.length() > 0) {
                        msgIntent.putExtra(MainActivity.KEY_EXTRAS, extras);
                    }
                } catch (JSONException e) {

                }

            }
            LocalBroadcastManager.getInstance(context).sendBroadcast(msgIntent);
        }
    }
}

五、在Application中的onCreate()方法中初始化極光推送

        // 初始化 JPush
        JPushInterface.init(this);
        //發佈時關閉日誌
        JPushInterface.setDebugMode(false);

六、最後不要忘了添加混淆代碼哦!

請在工程的混淆文件proguard-rules.pro中添加以下配置:
-dontoptimize
-dontpreverify

-dontwarn cn.jpush.**
-keep class cn.jpush.** { *; }
-keep class * extends cn.jpush.android.helpers.JPushMessageReceiver { *; }

-dontwarn cn.jiguang.**
-keep class cn.jiguang.** { *; }

七、通過極光官網控制檯推送,測試推送結果。

 

《二》推送方式分析

使用場景分析:

    推送消息,無疑就是兩種情形,一種是全部推送,這種方式簡單,羣發就行了,Portal與API都支持向指定的 appKey 羣發消息。但是一般實際的業務需求,都不僅僅是羣發,還需要針對某一個人或者某一羣人進行推送。例如:給會員推送一些新的內容,此時就需要針對會員這一羣特定的人,來進行推送。簡單的說,就是需要把JPush的註冊用戶與開發者App用戶綁定起來,以達到精準推送的目的。

 

【推送方式一:RegistrationID方式實現點對點的精準推送(把綁定關係保存到開發者應用服務器中)】

    集成了 JPush SDK 的應用程序在第一次成功註冊到 JPush 服務器時,JPush 服務器會以廣播的形式發送RegistrationID到應用程序,給客戶端返回一個唯一的該設備的標識 - RegistrationID。首次註冊成功,自定義的MyReceiver中會收到一條廣播。

如下圖,會在MyReceiver中收到RegistrationID的值。只要極光推送第一次註冊成功了,後期不會再發 RegistrationID 的廣播了。RegistrationID 就會被保留在手機內,下次即使你是無網狀態進入APP,你也可以獲取到這個RegistrationID。有了這個標識,App 編程可以把這個 RegistrationID 保存到自己的應用服務器上,然後就可以根據 RegistrationID 來向設備推送消息或者通知。所以建議可以在你的Application和MyReceiver中都對這個RegistrationID進行賦值。然後根據項目的業務需要,將RegistrationID和用戶標識的對應關係,上傳自己的服務端。

public class MyApplication extends Application{
    public static String registrationID;
     @Override
    public void onCreate() {
        // 初始化 JPush
        JPushInterface.init(this);
        registrationID = JPushInterface.getRegistrationID(this);
        Log.d("TAG", "接收Registration Id : " + registrationID);
    }
}

 

【注意】

如果 App 不卸載,是直接覆蓋安裝,Android, iOS 上 RegistrationID 的值都不會變化。
如果 App 是卸載之後再次安裝:Android 上 RegistrationID 基本不會變;
iOS 上如果啓用了 IDFA 變化可能性不大,如果未啓用 IDFA 則每次安裝 RegistrationID 都會變;
參考:極光推送的設備唯一性標識 RegistrationID

如果使用此種推送方式,你可能會遇到的問題:假設在一個設備中登錄不同的賬號,那此時上傳給服務器的都是同一個RegistrationID,因爲設備沒有變化。那麼可能出現:本來服務器是根據A用戶找到它的RegistrationID進行推送,但是B用戶也是在A用戶登錄的設備登錄的,RegistrationID跟A的一樣,這樣就導致B用戶收到了推送給A用戶的推送內容。

需要謹記:

使用 RegistrationID 推送的關鍵於,App 開發者需要在開發 App 時,獲取到這個 RegistrationID,保存到 App 業務服務器上去,並且與自己的用戶標識對應起來。建議 App 開發者儘可能做這個保存動作。因爲這是最精確地定位到設備的。(RegistrationID 的方式,相對而言比較麻煩一點,因爲需要自己的App服務端維護RegistrationID和用戶的對應關係。暫時我還沒有使用這種方式,真正投入到線上的項目,只是測試環境下調試過。)
值得一讀:推送人羣的選擇 – 推送方式-技術篇

 

【推送方式二:別名與標籤推送(把綁定關係保存到 JPush 服務器端)

別名推送也是一種實現點對點推送的方式,用於給某特定用戶推送消息。
功能介紹:
①爲安裝了應用程序的用戶,取個別名來標識。以後給該用戶 Push 消息時,就可以用此別名來指定。
②每個用戶只能指定一個別名。
③同一個應用程序內,對不同的用戶,建議取不同的別名。這樣,儘可能根據別名來唯一確定用戶。
④系統不限定一個別名只能指定一個用戶。如果一個別名被指定到了多個用戶,當給指定這個別名發消息時,服務器端API會同時給這多個用戶發送消息。

舉例:在一個用戶要登錄的遊戲中,可能設置別名爲 userid。遊戲運營時,發現該用戶 3 天沒有玩遊戲了,則根據 userid 調用服務器端API發通知到客戶端提醒用戶。

別名設置:

需要和自己的服務端協商好別名的規則,例如下面的代碼中,是將用戶ID通過MD5加密,作爲別名,設置保存到JPush服務器。當然你也可以使用其他的拼接規則,具體根據每個公司的項目需要設置即可,只要滿足別名的命名限制就行: 命名長度限制爲 40 字節。(判斷長度需採用UTF-8編碼)

深入理解各種推送方式可以參考:推送人羣的選擇 – 推送方式-技術篇

下面是一個JPush設置別名和標籤的輔助類。

public class JPushHelper {
    private String TAG = "JPushHelper";

    /**
     * 設置別名與標籤
     *
     * @param UUID
     */
    private void setAlias(String UUID) {
        if (null != UUID) {
            //恢復接收推送
            JPushInterface.resumePush(MyApplication.getInstance());
            JPushInterface.setAliasAndTags(MyApplication.getInstance(), UUID, null, mAliasCallback);
        }
    }

    public void setAlias() {
        if (MyApplication.isLogin) { //必須登錄
            UserInfo userBean = ACT_Login.getLoginUser();
            if (null != userBean) {
                 //根據具體業務需求,可以再拼接上其他相關字段
                //String alias = "";
                //StringBuilder stringBuffer = new StringBuilder();
                // stringBuffer.append(StringUtil.getString(userBean.user_id));
                Log.e(TAG, stringBuffer.toString());
                alias = MD5Util.string2MD5(userBean.user_id);
                //限制:alias 命名長度限制爲 40 字節。(判斷長度需採用UTF-8編碼)
                Log.e(TAG, alias);
                //setAlias("");
                setAlias(alias);
            }
        }
    }

    /**
     * 停止接收推送
     */
    public void removeAlias() {
        JPushInterface.clearAllNotifications(MyApplication.getInstance());
        //setAlias("");//該句打開的話,如果退出登錄後,用戶就收不到離線的消息了。
        JPushInterface.stopPush(MyApplication.getInstance());
    }

    private final TagAliasCallback mAliasCallback = new TagAliasCallback() {

        @Override
        public void gotResult(int code, String alias, Set<String> tags) {
            String logs;
            switch (code) {
                case 0:
    // 建議這裏往 SharePreference 裏寫一個成功設置的狀態。成功設置一次後,以後不必再次設置了。
                    logs = "Set tag and alias success";
                    Log.e(TAG, logs);
                    break;
                case 6002:
                    logs = "Failed to set alias and tags due to timeout. Try again after 60s.";
                    // 延遲 60 秒來調用 Handler 設置別名
                    mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SET_ALIAS, alias), 1000 * 6);
                    Log.e(TAG, logs + AppDateUtil.getTimeStamp(System.currentTimeMillis(), AppDateUtil.MM_DD_HH_MM_SS));
                    break;
                default:
                    logs = "Failed with errorCode = " + code;
                    Log.e(TAG, logs);
            }

        }

    };

    private static final int MSG_SET_ALIAS = 1001;
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(android.os.Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case MSG_SET_ALIAS:
                    Log.e(TAG, "Set alias in handler." + ((String) msg.obj));
                    // 調用 JPush 接口來設置別名。
                    JPushInterface.setAliasAndTags(MyApplication.getInstance(),
                            (String) msg.obj,
                            null,
                            mAliasCallback);
                    break;
                default:
                    Log.e(TAG, "Unhandled msg - " + msg.what);
            }
        }
    };

    // 校驗Tag Alias 只能是數字,英文字母和中文
    public static boolean isValidTagAndAlias(String s) {
        Pattern p = Pattern.compile("^[\u4E00-\u9FA50-9a-zA-Z_!@#$&*+=.|]+$");
        Matcher m = p.matcher(s);
        return m.matches();
    }
}

 

【關於後端服務器設置別名還是前端設置別名】

說實在的,以前沒有遇到過這個選擇題,因爲之前做的極光推送都是在我們客戶端設置,不過最近調試JPush的時候後臺說他設置別名,我突然就有點蒙了?不是一般都是前端設置嗎???
於是有個疑問:如果是服務端設置別名,那服務端也沒有經過客戶端,那極光服務器咋通過別名來匹配用戶進行點對點推送呀???我一下子有點想不通了。然後查了一下文檔和相關博客,確實極光服務器也給後端服務提供設置別名的API,又問了一下後端開發,他是不是隻是單單設置了別名,還是在設備ID上也有做了處理,因爲沒有映射關係,極光服務器也不可能找到對應的用戶進行推送呀。果不其然,他是在RegistrationID上設置的別名,這下我就明白了。
不過大部分的情況,應該還是前端設置別名吧,畢竟那些登入登出的操作在前端控制會比較方便。

小提醒:

實際應用場景,客戶端一般都需要在登錄到App成功後,設置別名,恢復接收推送。退出登錄後,停止接收推送。
恢復接收推送:

JPushInterface.resumePush(MyApplication.getInstance());

停止接收推送:

JPushInterface.stopPush(MyApplication.getInstance());

 

【App殺死後還想要收到推送】

有的公司由於業務需求,可能會要求你,App殺死後還想要收到推送。如果知道JPush Android SDK 是作爲 Android Service 長期運行在後臺的,從而創建並保持長連接,保持永遠在線的能力...的童鞋們,那麼必須要清楚一點的是:如果APP真的被殺死了,是不可能收到推送的,如果殺死了還能收到,那說明可能是以下幾種情況:
①應用自啓動了
②要麼是其他方式將App拉起來了。
③你壓根就沒有殺死App,Service還在運行着。(有的手機清理App,並沒有完全殺死進程的)

關於這個問題可以看看這位小姐姐的總結:Android 關於App被殺死後,如何接收極光推送
閱讀參考:
官網集成
極光推送Android端API
詳解極光推送的 4 種消息形式 —— Android 篇 
常見問題 - JPush 合集(持續更新)

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