相信开发者们必定不陌生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值便可,你们也能够去官网上下载。