一、 概念:
极光推送(JPush)是一个端到端的推送服务,使得服务器端消息能够及时地推送到终端用户手机上,让开发者积极地保持与用户的连接,从而提高用户活跃度、提高应用的留存率。极光推送客户端支持 Android, iOS 两个平台。
1.主要功能
(1)保持与服务器的长连接,以便消息能够即时推送到达客户端
(2)接收通知与自定义消息,并向开发者App 传递相关信息
2.主要特点
(1)客户端维持连接占用资源少、耗电低
(2)SDK丰富的接口,可定制通知栏提示样式
(3)服务器大容量、稳定
二、 极光推送技术原理:移动无线网络长连接
1.为了保证数据能及时到达客户端。常用的方法有2种。
(1)定时去服务器上查询数据,也叫Polling,
(2)手机跟服务器之间维护一个 TCP 长连接,当服务器有数据时,实时推送到客户端,也就是我们说的 Push。电量、流量和数据送达的及时性来说,Push 都会有明显的优势,Push 的实现和维护成本相对较高。移动无线网络的特点
2. Android 平台上长连接的实现
因为 IP v4 的 IP 量有限,运营商分配给手机终端的 IP 是运营商内网的 IP,手机要连接 Internet,就需要通过运营商的网关做一个网络地址转换(Network Address Translation,NAT)。简单的说运营商的网关需要维护一个外网 IP、端口到内网 IP、端口的对应关系,以确保内网的手机可以跟 Internet 的服务器通讯。
NAT 功能由图中的 GGSN 模块实现。大部分移动无线网络运营商都在链路一段时间没有数据通讯时,会淘汰 NAT 表中的对应项,造成链路中断。Android 平台上长连接的实现,为了不让 NAT 表失效,我们需要定时的发心跳,以刷新 NAT 表项,避免被淘汰。Android 上定时运行任务常用的方法有2种,一种方法用 Timer,另一种是AlarmManager。Android 的 Timer 类可以用来计划需要循环执行的任务,Timer 的问题是它需要用 WakeLock 让 CPU 保持唤醒状态,这样会大量消耗手机电量,大大减短手机待机时间。这种方式不能满足我们的需求。AlarmManager 是 Android 系统封装的用于管理 RTC 的模块,RTC (Real Time Clock) 是一个独立的硬件时钟,可以在 CPU 休眠时正常运行,在预设的时间到达时,通过中断唤醒 CPU。这意味着,如果我们用 AlarmManager 来定时执行任务,CPU 可以正常的休眠,只有在需要运行任务时醒来一段很短的时间。极光推送的 Android SDK 就是基于这种技术实现的。
3.服务器设计
当有大量的手机终端需要与服务器维持长连接时,对服务器的设计会是一个很大的挑战。针对这个问题,我们专门成立了一个项目,命名为C2000K,顾名思义,我们的目标是单机维持200万个长连接。最终我们采用了多消息循环、异步非阻塞的模型,在一台双核、24G内存的服务器上,实现峰值维持超过300万个长连接。只要 JPush 的网络连接是正常的,则:JPush 收到消息一定是及时的。其延迟是秒级的,一般在 1 秒之内。如果超过 10 秒,则一定是客户端网络出了问题。手机休眠时,也能够及时地收到推送消息。
三、Android SDK 概述
开发者集成 JPush Android SDK 到其应用里,JPush Android SDK 创建到 JPush Cloud 的长连接,为 App 提供永远在线的能力。
当开发者想要及时地推送消息到达 App 时,只需要调用 JPush API 推送,或者使用其他方便的智能推送工具,即可轻松与用户交流。
图中红色部分,是 JPush 与 App 开发者的接触点。手机客户端侧,App 需要集成 JPush SDK;服务器端部分,开发者调用 JPush REST API 来进行推送。
四、 Android SDK 集成指南
一、极光推送SDK集成步骤:
1.导入SDK开发包到你自己的应用程序项目
(1)登陆后,创建应用
(2)下载SDK
(3)创建一个新应用
A.将SDK中的JAR包拷入项目。
复制动态库文件
复制res下drawble中的文件到项目中,复制layout文件夹下的东西到项目中。
2.配置AndroidMainfest.xml文件
A.配置权限:
<!-- Required -->
<permission
android:name="com.example.j.permission.JPUSH_MESSAGE"
android:protectionLevel="signature"/>
<!-- Required -->
<uses-permission android:name="com.example.j.permission.JPUSH_MESSAGE"/>
<uses-permission android:name="android.permission.RECEIVE_USER_PRESENT"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
B:配置application中的内容
<!-- Required SDK核心功能-->
<receiver
android:name="cn.jpush.android.service.PushReceiver"
android:enabled="true">
<intent-filter android:priority="1000">
<action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED_PROXY"/>
<category android:name="您应用的包名"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.USER_PRESENT"/>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</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>
<!-- Required SDK核心功能-->
<activity
android:name="cn.jpush.android.ui.PushActivity"
android:configChanges="orientation|keyboardHidden"
android:theme="@android:style/Theme.NoTitleBar"
android:exported="false">
<intent-filter>
<action android:name="cn.jpush.android.ui.PushActivity"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="您应用的包名"/>
</intent-filter>
</activity>
<!-- Required SDK核心功能-->
<service
android:name="cn.jpush.android.service.DownloadService"
android:enabled="true"
android:exported="false">
</service>
<!-- Required SDK核心功能-->
<receiver android:name="cn.jpush.android.service.AlarmReceiver"/>
C:复制元数据部分
<!-- Required SDK核心功能-->
<receiver android:name="cn.jpush.android.service.AlarmReceiver"/>
<!-- Required. For publish channel feature -->
<!-- JPUSH_CHANNEL 是为了方便开发者统计APK分发渠道。-->
<!-- 例如: -->
<!-- 发到 Google Play 的APK可以设置为 google-play; -->
<!-- 发到其他市场的 APK 可以设置为 xxx-market。 -->
<!-- 目前这个渠道统计功能的报表还未开放。-->
<meta-data android:name="JPUSH_CHANNEL"android:value="developer-default"/>
<!-- Required. AppKey copied from Portal -->
<meta-data android:name="JPUSH_APPKEY"android:value="您应用的Appkey"/>
3.添加初始化代码
(1)创建App这个类,并在application中进行配置。
(2)配置gradle.
4.测试是否成功,
五、推送常识问题:
1. 其正常工作的必要条件:JPush SDK 与 JPush Server 的网络保持着连接。
2.手机休眠时收不到 JPush 消息,解锁或屏幕灯亮则可以成功接收。这个现象表明,手机休眠时,JPush SDK “被迫”与服务器端的网络失去了连接。
3.JPush SDK 的工作原理是要确保在手机休眠时也能正常的工作,即休眠时也可以及时地收到Push消息。这个“被迫”,是由 Android 设备的环境所导致的。涉及的原因有如下几个方面:
(1)手机本身的网络设置。标准版本的 Android ROM 是没有这个设置的,但某些特殊的 ROM 可能会有这方面的设置。
(2)手机上的安全、省电工具软件额外做的事情.
上述的特殊机制会关闭网络。网络一旦连接上,JPush也会连接上服务器,从而Push消息就会收到。
4.有时候收到 JPush 消息很及时,有时候则要等几分钟
JPush 会监听网络切换广播。当网络关闭时,把原来JPush连接关闭。当有新的网络时,创建JPush连接。另外,RTC会定时发送心跳。如果之前的网络已经断了,则会重新连接。
应该说,当前的网络连接策略还是相对简单的,这样做的目的是:省电、省流量。不好之处就是:网络没有切换时,因为当时网络过差,JPush连接会被中断。这种情况下,就只能等 RTC 心跳去触发连接。这也是有时候JPush 无法及时接收Push消息的原因。根据网络条件的不同,出现这个情况的概率也会不同。但据我们自己的测试,90% 的时候是可以及时地收到Push消息的。
JPush 目前在网络策略方面没有像微信这种聊天工具做得积极。如果这样做到,电量和流量的消耗必然会成倍地增加。
5.完全收不到 JPush 消息
如果集成之后就完全收不到Push消息,则很有可能是某个地方配置错误。