極光推送 JPush 簡介 集成 MD


Markdown版本筆記 我的GitHub首頁 我的博客 我的微信 我的郵箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

目錄

JPush產品簡介

Demo地址
控制台

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

主要功能:

  • 保持與服務器的長連接,以便消息能夠即時推送到達客戶端
  • 接收通知與自定義消息,並向開發者App傳遞相關信息

主要特點:

  • 客戶端維持連接占用資源少、耗電低
  • SDK豐富的接口,可定制通知欄提示樣式
  • 服務器大容量、穩定

原理:JPush Android SDK 是作為 Android Service 長期運行在后台的,從而創建並保持長連接,保持永遠在線的能力。

JPush是經過考驗的大規模APP推送平台,每天推送消息數超過5億條。 開發者集成SDK后,可以通過調用API推送消息。同時,JPush提供可視化的web端控制台發送通知,統計分析推送效果。 JPush全面支持 Android, iOS, Winphone 三大手機平台。

消息形式

JPush提供四種消息形式:通知,自定義消息,富媒體和本地通知。

1、通知
或者說 Push Notification,即指在手機的通知欄(狀態欄)上會顯示的一條通知信息。 通知主要用於提示用戶的目的,應用於新聞內容、促銷活動、產品信息、版本更新提醒、訂單狀態提醒等多種場景
開發者參考文檔:Push API v3 notification

2、自定義消息
自定義消息不是通知,所以不會被SDK展示到通知欄上。其內容完全由開發者自己定義。 自定義消息主要用於應用的內部業務邏輯。一條自定義消息推送過來,有可能沒有任何界面顯示。
開發者參考文檔:Push API v3 message

3、富媒體
JPush支持開發者發送圖文並茂的通知,從而更好的傳達信息,帶來更豐富的用戶互動。 JPush提供了5種模板,開發者可以通過填充模板的內容,發送landing page、彈窗、信息流形式的富媒體通知。 開發者還可以直接通過URL發送預先編輯好的頁面。 富媒體當前支持Android平台,為更好的使用富媒體的功能,建議更新當前SDK版本至v2.1.8及以上。 暫時只能通過極光推送的控制台發送,不支持API形式。
Android 開發者參考文檔:Rich Push開發指南

4、本地通知
本地通知API不依賴於網絡,無網條件下依舊可以觸發;本地通知的定時時間是自發送時算起的,不受中間關機等操作的影響。 本地通知與網絡推送的通知是相互獨立的,不受保留最近通知條數上限的限制。 本地通知適用於在特定時間發出的通知,如一些Todo和鬧鍾類的應用,在每周、每月固定時間提醒用戶回到應用查看任務。
Android 開發者參考文檔:Android 本地通知

推送目標

通過使用標簽,別名,Registration ID 和用戶分群,開發者可以向特定的一個或多個用戶推送消息。

1、標簽
為安裝了應用程序的用戶打上標簽,其目的主要是方便開發者根據標簽,來批量下發 Push 消息。 可為每個用戶打多個標簽。 舉例: game, old_page, women

2、別名
每個用戶只能指定一個別名(意思是說,一個別名可以有多個用戶)。 同一個應用程序內,對不同的用戶,建議取不同的別名。這樣,盡可能根據別名來唯一確定用戶。
Android 開發者參考文檔:Android 標簽和別名,使用別名和標簽推送請參考文檔:Push API v3 Audience

3、Registration ID
客戶端初始化 JPush 成功后,JPush 服務端會分配一個 Registration ID,作為此設備的標識(同一個手機不同 APP 的 Registration ID 是不同的)。開發者可以通過指定具體的 Registration ID 來進行對單一設備的推送。

4、用戶分群
用戶分群的篩選條件有:標簽、地理位置、系統版本、注冊時間、活躍用戶和在線用戶。 比如,開發者可以設置這樣的用戶分群:位於北京、上海、廣州和深圳,並且最近7天在線的用戶。 開發者可以通過在控制台設置好用戶分群之后,在控制台推送時指定該分群的名稱或使用API調用該分群的id發送。
用戶分群控制台使用指南:用戶分群

5、統計分析
JPush支持推送數量、用戶打開次數、用戶使用時長、新增用戶、活躍用戶等數據的統計。 Android開發者需要實現了相關的統計API,才可以進行用戶相關的統計。 iOS的開發者不需要實現統計API,可以直接在【控制台】-【統計】頁面查看相關數據。
Android 開發者參考文檔:統計分析API

集成步驟

文檔首頁

1、快速開始

  • 到極光推送官方網站注冊開發者帳號
  • 登錄進入管理控制台,創建應用程序,得到 Appkey(SDK與服務器端通過Appkey互相識別);
  • 下載SDK 集成到 App 里。

2、三 分鍾快速使用
文檔地址

創建應用
使用注冊賬號登陸,進入極光控制台后,點擊“創建應用”按鈕。創建帳號進入極光推送后,首先顯示的是創建應用的界面。填上你的應用程序的名稱,以及 Android包名這二項就可以了。

下載Demo,導入AS,運行
點擊 ”下載Demo“,你將下載到一個 .zip 壓縮文件。解壓后,即看到一個同名目錄。這個目錄下,是一個 Android 項目里的所有文件。
在 Android Studio 中,新建一個項目。 通過 import module 導入 JPush Example
導入 module 后在 Android studio 內運行到指定設備上

Portal上推送通知和消息
在上述步驟安裝 JPush Example 的手機上,你就可以收到推送的通知和消息了。

FAQ

FAQ
產品價格

怎么樣保證推送消息的安全?
我們建議開發者不要推送保密的信息,就像QQ建議你不要在聊天時發送保密的信息一樣。
如果開發者的確有保密的信息,需要送達到用戶,則可以考慮這樣做:
先通過 JPush 推送一條消息,這條消息觸發客戶端App去與開發者服務器交互保密信息。

極光推送后台使用什么技術實現的?是 XMPP 協議么?
后台主要使用純 C 語言實現。
使用自定義的二進制協議,以盡可能節約流量。

可以推送多媒體文件到客戶端么?
推送消息本身是限定長度的文本。
不直接支持文件的推送,但可以通過推送 url 來實現。
即先推送文件下載 url,到客戶端觸發邏輯來通過 url 下載文件。

為什么應用程序無法收到 Push 消息(Android)?
確認 appKey(在Portal上生成的)已經正確的寫入 Androidmanifest.xml
確認測試手機(或者模擬器)已成功連入網絡
確認有客戶端 "Login succeed" 日志

日志:Java.lang.UnsatisfiedLinkError
此錯誤是由於沒有正確的加載libjpush.so文件,請檢查libjpush.so是否在正確的位置(libs–>armeabi–>libjpush.so)
JPush SDK 遷移到 Android Studio 需要添加 .SO 文件打包到APK的lib文件夾中,可以編輯 build.gradle 腳本,自定義 .so 目錄。

日志:The permission should be defined
此錯誤是沒有正確的定義permision,請添加權限:

<permission android:name="您應用的包名.permission.JPUSH_MESSAGE" android:protectionLevel="signature" ></permission>
<uses-permission android:name="您應用的包名.permission.JPUSH_MESSAGE" ></uses>

推送成功了,為什么有部分客戶端收不到推送?
請檢查收不到通知的手機:
請在logcat查看日志,確定客戶端的jpush是否集成成功,網絡是否有問題
請看日志或使用接口 isPushStopped來檢查是否調用了stoppush
檢查手機的JPush高級設置中是否設置了“允許推送時間”
手機的應用中是否勾選了“顯示通知”

Tag、Alias、Registrationid需要每次初始化時都重新設置嗎,會變化嗎?
tag、alias可以參考別名與標簽 API進行設置,每次設置是覆蓋設置,而不是增量設置。Tag和alias一經設置成功,除非取消或覆蓋,是不會變化的。設置好的tag、alias與客戶端的對應關系保存在JPush服務器,目前沒有從JPush服務器查詢這個對應關系的接口,所以需要客戶將對應關系保存在APP應用服務器。
Registrationid是客戶端SDK第一次成功連接到Jpush服務器時,Jpush服務器給分配的。可以通過獲取 RegistrationID API來獲取Registrationid進行推送。Registrationid對應一個應用的一個客戶端。

appkey是怎么對應的?
android 的包名和 appkey 需對應。

簡潔版自定義消息推送Demo

自定義消息又叫應用內消息,或者稱作透傳消息。此部分內容不會展示到通知欄上,JPush SDK 收到消息內容后透傳給 App,需要 App 自行處理。

關鍵字 類型 選項 含義
msg_content string 必填 消息內容本身
title string 可選 消息標題
content_type string 可選 消息內容類型
extras JSON Object 可選 JSON 格式的可選參數

在適當的時候初始化

private void initJPush() {
    JPushInterface.setDebugMode(true);    // 設置開啟日志,發布時請關閉日志
    JPushInterface.init(this);            // 初始化 JPush
}

Activity

public class JPushTestActivity extends ListActivity {

    public static boolean isForeground = false;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView tv = new TextView(this);
        tv.setTextColor(Color.BLUE);
        String string = "AppKey: " + PushUtil.getAppKey(this) + "\n" +
                "IMEI: " + PushUtil.getImei(this, "") + "\n" +
                "RegId:" + JPushInterface.getRegistrationID(this) + "\n" +
                "PackageName: " + getPackageName() + "\n" +
                "deviceId:" + PushUtil.getDeviceId(this) + "\n" +
                "Version: " + PushUtil.GetVersionName(this);
        tv.setText(string);
        getListView().addHeaderView(tv);

        String[] array = {"initPush", "stopPush", "resumePush",};
        setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, new ArrayList<>(Arrays.asList(array))));
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        switch (position - 1) {
            case 0:
                JPushInterface.init(this);// 初始化 JPush。如果已經初始化,但沒有登錄成功,則執行重新登錄。
                break;
            case 1:
                JPushInterface.stopPush(this);
                break;
            case 2:
                JPushInterface.resumePush(this);
                break;
        }
    }

    @Override
    protected void onResume() {
        isForeground = true;
        super.onResume();
    }

    @Override
    protected void onPause() {
        isForeground = false;
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onPushEvent(BasePushBean bean) {
        TextView tv = new TextView(this);
        tv.setTextColor(Color.BLUE);
        tv.setText(bean.msg);
        getListView().addFooterView(tv);
    }
}

Receiver

/**
 * 自定義接收器。如果不定義這個 Receiver,則默認用戶會打開主界面,接收不到自定義消息
 */
public class JPushReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent == null || intent.getAction() == null) return;

        Bundle bundle = intent.getExtras();
        Log.i("bqt", "【JPushReceiver】Action:" + intent.getAction() + "\nextras:" + printBundle(bundle));

        switch (intent.getAction()) {
            case JPushInterface.ACTION_MESSAGE_RECEIVED://將自定義消息轉發到需要的地方
                Log.i("bqt", "【JPushReceiver】接收到推送下來的自定義消息");
                if (bundle != null) {
                    PushMsgReceiverHelper.getInstance().onJPushMsgReceiver(bundle);
                }
                break;
            case JPushInterface.ACTION_REGISTRATION_ID:
                String regId = bundle == null ? "" : bundle.getString(JPushInterface.EXTRA_REGISTRATION_ID);
                Log.i("bqt", "【JPushReceiver】接收Registration Id : " + regId);
                break;
            case JPushInterface.ACTION_NOTIFICATION_RECEIVED:
                Log.i("bqt", "【JPushReceiver】接收到推送下來的通知");
                int notifactionId = bundle == null ? -1 : bundle.getInt(JPushInterface.EXTRA_NOTIFICATION_ID);
                Log.i("bqt", "【JPushReceiver】接收到推送下來的通知的ID: " + notifactionId);
                break;
            case JPushInterface.ACTION_NOTIFICATION_OPENED:
                Log.i("bqt", "【JPushReceiver】用戶點擊打開了通知");
                break;
            case JPushInterface.ACTION_RICHPUSH_CALLBACK:  // 根據 JPushInterface.EXTRA_EXTRA 的內容處理代碼
                String extra = bundle == null ? "" : bundle.getString(JPushInterface.EXTRA_EXTRA);
                Log.i("bqt", "【JPushReceiver】用戶收到到RICH PUSH CALLBACK: " + extra);
                break;
            case JPushInterface.ACTION_CONNECTION_CHANGE:
                boolean connected = intent.getBooleanExtra(JPushInterface.EXTRA_CONNECTION_CHANGE, false);
                Log.i("bqt", "【JPushReceiver】" + intent.getAction() + " connected state change to " + connected);
                break;
            default:
                Log.i("bqt", "【JPushReceiver】Unhandled intent - " + intent.getAction());
                break;
        }
    }

    // 打印所有的 intent extra 數據
    private static String printBundle(Bundle bundle) {
        if (bundle == null) return "";
        StringBuilder sb = new StringBuilder();
        for (String key : bundle.keySet()) {
            switch (key) {
                case JPushInterface.EXTRA_NOTIFICATION_ID:
                    sb.append("\nkey:").append(key).append(", value:").append(bundle.getInt(key));
                    break;
                case JPushInterface.EXTRA_CONNECTION_CHANGE:
                    sb.append("\nkey:").append(key).append(", value:").append(bundle.getBoolean(key));
                    break;
                case JPushInterface.EXTRA_EXTRA:
                    if (TextUtils.isEmpty(bundle.getString(JPushInterface.EXTRA_EXTRA))) {
                        Log.i("bqt", "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:").append(key)
                                    .append(", value: [").append(myKey)
                                    .append(" - ").append(json.optString(myKey)).append("]");
                        }
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    sb.append("\nkey:").append(key).append(", value:").append(bundle.getString(key));
                    break;
            }
        }
        return sb.toString();
    }
}

消息處理類

/**
 * 處理推送SDK推過來的自定義消息(又叫應用內消息,或者透傳消息)
 */
public class PushMsgReceiverHelper {
    private static PushMsgReceiverHelper instance = new PushMsgReceiverHelper();

    private PushMsgReceiverHelper() {
    }

    public static PushMsgReceiverHelper getInstance() {
        return instance;
    }

    /**
     * 處理極光推送推過來的自定義消息
     */
    public void onJPushMsgReceiver(Bundle bundle) {
        String message = bundle.getString(JPushInterface.EXTRA_MESSAGE);//必填,消息內容本身
        String title = bundle.getString(JPushInterface.EXTRA_TITLE);//可選,消息標題
        String type = bundle.getString(JPushInterface.EXTRA_CONTENT_TYPE);//可選,消息內容類型
        String extra = bundle.getString(JPushInterface.EXTRA_EXTRA);//可選,JSON 格式的可選參數
        String content = "msg:" + message + "\t title:" + title + "\t type:" + type + "\t extra:" + extra;
        Log.i("bqt", "【極光推送】" + content);
        if (JPushTestActivity.isForeground) {
            EventBus.getDefault().post(new BasePushBean(content, BasePushBean.TYPE_STRING));
        }
    }

    /**
     * 處理小米推送推過來的自定義消息
     */
    public void onMiPushMsgReceiver(MiPushMessage message) {
    }

    /**
     * 處理華為光推送推過來的自定義消息
     */
    public void onHuaweiPushMsgReceiver(String message) {
    }

    /**
     * 處理魅族推送推過來的自定義消息
     */
    public void onMeiZhuPushMsgReceiver(String message) {
    }

}

Bean

public class BasePushBean {
    public static final int TYPE_STRING = 1;
    public static final int TYPE_JSONOBJ = 2;

    public String msg;
    public int type;

    public BasePushBean(String msg, int type) {
        this.msg = msg;
        this.type = type;
    }
}

配置文件

build.gradle

implementation files('libs/jcore-android-1.2.0.jar')
implementation files('libs/jpush-android-3.1.2.jar')
implementation 'org.greenrobot:eventbus:3.1.1'

混淆文件:proguard-android.txt

-dontoptimize
-dontpreverify
-dontwarn cn.jpush.**
-keep class cn.jpush.** { *; }

#v2.0.5 及以上的版本由於引入了protobuf ,在上面基礎之上增加排除混淆的配置。
#==================gson==========================
-dontwarn com.google.**
-keep class com.google.gson.** {*;}

#==================protobuf======================
-dontwarn com.google.**
-keep class com.google.protobuf.** {*;}

AndroidManifest.xml

<!--====================== 推送SDK需要定義的權限【需要更改包名】 =====================-->
<!--極光-->
<permission
    android:name="com.bqt.push.permission.JPUSH_MESSAGE"
    android:protectionLevel="signature"/>
<uses-permission android:name="com.bqt.push.permission.JPUSH_MESSAGE"/>
<!--小米-->
<!--華為-->
<!--魅族-->
<!--========================= 極光推送需要注冊的組件 start =========================-->
<!-- User defined.  用戶自定義的廣播接收器-->
<receiver
    android:name="com.bqt.push.receiver.JPushReceiver"
    android:enabled="true"
    android:exported="false">
    <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="com.bqt.push"/>
    </intent-filter>
</receiver>

<activity
    android:name="cn.jpush.android.ui.PopWinActivity"
    android:exported="false"
    android:theme="@style/MyDialogStyle">
</activity>

<activity
    android:name="cn.jpush.android.ui.PushActivity"
    android:configChanges="orientation|keyboardHidden"
    android:exported="false"
    android:theme="@android:style/Theme.NoTitleBar">
    <intent-filter>
        <action android:name="cn.jpush.android.ui.PushActivity"/>

        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="com.bqt.push"/>
    </intent-filter>
</activity>

<service
    android:name="cn.jpush.android.service.PushService"
    android:exported="false"
    android:process=":mult">
    <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>

<provider
    android:name="cn.jpush.android.service.DataProvider"
    android:authorities="com.bqt.push.DataProvider"
    android:exported="false"/>

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

<provider
    android:name="cn.jpush.android.service.DownloadProvider"
    android:authorities="com.bqt.push.DownloadProvider"
    android:exported="true"
    tools:ignore="ExportedContentProvider"/>

<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="com.bqt.push"/>
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.USER_PRESENT"/>
        <action
            android:name="android.net.conn.CONNECTIVITY_CHANGE"
            tools:ignore="BatteryLife"/>
    </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>

<receiver
    android:name="cn.jpush.android.service.AlarmReceiver"
    android:exported="false"/>

<!-- 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="7ced56a29466cb706362bb82"/> <!-- 值來自開發者平台取得的AppKey-->
<!--========================= 極光推送需要注冊的組件 end =========================-->

2018-4-9


免責聲明!

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



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