三分鐘幫你集成極光推送——和那些你可能不知道的事

本文簡介:本文前篇,可以幫助朋友們快速集成極光推送。本文後篇,是我自己項目實踐的一些總結和心得,應該對讀者們還是很有參考價值的,相信讀完這篇文章,你會對極光推送有更加深入的理解,而不僅僅只是會集成而已。總之呢,集成第三方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用戶綁定起來,以達到精準推送的目的。

1.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和用戶的對應關係。暫時我還沒有使用這種方式,真正投入到線上的項目,只是測試環境下調試過。)
值得一讀:推送人羣的選擇 – 推送方式-技術篇

2.別名與標籤推送(把綁定關係保存到 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();
    }
}

3.關於後端服務器設置別名還是前端設置別名:

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

小提醒:

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

 JPushInterface.resumePush(MyApplication.getInstance());

停止接收推送:

JPushInterface.stopPush(MyApplication.getInstance());

4.App殺死後還想要收到推送

有的公司由於業務需求,可能會要求你,App殺死後還想要收到推送。如果知道JPush Android SDK 是作爲 Android Service 長期運行在後臺的,從而創建並保持長連接,保持永遠在線的能力…的童鞋們,那麼必須要清楚一點的是:如果APP真的被殺死了,是不可能收到推送的,如果殺死了還能收到,那說明可能是以下幾種情況:

①應用自啓動了
②要麼是其他方式將App拉起來了。
③你壓根就沒有殺死App,Service還在運行着。(有的手機清理App,並沒有完全殺死進程的)

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

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