市面上目前做免费推送服务的有很多,友盟、极光、百度、小米、华为等,由于android机型的多样性,在使用单独的一种推送时,往往会造成一些机型无法获取(当然,内部原因可能很复杂)。
Android 第三方 Push 推送方案使用调查
因此往往需要同时集成多个第三方推送,并且能做到:
目前,采用了百度、小米和华为的推送SDK,层次上以百度为主覆盖大量机型,小米和华为为辅覆盖专有机型。
三种SDK在基本的使用方法上大同小异,现简单加以说明:
在移动互联网时代,移动端的开发都必须涉及 客户端/服务端的概念,但是从字面上看,服务端和客户端是完全独立的两套逻辑
客户端可以主动的向服务端请求数据,譬如最常见的登录,用户输入账户密码,点击确定按钮—客户端主动把信息抛给给服务端,并等待服务端返回—-服务端返回校验状态–客户端拿到,并呈现相应的UI状态
注意,上述流程的起点是客户端主动发起请求那么问题来了,一旦服务端的数据发生了变更,如何主动告知客户端
那么问题来了,一旦服务端的数据发生了变更,如何主动告知客户端呢? 譬如,你的服务器收到了一个紧急邮件,但是你没在电脑前边,服务器如果通知到你呢?
这种服务器主动发起的信息交互,就是推送
关于推送的具体概念和不同实现方式,可以自行查看http://www.cnblogs.com/liangqihui/p/3539984.html
在进行一下概念之前,先声明一点,也存在一些app的端内推送和端外推送是混合在一起的,譬如完全使用第三方的推送构建自己的聊天或业务逻辑,再譬如完全不依赖第三方,把自己的端内推送做的牛逼到可以用作端外
其实这些概念的界限很模糊,我们就kdzl本身来谈一下:
端内推送和端外推送是一对性质相同的概念,这里的端其实指的就是我们的应用app本身
第三方推送则是一个独立的概念,主要描述了推送的一种实现方式,主要涉及推送整个流程中是否涉及第三方的服务器
好了,我们结合实际的kdzl分析下:
现在可以看出来,kdzl的推送分为了两种,而且基本是相互独立的,这点大家要先明白
“第三方推送”向开发者提供的消息推送服务;通过利用云端与客户端之间建立稳定、可靠的长连接来为开发者提供向客户端应用推送实时消息服务。
为了使用第三方推送,往往需要继承对应第三方平台的相关资源文件,这些文件一般被封装为一个SDK,借助SDK,应用的开发者可以通过少量的代码,就可以将推送服务接入的APP应用中,同时可选择使用(1)自己搭建服务器使用对应第三方平台服务API 或者(2)直接使用第三方平台服务控制台的方式进行通知信息的发送。
为了实现推送服务,实质上需要以下4个模块:
App应用(App Client)
自己开发的应用,需要接入push,只需要和pushService打交道提出请求即可
pushService(Push Client)
运行在手机上的推送服务,直接代理App应用与“第三方推送服务器”打交道
用于与服务器建立稳定push通道,上报标签、 LBS信息,通知推送(含富媒体)等
集成SDK后,一般由SDK维护
第三方推送服务器(推送控制台,Push Server)
pushService监听的服务器,该服务器用于发送通知或者信息,同时也会处理设置标签、用户记录等操作
自己的业务服务器(App Server)
实现自己的业务功能,往往需要接入“第三方推送服务器”所设定的一些API,从而可以自动的发送通知,而不再需要人工登录“推送控制台”进行信息推送
请求TMID—-注册请求,委托推送服务去开启推送服务
一般在app启动(activity或者application的onCreate等函数中,不确定,不同平台有不同设定)时,通过调用对应的SDK方法,请求PushService,为当前设备注册并开启推送服务
请求分配TMID—-接收到应用请求,向“第三方服务器”发起注册请求
该方法一般由SDK默认实现和控制,应用开发者只需要在步骤1中调用对应的接口方法后,pushservice会向服务器提出申请
该申请一般是异步申请
TMID应答
该pushService一直运行在手机中,该service在接收到“第三方服务器”给出的应答后,会发送广播,通知对应的接受者,并转发信息
该方法一般由SDK默认实现和控制,
TMID响应
App应用往往需要继承SDK提供“receiver接收器”,并进行注册。
pushService发出的广播,会被所继承的“接收器”中的对应方法接收,并调用
上报TMID
App应用获取到“第三方推送服务器”分配给自己的TMID时,需要将TMID上传给“自己的业务服务器”,从而可以长久保存该ID,并且告知业务服务器通过该TMID,可向对应的业务用户发送通知。
譬如,1:1的聊天信息,需要做定向推送,A发送给B的信息,需要得知B的TMID码
再譬如,非定向信息,覆盖全部用户的热定新闻通知,不需要TMID码,全部推送即可。然而实际上,虽然“业务服务器”没有给出TMID码,其实是第三方服务器对“所有该App的TMID码”进行了推送。实质上和上个例子一样
我们以“1:1的聊天信息”为例,A发送给B信息:
根据TMID推送信息
业务服务器调用“第三方平台”给出的对应API方法,向“推送服务器”发出推送信息的申请,定向推送需要给出B用户的TMID码
根据TMID找到手机并下发
第三方的推送服务器会通过TMID码找到对应的手机,并定向发出推送信息
PushService转发信息
该service一直运行在手机中,监听“ 第三方的推送服务器”的推送,获取到推送信息, 会发送广播,通知对应的接受者,并转发信息
信息响应
App应用往往需要继承SDK提供“receiver接收器”,并进行注册。
pushService发出的广播,会被所继承的“接收器”中的对应方法接收,并调用。
可以在该方法中实现自己的业务操作。
App应用中设定标签,设定别名等操作的过程,与注册流程基本一致
为什么百度排第一呢,后台和客服反映说百度推送一堆问题,为什么还是采用百度第一呢? 原因两点:
一直想找个机会,好好的分析下,这些很浅显的问题:
如果,我们非要从一个外在形式上来评判一个应用是Alive还是Deaded,那么我们可以使用当前进程的Application是否存在作为评判,那么唤醒一个应用其实就是意味着做了初始化Application的行为:
事实上,我们经常说“有些应用会后台自启动”,“后台偷跑流量”,这里所谓的自启动,其实并没有启动任何一个Activity,也就是根本没有构建Task,而只是启动了某个Service或者Receiver,从而势必引发至少一个进程和对应Applicaiton,我们视作Alive状态
好了,现在我们得知Application可以作为应用是唤醒状态Alive 或者 死亡deaded状态 的标准了:
那么,应用被唤醒了,就意味着 应用可能维持的通信长连接已经建立了么?
相信看到这里你基本也清楚了,应用被唤醒 与 应用具体的业务逻辑没有半毛钱关系(长连接的建立也属于业务逻辑分部),如果你想实现某个业务部分,那就必须在被唤醒的模块中 调用或者初始化
如何唤醒被杀死的android app
上篇文章里其实也有提及,唤醒应用的方式
在本处,我们主要关注下,广播这一块。我们知道,第三方推送SDK维护的PushService其实往往是运行在一个单独的进程中的(但是很大可能和app主进程同组,这点注意下,一会会提及) ,这个进程和我们应用app进程是分割开的
我们注册推送SDK的时候,所有的官方文档都会让我静态一个Receiver类,作为监听器。回顾下上文提到的“推送流程 3-4”,PushService维持一个长连接,如果收到了来自PushServer的信息,则跨进程的发送广播给我们的应用,试图唤醒我们的的应用
我们可以大胆猜测,这种inten方式应该如《如何唤醒被杀死的android app》一文中所讲:
我们一般发广播都是局限在app内部,所以通常都是这么发的:
Intent intent = new Intent();
intent.setAction("my.broadcast.test");
sendBroadcast(intent);
或者这么发:
Intent intent = new Intent(context, TestBroadcastReceiver.class);
sendBroadcast(intent);
静态的系统广播,例如:开机广播,用户开屏广播,USB插入和拔出广播等,在app运行期间可以用静态注册的接收器正常接收,但是在app被杀死后就无法收到了,Android系统做了屏蔽,把被杀死的app的系统静态广播都过滤了,所以想让app被杀死后仍然通过静态注册的接收器接收系统广播是做不到的
静态注册的自定义广播也会遇到类似问题,尤其是在定制版的系统中
采用下面这种方式发送广播即使app被杀死后,静态广播也能正常收到:
Intent intent = new Intent();
Context c = null;
try {
c = createPackageContext("com.example.broadcasttest", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
// intent.setPackage(getPackageName());
// intent.setComponent(pkgName, className);
// intent.setComponent(pkgNameContext, className);
intent.setClassName(c, "com.example.broadcasttest.TestBroadcastReceiver");
// intent.setClassName("com.example.broadcasttest", "com.example.broadcasttest.TestBroadcastReceiver");
intent.setAction("my.broadcast.test");
intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(intent);
接收放广播的配置(app内的自定义推送Receiver)要把exported设置成true,否则就无法收到app以外的广播发送,只能收到app内部的广播发送
正如文中所说:
以上通过广播唤醒在一些手机上可以正常唤醒app,例如小米3;但是在魅族手机上就没办法唤醒了,需要到安全中心把app的自启动权限开启后才能正常唤醒,由此可见,一些手机厂商可能对于静态广播的接收做了一些优化导致静态广播还是没办法被接收,所以会唤醒失败
即便 intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
相信到这里大家也有所明悟了,为什么推送有时候收不到,我们让客户开启自启动权限,就可以收到了,原因如下:
App的自启动权限,会限制 PushService的自启动权限;
App的自启动权限,会影响自身被唤醒的限制,也就是receiver的正常接收
这也是为什么有时开启app的自启动权限,就能导致第三方推送恢复正常的原因了。 至于app被唤醒后,触发通知栏,但是通知栏信息未显示的原因,我们在后续文章中会讲解。
信息推送是一个动词,我们上边主要关注了“推送”这个动作的实现方式,我们知道了实现这个动作所需要的 四大天王,缺一不可
那么作为这个动作的受体,到底是什么呢?
我们可以简单地认为是一串文本信息,譬如一句话: “你好”
到这里,我们回归下最开始的意图,我们使用第三方推送,是期望能在手机的通知栏弹出一栏信息,根据 执行弹出动作的主体可以分为两种类型:
相对而言,透传信息的自由度更高,我们可以自行处理收到信息后的状态,譬如弹窗,开启引用,或者不弹窗
我们现在在百度和华为平台上使用了透传信息,而小米则使用了通知
其实这和我们之前说的“非典型交互流程”有关
我们知道, pushservice 在收到信息后,会发送广播,试图唤醒我们的应用,然后将信息传递过去
那么唤醒不成功呢? 自然无法将信息传递过去,应用本身不知道信息,处于dead状态,自然就不会弹窗提醒了
然而通知类型则不需要唤醒了,因为弹窗动作直接由pushservice完成啦,管你app是死是活,都能弹出来提示
而在小米手机上,pushservice是一种系统支持的服务,你杀都杀不死,所以我们在一般在小米手机上使用通知方式
很简单呀,一旦使用通知,相当于把全部责任给 pushservice, 如果pushservice越坚挺,我们就都使用通知
然而实际上,只有小米和华为这种手机厂商自拟的pushservice,在自己手机上 pushservice 才有这么牛逼
因为,是小米手机我们就用小米推送
华为的透传信息也是系统级别的,能保持 pushservice 坚挺就足够了,,,我们还是期望多些自由度
肯定不是了,弹窗只是一个动作,很有可能被“通知中心”之类的限制,你弹,它限制,你弹,它限制,自然出不来了