无意间看到了友盟微信公众号的一篇推送,文章名字叫“你的消息到达率是多少?“,这是一篇关于友盟推送的消息推文,大概说明了友盟推送消息送达到手机的过程中的有关影响因素。
然后总结出:提高手机收到率的三个方法:
三、提升收到率的方法:
方法一:提高App的在线活跃用户数;对于Android平台,收到率其实都是和App自身日活比例呈正相关的,App日活度不高,那么收到率也不会太高。
方法二:增加离线App的消息推送;通过集成厂商通道,可将消息直接发送到手机厂商的系统通道中,增加对App离线这部分有效用户的实际下发数,提高整体的“消息到达率”。到目前为止友盟+的消息推送提供了小米、华为、魅族、OPPO、vivo五个主流手机厂商通道。
方法三:延长消息有效期;比如设置12H和24H的有效期,消息下发数以及送达App数肯定是不一样的。消息有效期越长,消息收到数会越多。U-Push默认发送时间是72H。
下面我们就来说明一下消息送达的过程:
从上图我们可以知道,我们广播推送消息都是先由我们集成了友盟SDK的后台服务器首先推送消息到我们注册申请的友盟推送服务器后台,然后再由友盟推送服务器后台把消息推送到我们的APP的手机上。我们手机上因为也集成了友盟提供的SDK,这个SDK的作用就是在后台和友盟推送服务器的后台建立一条HTTP的数据长链接,这个链接是长期存活的。
这样就存在一个各种终端设备实现推送服务的方式的统一性问题,后由工信部联合各大手机生产厂商统一了一套安卓统一推送服务(Unified Push Service,简称UPS)技术标准,旨在为国内的消息推送服务建立统一的标准,为终端用户提供更好的手机使用体验,为应用开发者更好解决消息推送需求,并取得了阶段性成果。
统一推送对于开发者的一个福音是,由于推送 API 的统一,未来各终端厂商将提供系统级 API 实现推送功能(即App无需嵌入各通道SDK)。考虑到实际情况,为了兼容已有机型,手机端还是会提供一个简单的SDK,判断手机是否支持统一推送。若支持则可以直接调用 ROM API ,否则按照当前已有方式进行推送(为了适配已有机型还需要保留推送SDK)。随着手机的自然更替,未来支持统一推送的终端数目会不断更加,从而逐步实现统一推送的平滑演进。
下面我们就来说明一下各大手机生产厂商是如何实现UPS技术标准的:
我们知道手机操作系统开发主要就是针对手机的ROM模块进行开发,所以我们可以得知各种手机设备生产厂商集成UPS-SDK的位置就是在手机的ROM模块。每个手机生产设备厂商都在各自的ROM模块实现了UPS-SDK接受推送消息的服务,这个服务只跟手机网络有关,一直存在后台,使用的时候需要手机生产厂商进行授权! 这里为什么需要手机设备生产厂商的授权才能使用呢?如果去掉这个授权不是也很合理吗?(所有安装在该手机设备上的APP都可以使用这条推送后台服务,这样也是合情合理),具体为什么使用手机设备的推送后台服务需要授权?还需要有知道的朋友可以解答。
这里需要理解一下友盟推送和手机设备生产厂商各自的推送服务功能之间的区别,首先我们可以理解各种手机生产厂商的推送SDK和友盟、极光推送服务的性质是相同的,实现的功能都是推送消息到手机设备的功能。
但是由于手机生产厂商遵守了UPS技术标准,在你集成各种手机设备生产厂商的SDK的同时,它们也就默认给你授权使用了UPS推送服务,所以集成该手机设备的推送SDK就可以大大提高消息的送达率。
如果你之前集成了手机设备生产厂商的推送SDK,需要先删除之后再去集成友盟的推送SDK。
如果你之前集成了友盟的SDK,感觉小米手机接受到友盟推送的接受率非常低的话,就可以试着按友盟官网推送SDK集成文档的要求配置手机设备厂商的渠道:厂商通道集成文档。
根据集成文档我们发现,并不只是在友盟推送应用管理后台配置各个手机设备厂商的的AppID和AppSecret就可以了,还需要在项目的APP目录下集成各个手机设备厂商的推送SDK来实现手机推送服务的授权,但是我们在友盟推送官网复制的手机设备厂商推送SDK和手机设备厂商官网的提供的SDK一样吗?
下面我们就选择小米来试着集成厂商通道:小米推送服务集成官网。创建完应用之后,就按照友盟集成厂商渠道的步骤,添加好x-secert和在app目录下导入友盟提供的设备厂商SDK,然后在Application类的onCreate()方法里面注册,添加应用的AppId和App Secert成功之后就可以在小米系统打印注册日志。
我按友盟厂商渠道集成的文档添加完之后,编译却报以下错误:
Manifest merger failed : Attribute application@allowBackup value=(true) from AndroidManifest.xml:41:9-35
is also present at [com.umeng.umsdk:xiaomi-umengaccs:1.1.0] AndroidManifest.xml:24:18-45 value=(false).
Suggestion: add 'tools:replace="android:allowBackup"' to element at AndroidManifest.xml:39:5-332:19 to override.
看到这个错误,我相信很多人都会和我一样,把AndroidManifest文件的allowBackUp字段改成tools:replace系统推荐的属性,但是编译发现还是报错,但是系统却没有告诉我具体的错误问题。
Manifest merger failed with multiple errors, see logs
一直尝试着解决,但是因为无法定位具体问题,所以一直都没有解决。到后来我试着把完整的错误日志百度之后,发现是因为我改动之前的代码之后,引发了系列问题 ,但是系统并没有提示我如何去解决。然后tools:replace属性可以添加多个选项,并不需要移除之前的属性代码。
tools:replace="android:label,android:allowBackup"
我犯的错误就是使用“|”来区别多个属性,导致出现了问题,后来发现是使用“,”来进行区分的。现在问题解决。
在友盟推送集成文档中集成小米的x-sercet配置:
然后登录小米官网平台,去打开小米官网的推送服务。
总结一下主要以下步骤:
1)在小米官网上传应用之后,给上线的应用开启推送服务功能。
2)在友盟官网推送后台-》应用信息配置小米官网的appSecert.
3)按照友盟推送集成文档集成各个生产厂商的SDK,并在Application类里面注册(通过平台的appkey和appSecert)。
4)在app清单文件里面配置好离线消息处理类,各厂商都统一继承自UmengNotifyClickActivity。
5)使用各个设备生产厂商的官网推送测试消息是否可以接受离线消息。
6)使用友盟推送后台测试推送消息是否可以接受离线消息。
集成厂商SDK之后可以用厂商的推送平台测试,查询设备状态。
测试需要注册的registerId,可以根据友盟推送集成厂商设备的提示过滤字段查找到:
下图是你集成了厂商的SDK,但是没有在厂商的官网开启App的推送服务导致获取不到注册ID的问题:
通过消息轨迹查看消息是否发送成功,如果实在不知道如何解决,可以咨询友盟在线客服。
注意:使用友盟集成厂商渠道离线推送。需要后台推送参数增加两个字段,来标识设备离线时,开启离线下发消息的功能。
App后台推送消息增加以下两个字段:
"mipush":true
"mi_activity":"com.umeng.message.example.MipushTestActivity" //此处请填写Activity完整包路径
推送消息处理类:
/**
* 小米手机设备推送消息的处理activity
* @author guotianhui
*/
public class MipushHandlerActivity extends UmengNotifyClickActivity {
public int TAB_TYPE_TASK = 1003;
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
View customView = new View(this);
customView.setBackgroundColor(Color.TRANSPARENT);
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(-1,-1);
customView.setLayoutParams(layoutParams);
setContentView(customView);
}
/**
* 设备厂商推送离线消息处理,目前支持小米、OPPO
*/
@Override
public void onMessage(Intent intent) {
super.onMessage(intent); //此方法必须调用,否则无法统计打开数
try {
String body = intent.getStringExtra(AgooConstants.MESSAGE_BODY);
JSONObject bodyObject = new JSONObject(body);
UMessage uMessage = new UMessage(bodyObject);
String custom = uMessage.custom;
LogUtils.e("推送消息custom:"+custom);
JSONObject object = new JSONObject(custom);
Bundle bundle = new Bundle();
int type = object.getInt("type");
LogUtils.e("推送消息type:"+type);
String token = TokenManager.getInstance().getToken();
if (ObjectUtils.isEmpty(token)) {//判断用户是否登陆,没有登陆直接返回
return;
}
switch (type) {// 1001 阅读计划,1002 班级任务 1003完成所有班级任务弹窗
case 1001://阅读计划
bundle.putInt(ConstantExtra.INSTANCE.INTENT_TYPE, TAB_TYPE_TASK);
ARouter.getInstance().build(StudentRouter.MAIN_HOME).with(bundle).navigation();
break;
case 1002: {//班级作业推送
String articleId = object.getString(ConstantExtra.INSTANCE.KEY_SUMMARY_ID);
int summaryId = 0;
try {
summaryId = Integer.parseInt(articleId);
} catch (Exception e) {
LogUtils.e(e.getMessage());
}
bundle.putInt(ConstantExtra.INSTANCE.ARTICLE_ID, summaryId);
//路由的方式跳转,目前出现目的Activity中不再相应路由方式跳转事件
// ARouter.getInstance().build(StudentRouter.FUN_DETAIL_ARTICLE).with(bundle).navigation();
Intent intentAction = new Intent();
intent.setAction("com.fenjiread.learner.activity.HomeArticleDetailActivity");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtras(bundle);
startActivity(intentAction);
}
break;
case 1003: {//阅读报告推送
boolean showNotificationDialog = isShowNotificationDialog();
if (showNotificationDialog) {
Intent notificaIntent = new Intent(getBaseContext(), NotificationActivity.class);
int totalWords = object.getInt(ConstantExtra.INSTANCE.KEY_TOTAL_READ_WORD);
int totalDays = object.getInt(ConstantExtra.INSTANCE.KEY_TOTAL_READ_DAY);
int todayReadWords = object.getInt(ConstantExtra.INSTANCE.KEY_TODAY_READ_WORD);
notificaIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
notificaIntent.putExtra(ConstantExtra.INSTANCE.KEY_TOTAL_READ_WORD, totalWords);
notificaIntent.putExtra(ConstantExtra.INSTANCE.KEY_TOTAL_READ_DAY, totalDays);
notificaIntent.putExtra(ConstantExtra.INSTANCE.KEY_TODAY_READ_WORD, todayReadWords);
startActivity(notificaIntent);
//保存一个时间戳
UserPreferences.INSTANCE.saveKeyCurrentTimer(String.valueOf(System.currentTimeMillis()));
}
}
case 1004: {//用户写一写列表
int summaryId = object.getInt(ConstantExtra.INSTANCE.KEY_SUMMARY_ID);
int levelId = object.getInt(ConstantExtra.INSTANCE.KEY_LEVEL_ID);
long questionId = object.getLong(ConstantExtra.INSTANCE.KEY_QUESTION_ID);
bundle.putInt(ConstantExtra.ARTICLE_ESSAY_ID, summaryId);
bundle.putInt(ConstantExtra.ARTICLE_LEVEL_ID, levelId);
bundle.putString(ConstantExtra.WRITE_QUESTION_ID, String.valueOf(questionId));
ARouter.getInstance().build(StudentRouter.FUN_DETAIL_ARTICLE_COMMENT).with(bundle).navigation();
}
break;
default: {
}
break;
}
} catch (JSONException e) {
LogUtils.e(">>>>>>>>>>>>>>厂商设备处理友盟推送报错:"+ e.getMessage());
}
}
/**
* 提供一个方法判断是否在本周之内显示
*/
public boolean isShowNotificationDialog() {
boolean firstPush = UserPreferences.INSTANCE.getKeyFirstPushState();
boolean selectedState = UserPreferences.INSTANCE.getKeyIsSelectedState();
if(firstPush) {
UserPreferences.INSTANCE.saveKeyFirstPushState(false);
return true;
}else {
Long daytime = 0l, pushTime = 0l;
String currentTime = UserPreferences.INSTANCE.getKeyCurrentTime();
if (ObjectUtils.isNotEmpty(currentTime)) {
daytime = Long.valueOf(currentTime);
}
String pushTimeString = UserPreferences.INSTANCE.getKeyPushTime();
if (ObjectUtils.isNotEmpty(pushTimeString)) {
pushTime = Long.valueOf(pushTimeString);
}
//如果没有选中按钮。没有保存时间戳,并且不再本周范围内则显示
if (!selectedState && !DataUtil.isThisWeek(pushTime) && !DataUtil.isToday(daytime)) {
return true;
} else {
return false;
}
}
}
}
这里接受到的消息参数比较多,查看源码可以使用友盟的数据类进行处理:
String body = intent.getStringExtra(AgooConstants.MESSAGE_BODY);
JSONObject bodyObject = new JSONObject(body);
UMessage uMessage = new UMessage(bodyObject);
String custom = uMessage.custom;
LogUtils.e("推送消息custom:"+custom);
JSONObject object = new JSONObject(custom);
传送门:友盟推送集成的常见问题