相信開發者們一定不陌生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