相信開發者們必定不陌生JPush極光推送,像QQ、微信的推送機制,QQ採用的是APNS推送服務,微信則採用google的GCM推送機制,很 多人都說APNS是一個死流氓服務,我也沒去了解,而GCM有點像IOS自帶的推送,有待了解。不少項目作一些通信功能,大部分人都會選擇JPush極光 推送,由於用起來簡單,代碼量也少,JPush官網上的開發文檔也寫的至關清楚,一些步驟也清晰明瞭。今天在這邊主要就是講一下推送的原理,以及demo 測試中會遇到的問題,這也是移植到項目中易出錯的地方。java
極光推送的功能:主動 即時的向用戶發起交互,能夠發送聊天信息等;
——做用:經過向精準的目標用戶推送有價值的消息,能夠提供用戶的忠誠度,提升留存率。android
(1)推送方式
——發送通知:推送的文本內容,展現在通知欄上面;
——自定義消息:推送自定義消息,給用戶自行處理;
——富媒體:推送的是HTML網頁內容。api
(2)推送目標
——廣播推送:向全部用戶發送廣播信息;
——標籤推送 Tag:根據用戶設置自定義的標籤分組,向某一組推送消息;
——別名推送 Alias:客戶端綁定用戶自定義的用戶別名,向單個用戶推送消息。服務器
(3)用戶分羣
——用戶分羣:能夠根據JPush提供的多條件組合,對用戶進行羣組劃分,實現實時篩選推送。微信
(4)推送歷史
——推送歷史:經過WEB或者API發出的推送,均可以在推送歷史記錄中查詢到,並能夠實時顯示推送結果數據。網絡
——推送的數據源:本身開發的服務器端或者使用極光推送官網的WEB後臺;
——JPush API:部署在服務器端,開發者的服務器端發起推送時,將數據傳到JPush API中,而後向下傳遞;
——創建長連接:集成JPush的SDK客戶端啓動後會創建一個到JPush Cloud的長連接,提供App永遠在線的能力(能夠參考極光推送官方博客);
——原理圖:
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,這樣作到了電量節省。less
簡單demo容易出錯的地方
·Appcation中初始化JPush
——JPushInterface.init(this); // 初始化 JPush
·啓動的主程序裏面必定要複寫兩個生命週期:eclipse
@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,而後再移植到本身的項目中,這樣作起來比較快,也不會太亂。偏偏這個問題讓我很是痛苦,最後看了日誌才發現的,值得你們注意。
源碼下載地址 這個是我修改過得demo,只須要在AndroidManifest.xml中修改包名,以及包名的Appkey值便可,你們也能夠去官網上下載。