JPush極光推送的原理

 相信開發者們一定不陌生JPush極光推送,像QQ、微信的推送機制,QQ採用的是APNS推送服務,微信則採用google的GCM推送機制,很多人都說APNS是一個死流氓服務,我也沒去了解,而GCM有點像IOS自帶的推送,有待了解。很多項目做一些通訊功能,大部分人都會選擇JPush極光推送,因爲用起來簡單,代碼量也少,JPush官網上的開發文檔也寫的相當清楚,一些步驟也清晰明瞭。今天在這邊主要就是講一下推送的原理,以及demo測試中會遇到的問題,這也是移植到項目中易出錯的地方。

極光推送的功能:主動 即時的向用戶發起交互,可以發送聊天信息等; 
——作用:通過向精準的目標用戶推送有價值的消息,可以提供用戶的忠誠度,提高留存率。

(1)推送方式 
——發送通知:推送的文本內容,展示在通知欄上面; 
——自定義消息:推送自定義消息,給用戶自行處理; 
——富媒體:推送的是HTML網頁內容。

(2)推送目標 
——廣播推送:向所有用戶發送廣播信息; 
——標籤推送 Tag:根據用戶設置自定義的標籤分組,向某一組推送消息; 
——別名推送 Alias:客戶端綁定用戶自定義的用戶別名,向單個用戶推送消息。

(3)用戶分羣 
——用戶分羣:可以根據JPush提供的多條件組合,對用戶進行羣組劃分,實現實時篩選推送。

(4)推送歷史 
——推送歷史:通過WEB或者API發出的推送,都可以在推送歷史記錄中查詢到,並可以實時顯示推送結果數據。

推送框架
——推送的數據源:自己開發的服務器端或者使用極光推送官網的WEB後臺; 
——JPush API:部署在服務器端,開發者的服務器端發起推送時,將數據傳到JPush API中,然後向下傳遞; 
——建立長鏈接:集成JPush的SDK客戶端啓動後會建立一個到JPush Cloud的長鏈接,提供App永遠在線的能力(可以參考極光推送官方博客); 
——原理圖: 


客戶端原理
IP地址的分配原理 
——IP地址有限:IPv4的IP地址數量有限,運營商要動態的爲手機分配IP地址,這些IP地址都是運營商的內網IP; 
——網絡地址轉換(NAT):全稱Network Address Translation,網關維護一個外網IP地址,與內網的IP地址對應; 
——外網IP不固定:由於運營商持有的外網IP數量有限,需要動態的爲分配給接入運營商的用戶,因此在手機一段時間沒有數據傳輸時會將該手機分配的外網IP地址收回,分配給其他用戶; 
——解決方案:Android手機端想要保持長鏈接,首先外網IP地址不能變,不能讓運營商收回這個IP地址。

Android手機端實現方案 
——心跳:爲了長時間保持外網IP,需要客戶端定期發送心跳給運營商,以便刷新NAT列表; 
——Timer定時方法:該類計劃循環執行定時任務,但是使用該類會使CPU保持喚醒狀態,比較費電。 
——AlarmManager定時方法:該類封裝了Android手機的RTC硬件時鐘模塊,可以在CPU休眠時正常運行,保持任務執行時再喚醒CPU,這樣做到了電量節省。

簡單demo容易出錯的地方 
·Appcation中初始化JPush 
——JPushInterface.init(this); // 初始化 JPush 
·啓動的主程序裏面一定要複寫兩個生命週期:

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

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

注意:如果主程序裏面的Activity繼承的是InstrumentedActivity,則不需要寫JPushInterface.onResume(MainActivity.this);我的MainActivity繼承的是FragmentActivity,所以加上了這句。

這是我的demo代碼,貼出來大家看的比較清晰些:

package com.lai.jpushdemo;
 
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.FragmentActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.Toast;
 
import com.ms.stock.R;
 
import java.util.LinkedHashSet;
import java.util.Set;
 
import cn.jpush.android.api.JPushInterface;
import cn.jpush.android.api.TagAliasCallback;
 
 
public class MainActivity extends FragmentActivity{
    private static final String TAG = "JPush";
 
 
    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setTag("abc");
    }
 
    /**
     * 設置tags
     */
    private void setTag(String tag){
 
        // 檢查 tag 的有效性
        if (TextUtils.isEmpty(tag)) {
            Toast.makeText(MainActivity.this,R.string.error_tag_empty, Toast.LENGTH_SHORT).show();
            return;
        }
        // ","隔開的多個 轉換成 Set
        String[] sArray = tag.split(",");
        Set<String> tagSet = new LinkedHashSet<String>();
        for (String sTagItme : sArray) {
            if (!ExampleUtil.isValidTagAndAlias(sTagItme)) {
                Toast.makeText(MainActivity.this,R.string.error_tag_gs_empty, Toast.LENGTH_SHORT).show();
                return;
            }
            tagSet.add(sTagItme);
        }
        //調用JPush API設置Tag
        mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_TAGS, tagSet));
 
    }
 
 
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK){
            finish();
        }
        return super.onKeyDown(keyCode, event);
    }
 
 
 
 
    private final TagAliasCallback mTagsCallback = new TagAliasCallback() {
 
        @Override
        public void gotResult(int code, String alias, Set<String> tags) {
            String logs ;
            switch (code) {
            case 0:
                logs = "設置別名和標籤成功!";
                Log.i(TAG, logs);
                break;
 
            case 6002:
                logs = "設置超時,60s後重試!";
                Log.i(TAG, logs);
                if (ExampleUtil.isConnected(getApplicationContext())) {
                    mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SET_TAGS, tags), 1000 * 60);
                } else {
                    Log.i(TAG, "沒有連接網絡");
                }
                break;
 
            default:
                logs = "失敗代碼 = " + code;
                Log.e(TAG, logs);
            }
 
            ExampleUtil.showToast(logs, getApplicationContext());
        }
 
    };
    private static final int MSG_SET_TAGS = 1002;
 
 
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(android.os.Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
 
            case MSG_SET_TAGS:
                Log.d(TAG, "在handler裏面設置tags");
                JPushInterface.setAliasAndTags(getApplicationContext(), null, (Set<String>) msg.obj, mTagsCallback);
                break;
 
            default:
                Log.i(TAG, "handler沒有內容 - " + msg.what);
            }
        }
    };
 
    public static boolean isForeground = false;
 
    @Override
    protected void onResume() {
        isForeground = true;
        super.onResume();
        JPushInterface.onResume(MainActivity.this);
    }
 
 
    @Override
    protected void onPause() {
        isForeground = false;
        super.onPause();
        JPushInterface.onPause(MainActivity.this);
    }
 
}

這裏設置的Tag值是寫死的(我項目中只用到Tag),寫成動態的話,後面大家可以寫到Appcation裏面。

·別忘記添加jar包和.so文件 
  如果博友用eclipse開發的話,直接把jar和.so文件添加到libs目錄中即可,如果用的是android studio,則需要把.so文件單獨放進jniLibs文件夾中(注意:studio新建的文件夾自帶是jni,這邊的jniLibs目錄是需要手動去重命名文件夾,經測試,如果是jni文件夾,項目會異常,找不到文件,所以必須是jniLibs)。

·AndroidManifest.xml 
  *權限(uses-permission)一定要放在appcation前面。 
這點大家注意下,昨天因爲這個問題折騰了好久,我的習慣就是先寫一個demo,然後再移植到自己的項目中,這樣做起來比較快,也不會太亂。恰恰這個問題讓我非常痛苦,最後看了日誌才發現的,值得大家注意。
————————————————
版權聲明:本文爲CSDN博主「上士聞道~勤而行之」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/huangli1466384630/article/details/79889473

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