集成第三方推送最佳实践

字数6571  阅读7983  评论46  喜欢57

本文会不定期更新,推荐watch下项目。如果喜欢请star,如果觉得有纰漏请提交issue,如果你有更好的点子可以提交pull request。
本文的示例代码主要是基于作者的经验来编写的,若你有其他的技巧和方法可以参与进来一起完善这篇文章。

注意:本报告写于2016年04月25日,本文内容可能随着时间和技术的发展而产生错误和偏差。

本文固定连接:https://github.com/tianzhijiexian/Android-Best-Practices

一、需求背景

Android在国内没有办法利用系统级的推送服务,再加上第三方rom对于原生的各种定制(这里的定制并无褒贬之意)所以国内的开发者就需要选择一些成熟的推送平台做推送服务了。目前的推送平台种类繁多,本文将给出推送平台的具体对比和调研结果,方便后来人进行选择。因本人对于国外的推送服务没有了解,所以选择的是国内的推送平台。

二、需求

  • 调研国内推送平台的优劣
  • 给出技术方案和选型
  • 能快速集成推送服务

三、实现

调研信息一览

推送平台

  • 极光推送
  • 小米推送
  • 友盟推送
  • 个推
  • 阿里推送

参与测试的机型:

  • MI2 (android 5.1.1)
  • N5 (android 6.0.1)
  • MI4 (miui 7.2.4)
  • OPPO (colorOS 2.1)
  • MX4 (flymeOS 4.2.2.1c)
  • Samsung (android4.1.2)

因为测试的结果在相同的rom上表现一致,所以下文的测试结果做了简化,屏蔽了机型的细节。

调研结果

信鸽
我们的项目长期使用的是信鸽的服务。为什么写这篇文章,是因为信鸽完全不能满足高到达率的标准,所以对于腾讯信鸽,就不进行调研了。

个推
我先排除的是个推,排出它并非是因为他不好,而是因为个推本身就是收费平台,其优点就是服务全,数据颗粒度细,对开发者的响应也比较及时,但因为其收费的属性让我无法将其作为选择之一。有很多朋友也说到了,个推在用户量少的情况下是免费的。只是本文挑选的是全量免费的服务,所以就暂时不列举个推了。

阿里推送
阿里推送采用的是阿里云的一整套服务,但因为大公司的原因,所以会强制植入one sdk,这个东西真的是全家桶级别的东西,而且联系技术的方式是通过阿里旺旺让人心累。大而全的东西一向不是我的选择,而且未来阿里推送可能会有付费的趋势,因此我对它不做考虑。

友盟推送

友盟是做数据的老牌厂商,但其给我的感觉总是不舒服,之前做过友盟反馈,接入和使用总觉得不够优雅。但就推送来说,它的后台界面和提供的小工具、文档都是最美观实用的。
注意:
需要注意的是友盟的sdk引入了httpClient(官方已废弃)、okio等第三方库,我是认为完全没有必要的。我认为一个独立的sdk应该尽可能的保持轻量,过多的自带库会引起很多问题。如果你要引入友盟反馈的话,那么一定要注意它带来的几千个方法数!

推送后台

友盟采用了共享连接的技术方案,即多个采用友盟推送的App可以相互共用长连接,保证你的app被杀死后只要用户打开了采用友盟推送的app,你的app就可以利用兄弟app来接收推送。这个技术也被很多推送平台所采用,是一个很不错的方案,至于是否流氓?不可说。

共享长连接(将长连接挂在高德上)

测试结果:

  安卓原生 MIUI ColorOS flymeOS Samsung
app处于前台
返回键退出
杀掉app × × √(有延迟)
手机重启后 × × √(有延迟) √(有延迟)
打开兄弟App - × × × -

(√:能收到推送,×:收不到推送,-:无意义的测试)

测试结果表明友盟对于原生的支持较好,可以做到在原生rom上不死,在flyme上面能有一定的几率复活,只是复活后有较大几率丢失之前的消息。

需要说明的是:
本次测试在flyme上在mx4和mx5上都是通过滑动来杀掉应用的,在mx5中,如果用顶部的“扫把”进行所有应用的杀死,那么友盟推送也无法到达了。

友盟进程

内存使用情况

sdk配置文件如下:

服务

通过代码分析,友盟的一些服务是在独立的进程(push)中运行的,这也是用来提升service存活率的通用方法,因此在debug下断点的时候需要选对进程!

广播接收器

我们可以明确的知道,友盟推送在监听到(可能在不同的rom有监听不到的情况)开机、网络改变、应用卸载这样的广播后就会自动复活,以便再应用被杀死后仍旧可以实时接收远端的推送信息。

兄弟应用

在测试中我发现在MIUI上即使打开了兄弟app(高德地图),我也收不到推送。后台工具告诉我长连接的service确实挂到了兄弟app上,至于为什么没办法接收到推送就不得而知了。

最后需要说下友盟的缺点,在service被杀死后有一定概率丢失消息,刚注册的设备是无法收马上到消息的,需要过一段时间才能收到测试消息,再过一段时间才能收到正式消息,总觉得不稳定。如果一条消息很久都没有收到,那么很可能会永久丢失。
再次提醒!友盟的sdk引入了4个库!带来了好几千个方法数!引入了已经被废弃的httpClient!

小米推送

小米的推送有一个很大的优势就是在MIUI上会直接使用系统的长连接,只要系统不死,那么你的app就有90%以上的概率接收到推送。当你的MIUI用户达到了一定的量级,你就必须要使用小米的推送。

更新日志

要吐槽的是小米的推送后台(网页端)经常出现打不开的情况,复现频率极高。

  安卓原生 MIUI ColorOS flymeOS Samsung
app处于前台
返回键退出
杀掉app × ×
手机重启后 × × √(有延迟)
打开兄弟App - - × × -

(√:能收到推送,×:收不到推送,-:无意义的测试)

从结果来看小米推送是符合其文档描述的,能够在原生和MIUI上有良好的表现,所以小米推送可以被列入选择列表。

原生rom上的内存信息

MIUI上的内存信息

我发现小米在MIUI上会自动采用系统的服务,也就是说不用自身建立一个service,所以理论上要更加轻量省电。

小米推送配置文件

从代码来分析,小米会监听网络切换的广播来重启service,对于MIUI层面可能会有一个特殊的方法,不启动额外service,却可以handleMessage。

兄弟应用

对于能否通过兄弟应用来接收推送信息,我的测试结果是无法收到,不知道是不是bug。我只能说通过这样的方式来提升消息的接收率是不靠谱的。

极光推送

极光早期专注于推送服务,可以说沉淀很深,现在改名为“极光”后业务面更广了。极光对开发者提问的响应可以说是做的最好的。机关的Android文档里说到了各个功能的目的,不会像OneSDK(阿里全家桶)那样偷偷加入一些权限。文档也是走AS的风格,较为现代。不足之处就是后台比较古老,三年前就是这样丑丑的样子,发送操作有一个延迟的过程,没有友盟做的好。
极光也支持兄弟应用相互拉起的功能,从代码上看是1.8.0之后的版本开始支持的。

第三方系统收不到推送的消息的原因(摘自极光文档)
由于第三方 ROM 的管理软件需要用户手动操作
小米【MIUI】
自启动管理:需要把应用加到【自启动管理】列表,否则杀进程或重新开机后进程无法开启
通知栏设置:应用默认都是显示通知栏通知,如果关闭,则收到通知也不会提示
网络助手:可以手动禁止已安装的第三方程序访问2G/3G和WIFI的网络和设置以后新安装程序是否允许访问2G/3G和WIFI的网络
MIUI 7 神隐模式: 允许应用进行自定义配置模式,应用在后台保持联网可用,否则应用进入后台时,应用无法正常接收消息。【设置】下电量和性能中【神隐模式】

华为【Emotion】
自启动管理:需要把应用加到【自启动管理】列表,否则杀进程或重新开机后进程不会开启,只能手动开启应用
后台应用保护:需要手动把应用加到此列表,否则设备进入睡眠后会自动杀掉应用进程,只有手动开启应用才能恢复运行
通知管理:应用状态有三种:提示、允许、禁止。禁止应用则通知栏不会有任何提醒

魅族【Flyme】
自启动管理:需要把应用加到【自启动管理】列表,否则杀进程或重新开机后进程无法开启
通知栏推送:关闭应用通知则收到消息不会有任何展示
省电管理: 安全中心里设置省电模式,在【待机耗电管理】中允许应用待机时,保持允许,否则手机休眠或者应用闲置一段时间,无法正常接收消息。

VIVO【Funtouch OS】
内存一键清理:需要将应用加入【白名单】列表,否则系统自带的“一键加速”,会杀掉进程
自启动管理:需要将应用加入“i管家”中的【自启动管理】列表,否则重启手机后进程不会自启。但强制手动杀进程,即使加了这个列表中,后续进程也无法自启动。

OPPO【ColorOS】
冻结应用管理:需要将应用加入纯净后台,否则锁屏状态下无法及时收到消息
自启动管理:将应用加入【自启动管理】列表的同时,还需要到设置-应用程序-正在运行里锁定应用进程,否则杀进程或者开机后进程不会开启,只能手动开启应用

总之,它就是一个很实在的东西,对于android的权限和中国国情无能为力,它只能尽可能做到高的推送送达率。

  安卓原生 MIUI ColorOS flymeOS Samsung
app处于前台
返回键退出
杀掉app √(有延迟) × × × √(有延迟)
手机重启后 × × × √(有延迟)
打开兄弟App - × × × -

(√:能收到推送,×:收不到推送,-:无意义的测试)

关于延迟:
延迟的原因是极光的服务被杀死后,是无法接收实时消息的,直到复活后才会继续工作,所以产生了消息延迟。这点在android原生和三星的rom上有明显的规律。在三星的手机上杀掉app->关屏->点亮屏幕后服务就会自动重启,我认为这个就是服务重启的方式之一。

内存信息

配置文件

极光的配置文件中详细说明了什么是用来测试的,什么是必选的,什么是可选的,而且是否将服务放入独立进程都是一个建议选项而非强制。整体的demo和文档给人的感觉是不流氓,却无奈。

兄弟应用

我打开了极光官方提供的兄弟app,但并不能接收到我测试app的推送消息。

结论

  • 在原生和MIUI上小米推送有很强的优势
  • 在三星的ROM上小米和友盟难分伯仲
  • 在魅族的Flyme中友盟有一定概率复活
  • 在OPPO的ColorOs中三家均无突出表现

整体结论是国外的rom推送成功率高,国内rom推送成功率低,国内定制真的是伤不起啊。

技术方案和选型

因为app有大量的miui用户,所以小米推送是必须被引入的,因为在flyme上友盟的表现较好,所以最终采用了小米和友盟共同接入app的方案。目前的策略是在miui上启用小米推送,不启用友盟推送;非miui上启用友盟,废弃小米推送。

判断用户rom是否是miui的方法:

public static boolean isMiUi() {return !TextUtils.isEmpty(getSystemProperty("ro.miui.ui.version.name"));}public static String getSystemProperty(String propName) {String line;BufferedReader input = null;try {java.lang.Process p = Runtime.getRuntime().exec("getprop " + propName);input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);line = input.readLine();input.close();} catch (IOException ex) {return null;} finally {if (input != null) {try {input.close();} catch (IOException e) {e.printStackTrace();}}}return line;}

因为推送服务是采用另一个进程,所以application会被启动多次,因此必须要判断是哪个进程启动的application,这样就可以让主进程才需要的配置工作对push进程屏蔽。开发者需要做的就是判断是否是主进程,如果是那么就执行原本application中的代码。

    /*** 注意:因为推送服务等设置为运行在另外一个进程,这导致本Application会被实例化两次。* 而有些操作我们需要让应用的主进程时才进行,所以用到了这个方法*/public static boolean isInMainProcess(Context context) {ActivityManager am = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE));List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses();String mainProcessName = context.getPackageName();int myPid = Process.myPid();for (ActivityManager.RunningAppProcessInfo info : processes) {if (info.pid == myPid && mainProcessName.equals(info.processName)) {return true;}}return false;}

千万要注意!!!!!!!!
因为推送服务的复活会启动application,即唤醒应用。所以千万不要在application启动的时候进行网络请求的操作。因为一旦开始推送,大量用户的应用会被唤醒,然后再几分钟内会产生对于少数api的大量请求。后台服务很可能会被搞挂,所以千万要小心这点。这也就是为什么我强烈不推荐application中做很多事情的原因之一。可以考虑把这些请求转移到mainActivity中进行。

集成推送服务

目前我采用的是集成多个第三方推送,然后根据机型来注册不同的推送服务。后端不控制推送的平台。因为友盟的推送必须要一个module(我也不知道为啥),所以我索性把第三方推送平台都整个成了一个module。

注意
友盟推送和友盟反馈会有冲突,现在友盟反馈已经不再维护了,所以强烈不建议使用友盟反馈。而且友盟推送和阿里的产品有冲突,所以如果产生了冲突,要下载去UTDID的SDK。

友盟和阿里冲突解决

友盟反馈和友盟推送冲突的解决方案(我才知道友盟反馈原来偷偷集成了推送服务):

compile('com.umeng:fb:5.4.0') {   transitive = true   exclude group: 'com.umeng', module: 'message'
}

module一览

一览

libs中集成了友盟所需要的so包(引入一个友盟的推送,要引入一堆东西,还各种冲突,这些so全是友盟的),此外还有小米的一个jar包。

一般我们不会把so放在lib中,都会采用新建一个main/jniLib文件夹来放,友盟采用了另一种方法来方,因此在build.gradle中出现了这样一段代码:

gradle

友盟文档中说,要引入我的推送,你就必须引入httpClient,因此我们必须在build.gradle中引入已经被谷歌废弃的httpClient!!!!!:

android {compileSdkVersion 23buildToolsVersion "23.0.3"// http://dev.umeng.com/push/android/integration// 如果是android6.0以上的api编译,需要在PushSDK的build.gradle文件的android{}块内添加:// useLibrary 'org.apache.http.legacy',并把compileSdkVersion的版本号改为23。useLibrary 'org.apache.http.legacy'defaultConfig {minSdkVersion 8targetSdkVersion 23}
}

友盟还使用了square家的东西,所以必须要引入以下两个依赖:

Paste_Image.png

到此为止,我终于引入了友盟的sdk。小米的sdk我就不说了,上面的图中也有,一个jar包搞定。

Manifest中的权限

因为各大推送平台都有自己的权限,但是如果混合到一起,以后想删除某个平台的代码都很难删,最终也不知道整个权限是谁加入的,有什么作用。因此我把各个平台的权限做了独立,这样做虽然在编译的时候会有重复定义权限的警告,但是并不影响最终的包,这点我通过反编译已经证实了,请放心。

    <!-- umeng必选 --><uses-permission android:name="android.permission.WRITE_SETTINGS" /><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WAKE_LOCK" /><uses-permission android:name="android.permission.READ_PHONE_STATE" /><uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /><uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /><uses-permission android:name="android.permission.GET_TASKS" /><uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /><!-- umeng可选 --><uses-permission android:name="android.permission.BROADCAST_PACKAGE_ADDED" /><uses-permission android:name="android.permission.BROADCAST_PACKAGE_CHANGED" /><uses-permission android:name="android.permission.BROADCAST_PACKAGE_INSTALL" /><uses-permission android:name="android.permission.BROADCAST_PACKAGE_REPLACED" /><uses-permission android:name="android.permission.RESTART_PACKAGES" /><uses-permission android:name="android.permission.GET_ACCOUNTS" /><uses-permission android:name="android.permission.VIBRATE" /><uses-permission android:name="android.permission.SEND_SMS" /><uses-permission android:name="android.permission.RECEIVE_SMS" /><uses-permission android:name="android.permission.READ_SMS" /><!--小米推送开始--><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><uses-permission android:name="android.permission.READ_PHONE_STATE" /><uses-permission android:name="android.permission.GET_TASKS" /><uses-permission android:name="android.permission.VIBRATE" /><permissionandroid:name="${applicationId}.permission.MIPUSH_RECEIVE"android:protectionLevel="signature"/><uses-permission android:name="${applicationId}.permission.MIPUSH_RECEIVE" /><!--小米推送结束-->

关于Mainfest中的key

在配置文件中因为用到了key和具体项目的包名,而我希望整个push的module保持独立性,所以我最终将key用代码进行写入,而不是采用在manifest中定义data标签的方式。

编写推送管理类

因为集成了多个平台,所以东西多,东西一多就需要管理类。管理类做的事情很简单,注册服务,接收推送消息,展示通知。

/*** @author Kale* @date 2016/4/27*/
public class PushManager {private static final String TAG = "PushManager";public static String tokenType; // token类型的缓存public static String token; // token的缓存@Retention(RetentionPolicy.SOURCE)@StringDef({PushType.MI, PushType.UMENG})public @interface PushType {String MI = "MI", UMENG = "UMENG";}public static void register(Context context) {if (Utils.isMiUi()) {registerMiPush(context);} else {registerUMengPush(context);}}/*** 注册小米push服务* 注册成功后会向{@link MiMessageReceiver}发送广播*/private static void registerMiPush(Context context) {if (BuildConfig.DEBUG) {MiPushClient.checkManifest(context);}final String APP_ID = "xxxxxxxxxxxxxx";final String APP_KEY = "xxxxxxxxxxxxxx";if (DTUtil.isInMainProcess(context)) {MiPushClient.registerPush(context, APP_ID, APP_KEY);}LoggerInterface newLogger = new LoggerInterface() {@Overridepublic void setTag(String tag) {// ignore}@Overridepublic void log(String content, Throwable t) {P.e(content, t); // log}@Overridepublic void log(String content) {P.d(content); // log}};Logger.setLogger(context, newLogger);if (!BuildConfig.DEBUG) {Logger.disablePushFileLog(context);}}/*** 注册友盟的推送服务*/private static void registerUMengPush(final Context context) {PushAgent agent = PushAgent.getInstance(context);// 如果你用了友盟数据分析,那么这个key和友盟数据分析保持一致final String appKey = AnalyticsConfig.getAppkey(context); final String appSecret = "xxxxxxxxxxxxxx";agent.setAppkeyAndSecret(appKey, appSecret);agent.setMessageChannel(AnalyticsConfig.getChannel(context));agent.setDebugMode(BuildConfig.DEBUG);agent.setPushCheck(BuildConfig.DEBUG);IUmengRegisterCallback callback = new IUmengRegisterCallback() {@Overridepublic void onRegistered(final String deviceToken) {new Handler().post(new Runnable() {@Overridepublic void run() {//onRegistered方法的参数registrationId即是device_tokensendDeviceToken(context, PushType.UMENG, deviceToken);handleRegisterResult(context, PushType.UMENG, true, null);}});}};agent.enable(callback);// 通过两种方式获得token,这里确保尽可能能获得token// 第一次获得是需要联网的,获得后友盟会存放在sp中,因此产生两种方法。String deviceToken = UmengRegistrar.getRegistrationId(context);// 得到deviceTokenif (deviceToken != null) {callback.onRegistered(deviceToken);}agent.setResourcePackageName("com.duitang.main");// 下面这段代码必须在application中调用agent.setMessageHandler(new UmengMessageHandler() {/*** 参考集成文档的1.6.3* http://dev.umeng.com/push/android/integration#1_6_3** 若开发者需要处理自定义的透传,则需要将方法dealWithCustomMessage()重写,自定义消息的内容则存放在UMessage.custom里* */@Overridepublic void dealWithCustomMessage(final Context context, final UMessage msg) {// http://dev.umeng.com/feedback/android/integrationif (FeedbackPush.getInstance(context).dealFBMessage(new FBMessage(msg.custom))) {// 如果是开发者回复友盟反馈的消息,那么就直接returnreturn;}new Handler().post(new Runnable() {@Overridepublic void run() {// 处理透传消息PushNotificationUtil.handlePassThroughNotify(context, PushType.UMENG, msg.custom);}});}});agent.setNotificationClickHandler(new UmengNotificationClickHandler() {/*** 点击推送后会触发的事件*/@Overridepublic void dealWithCustomAction(Context context, UMessage message) {super.dealWithCustomAction(context, message);// 解析信息PushNotificationUtil.MsgBean bean =PushNotificationUtil.parsePushMsg(message.custom);if (bean.target != null) {Intent intent = PushNotificationUtil.getPushIntent(context, bean.target);context.startActivity(intent);}}});}/*** 目前一个设备仅仅有一个token,所以用了静态的string做缓存;* 做如果以后有多个token,那么把{@link PushManager}变成单例,存一个token的map即可。*/public static void sendDeviceToken(final Context context) {sendDeviceToken(context, PushManager.tokenType, PushManager.token);}public static void sendDeviceToken(final Context context, @PushType String type, String token) {if (token == null) {return;}PushManager.tokenType = type;PushManager.token = token;// todo 发送token给服务器}public static void handleRegisterResult(Context context, @PushType String type,boolean isSuccess, @Nullable String resultMsg) {if (isSuccess) {// todo} else {// todo}}}

小米推送信息接收器:

/*** @author Kale* @date 2016/4/27** http://dev.xiaomi.com/doc/?p=544*/
public class MiMessageReceiver extends PushMessageReceiver {/*** 通知消息被点击时触发*/@Overridepublic void onNotificationMessageClicked(Context context, MiPushMessage msg) {try {PushNotificationUtil.MsgBean bean = PushNotificationUtil.parsePushMsg(URLDecoder.decode(msg.getContent(), "UTF-8"));if (bean.target != null) {Intent intent = PushNotificationUtil.getPushIntent(context, bean.target);context.startActivity(intent);}} catch (UnsupportedEncodingException e) {e.printStackTrace();}}/*** 用来接受客户端注册的结果*/@Overridepublic void onReceiveRegisterResult(Context context, MiPushCommandMessage message) {String command = message.getCommand();List<String> arguments = message.getCommandArguments();String cmdFirstArg = ((arguments != null && arguments.size() > 0) ? arguments.get(0) : null);// 如果是注册的命令,那么就得到tokenif (MiPushClient.COMMAND_REGISTER.equals(command)) {PushManager.sendDeviceToken(context,PushManager.PushType.MI, cmdFirstArg); // add tokenlong code = message.getResultCode();PushManager.handleRegisterResult(context, PushManager.PushType.MI, code == ErrorCode.SUCCESS, cmdFirstArg);}}/*** 接收透传消息*/@Overridepublic void onReceivePassThroughMessage(Context context, MiPushMessage message) {try {PushNotificationUtil.handlePassThroughNotify(context, PushManager.PushType.MI,URLDecoder.decode(message.getContent(), "UTF-8"));} catch (UnsupportedEncodingException e) {e.printStackTrace();}}}

PushNotificationUtil的代码就不贴了,不同的项目会有不同的实现形式。至此,推送的集成工作已经完毕!

注意
在miui上,如果应用被杀死,即使用了小米推送,透传消息也是无法收到的。
可以考虑采用非透传的模式进行推送,简单方便,而且自带点击统计。至于是否采用非透传,就看项目的设计和需求了。

总结

写到这里,在回过头来看,这篇真的是所谓的“最佳实践”么?或许是,也或许不是,其中更多的是一种妥协和无奈。技术本无罪,但它终究敌不过金钱与利益,毕竟操作技术的是人,那么技术也免不了人间的烟火气。
我们仅仅是希望更多的用户能接收到我们的推送,但在实现的路上却做了很多的妥协和权衡,甚至寄希望于不死的服务。这很可悲,但也很现实。
转念一想,在妥协和容忍中不断的寻找着实现梦想的最好的方式,这不就是人生么?是啊,这就是人生。

developer_kale@foxmail.com

  • android整合小米、个推、jpush推送 - yuanfen934的博客 - 博客频道 - CSDN.NET

    — 《CSS3实战》总结 box-flex属性

    天之界线2010・  blog.csdn.net →
  • 常见问题 - 极光文档
    天之界线2010・  docs.jpush.io →
  • 友盟推送SDK文档

    — 友盟消息推送——中国最精准的移动推送服务,支持Android、iOS推送。友盟推送帮助开发者建立于用户直接沟通的渠道。将APP的内容更新或者活动通知主动推送给终端用户,让用户第一时间获取到相关信息,有效提升用户活跃度和忠诚度。

    天之界线2010・  dev.umeng.com →

集成第三方推送最佳实践相关推荐

  1. Android第三方推送到达率调研

    由于众所周知的原因,国内APP不能使用谷歌官方推送GCM,同时国内android系统几乎都经过厂商的深度定制,对于后台应用的管控严格,导致推送到达率很低,现对市面上一些主流第三方推送(非所有)做一个调 ...

  2. Android第三方SDK集成 —— 极光推送

    前言: 本文前篇,可以帮助朋友们快速集成极光推送.本文后篇,是我自己项目实践的一些总结和心得,应该对读者们还是很有参考价值的,相信读完这篇文章,你会对极光推送有更加深入的理解,而不仅仅只是会集成而已. ...

  3. android应用接入第三方推送实践

    本文由来和目的 由来:昨天技术群里有人问到推送,作为一个亲自踩过坑的的人,回答了一些问题,有人就说,你应该写一篇博客啊.其实在做完公司的项目集成之后,是想写的,但是因为懒就一直没写,但昨天经人这么一说 ...

  4. Android开发之第三方推送JPush极光推送知识点详解 学会集成第三方SDK推送

    作者:程序员小冰,CSDN博客:http://blog.csdn.net/qq_21376985 下面是一些知识点介绍,后期将会带领大家进行代码实战: 一.Android实现推送方式解决方案: 1.推 ...

  5. 三分钟帮你集成极光推送——和那些可能你不知道的事

    本文简介:本文前篇,可以帮助朋友们快速集成极光推送.本文后篇,是我自己项目实践的一些总结和心得,应该对读者们还是很有参考价值的,相信读完这篇文章,你会对极光推送有更加深入的理解,而不仅仅只是会集成而已 ...

  6. IM篇 I (╯‵□′)╯︵┻━┻ 极光?个推?腾讯云?第三方推送平台该怎么选?

    推送的意义 「推送机制」为我们提供了一条可以「直接触达用户」的路径,使移动设备终端的「全时信息传播」成为可能,是C端运营人员提高用户活跃度.提高应用留存率的常规途径之一,以助于其「更高效地完成运营目标 ...

  7. flutter应用开发中集成极光推送(flutter极光推送第一篇)

    题记 -- 执剑天涯,从你的点滴积累开始,所及之处,必精益求精,即是折腾每一天. 重要消息 精通点的可以查看这里 精述 Flutter 从入门实践到开发一个APP之UI基础篇 视频 flutter从入 ...

  8. (4.2.12)浅谈第三方推送[推送基础]:百度推送、小米推送、华为推送

    市面上目前做免费推送服务的有很多,友盟.极光.百度.小米.华为等,由于android机型的多样性,在使用单独的一种推送时,往往会造成一些机型无法获取(当然,内部原因可能很复杂). Android 第三 ...

  9. 个推成为首家支持统一推送接口标准的第三方推送服务商!

    2017年10月16日,中国信息通信研究院泰尔终端实验室联合各大手机厂商和提供推送服务的互联网厂家成立"统一推送联盟",希望通过标准化的方式统一推送通道和接口,以降低终端功耗,提升 ...

最新文章

  1. Android开源项目SlidingMenu本学习笔记(两)
  2. Docker常用基础命令汇总
  3. 线程安全的ConcurrentQueueT队列
  4. string和C语言字符串之间的相互转换以及string常用函数
  5. Unity将来时:IL2CPP是什么?
  6. 在django中使用celery
  7. 服务器程序的Xamarin-Java.Interop体验(一)
  8. makefile中的shell调用---注意事项
  9. 【华为云技术分享】如何拆分用户故事
  10. STP RSTP MSTP PVST+学习 (1)
  11. 归并排序算法(java实现)
  12. python练手程序之猜大小
  13. RTOS成功取代Linux成为天猫精灵OS的关键 -- AliOS Things 维测专题
  14. mac百度网盘加速下载
  15. 阿里云GPU计算型实例规格族gn6i配置性能详解
  16. 9 EDA实用技术与教程【Verilog深入】
  17. 微软拟用DNA存储数据:一段就能顶一个数据中心
  18. Arcmap操作系列12:统计分析工具对点抽稀
  19. 音质好的入耳式耳机排行
  20. 杂记-有关Ubuntu快捷方式不能正常打开的问题

热门文章

  1. 零基础如何入门学CG原画?
  2. Temporary failure resolving ‘archive.ubuntu.com‘
  3. 这家公司要用6美元的午餐赢取西雅图吃货们的心
  4. nginx服务器资源路径配置文件,查看nginx配置文件路径和资源文件路径的方法
  5. 初识JS_API基础Day4——笔记整理+JS运行机制+1课后作业+蓝瘦香菇...
  6. java猜十二生肖,十二生肖对冲
  7. 3.4企业实训 网页开发
  8. 嵌入式,单片机,ARM,Android底层开发之间的关系
  9. ELK+kafaka+filebeat实现系统日志收集与预警
  10. Python导入模块3种方法