XPush
一个轻量级、可插拔的 Android 消息推送框架。一键集成推送(极光推送、友盟推送、华为、小米推送等),提供有效的保活机制,支持推送的拓展,充分解耦推送和业务逻辑,解放你的双手!
在提 issue 前,请先阅读【提问的智慧】,并严格按照 issue 模板进行填写,节约大家的时间。
在使用前,请一定要仔细阅读使用说明文档, 重要的事情说三遍!!!
在使用前,请一定要仔细阅读使用说明文档, 重要的事情说三遍!!!
在使用前,请一定要仔细阅读使用说明文档, 重要的事情说三遍!!!
关于我
特征
- 集成方便。只需几行代码即可实现推送的集成,目前已经提供极光、友盟等推送渠道,除此之外还可以根据自己的需要进行扩展。
- 兼容性强。目前已完美支持 Android 9.0。
- 功能强大。支持推送相关的注册、注销,标签的增加、删除、获取,别名的绑定、解绑、获取,推送的连接状态获取等操作,并能返回响应的结果;支持接收推送通知、通知的点击事件、自定义消息等推送类型。
- 统一的消息订阅。框架提供了统一的消息订阅渠道,无论你使用了何种推送方式,都可以在任何地方进行推送消息的订阅和取消订阅,方便消息的接收和处理。
- 支持增加消息过滤器。类似 OkHttp 中的拦截器,可以对接收的消息进行全局过滤,过滤出那些我们真正需要的推送消息。
- 提供有效的保活机制。保证接入 XPush 的应用消息推送的到达率和稳定性,这也是很多推送框架所做不到的。
组成结构
本框架借鉴了 OnePush(目前已不维护了)中的部分思想,加之我 3 年消息推送的经验,形成了如下几个部分:
- 消息推送客户端
IPushClient
:主要提供消息推送平台的主要 API。 - 消息推送事件转发器
IPushDispatcher
:主要用于将第三方的消息推送事件转发为 XPush 可识别的事件。 - 消息推送接收器
IPushReceiver
:统一接收 IPushDispatcher 转发过来的事件,是事件的接收中心。 - 推送消息的被观察者
IMessageObservable
:主要负责管理推送消息的订阅和转发。 - 推送消息的过滤策略
IMessageFilterStrategy
:主要负责推送消息的过滤处理和管理。
以上 5 个组成部分可以根据你自身的业务需求进行自定义。
消息推送流程
在后台发出一则推送消息后:
第三方推送平台 --- (消息) ---> 第三方推送平台内部的接收消息的 Receiver --->(重写其接收的方法)---> IPushDispatcher ---> (转发消息内容为 XPushMsg/XPushCommand)---> IPushReceiver ---> (如不使用 XPushManager 提供的消息管理,这里直接结束)【使用 XPushManager 提供的消息管理】:---IPushReceiver---> XPushManager -----> IMessageFilterStrategy --->(对消息进行过滤处理)---> IMessageObservable --->(消息转发到具体订阅的地方)
为什么要做这个项目
做过 Android 消息推送的人都知道,Android 不仅设备碎片化严重,推送平台也是五花八门的。早在 2017 年工信部就号召所有的厂商来制定统一的 Android 消息推送平台,可到现在也没有下文(究其原因还是这其中的利益太大了,谁也不想妥协)。
可是我们也不能将希望全都寄托在这个完全没有定数的事件上,代码终归要写,功能终归要上,与其受制于人,不如自己革命,搞一个自己能控制的消息推送全平台解决方案来得靠谱。
之前在 QQ 交流群里一直有人希望我开源一个消息推送框架,其实我在上一家公司的时候就写了一个推送框架,只不过捆绑业务太深,加之避开泄密之嫌,也就没有开源的必要。此次的推送框架完全是重新写了一个,加之全新的设计,会使框架更加通用,灵活。
快速集成指南
添加 Gradle 依赖
1. 先在项目根目录的 build.gradle 的 repositories 添加:
allprojects {
repositories {
...
maven {url "https://jitpack.io"}
}
}
2. 添加 XPush 主要依赖:
dependencies {
...
// 推送核心库
implementation 'com.github.xuexiangjys.XPush:xpush-core:1.0.0'
// 推送保活库
implementation 'com.github.xuexiangjys.XPush:keeplive:1.0.0'
}
3. 添加第三方推送依赖(根据自己的需求进行添加,当然也可以全部添加)
dependencies {
...
// 选择你想要集成的推送库
implementation 'com.github.xuexiangjys.XPush:xpush-jpush:1.0.0'
implementation 'com.github.xuexiangjys.XPush:xpush-umeng:1.0.0'
implementation 'com.github.xuexiangjys.XPush:xpush-huawei:1.0.0'
implementation 'com.github.xuexiangjys.XPush:xpush-xiaomi:1.0.0'
}
初始化 XPush 配置
1. 注册消息推送接收器。方法有两种,选其中一种就行了。
- 如果你想使用
XPushManager
提供的消息管理,直接在 AndroidManifest.xml 中注册框架默认提供的XPushReceiver
。当然你也可以继承XPushReceiver
,并重写相关方法。 - 如果你想实现自己的消息管理,可继承
AbstractPushReceiver
类,重写里面的方法,并在 AndroidManifest.xml 中注册。
<!-- 自定义消息推送接收器 -->
<receiver android:name=".push.CustomPushReceiver">
<intent-filter>
<action android:name="com.xuexiang.xpush.core.action.RECEIVE_CONNECT_STATUS_CHANGED" />
<action android:name="com.xuexiang.xpush.core.action.RECEIVE_NOTIFICATION" />
<action android:name="com.xuexiang.xpush.core.action.RECEIVE_NOTIFICATION_CLICK" />
<action android:name="com.xuexiang.xpush.core.action.RECEIVE_MESSAGE" />
<action android:name="com.xuexiang.xpush.core.action.RECEIVE_COMMAND_RESULT" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
<!-- 默认的消息推送接收器 -->
<receiver android:name="com.xuexiang.xpush.core.receiver.impl.XPushReceiver">
<intent-filter>
<action android:name="com.xuexiang.xpush.core.action.RECEIVE_CONNECT_STATUS_CHANGED" />
<action android:name="com.xuexiang.xpush.core.action.RECEIVE_NOTIFICATION" />
<action android:name="com.xuexiang.xpush.core.action.RECEIVE_NOTIFICATION_CLICK" />
<action android:name="com.xuexiang.xpush.core.action.RECEIVE_MESSAGE" />
<action android:name="com.xuexiang.xpush.core.action.RECEIVE_COMMAND_RESULT" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
注意,如果你的 Android 设备是 8.0 及以上的话,静态注册的广播是无法正常生效的,解决的方法有两种:
- 动态注册消息推送接收器
- 修改推送消息的发射器
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//Android8.0 静态广播注册失败解决方案一:动态注册
XPush.registerPushReceiver(new CustomPushReceiver());
//Android8.0 静态广播注册失败解决方案二:修改发射器
XPush.setIPushDispatcher(new Android26PushDispatcherImpl(CustomPushReceiver.class));
}
2. 在 AndroidManifest.xml 的 application 标签下,添加第三方推送客户端实现类.
需要注意的是,这里注册的 PlatformName
和PlatformCode
必须要和推送客户端实现类中的一一对应才行。
<!--name 格式:XPush_[PlatformName]_[PlatformCode]-->
<!--value 格式:对应客户端实体类的全类名路径 -->
<!-- 如果引入了 xpush-jpush 库 -->
<meta-data
android:name="XPush_JPush_1000"
android:value="com.xuexiang.xpush.jpush.JPushClient" />
<!-- 如果引入了 xpush-umeng 库 -->
<meta-data
android:name="XPush_UMengPush_1001"
android:value="com.xuexiang.xpush.umeng.UMengPushClient" />
<!-- 如果引入了 xpush-huawei 库 -->
<meta-data
android:name="XPush_HuaweiPush_1002"
android:value="com.xuexiang.xpush.huawei.HuaweiPushClient" />
<!-- 如果引入了 xpush-xiaomi 库 -->
<meta-data
android:name="XPush_MIPush_1003"
android:value="com.xuexiang.xpush.xiaomi.XiaoMiPushClient" />
3. 添加第三方 AppKey 和 AppSecret.
这里的 AppKey 和 AppSecret 需要我们到各自的推送平台上注册应用后获得。注意如果使用了 xpush-xiaomi, 那么需要在 AndroidManifest.xml 添加小米的 AppKey 和 AppSecret(注意下面的“”必须加上,否则获取到的是 float 而不是 String,就会导致 id 和 key 获取不到正确的数据)。
<!-- 极光推送静态注册 -->
<meta-data
android:name="JPUSH_CHANNEL"
android:value="default_developer" />
<meta-data
android:name="JPUSH_APPKEY"
android:value="a32109db64ebe04e2430bb01" />
<!-- 友盟推送静态注册 -->
<meta-data
android:name="UMENG_APPKEY"
android:value="5d5a42ce570df37e850002e9" />
<meta-data
android:name="UMENG_MESSAGE_SECRET"
android:value="4783a04255ed93ff675aca69312546f4" />
<!-- 华为 HMS 推送静态注册 -->
<meta-data
android:name="com.huawei.hms.client.appid"
android:value="101049475"/>
<!-- 小米推送静态注册,下面的“\”必须加上,否则将无法正确读取 -->
<meta-data
android:name="MIPUSH_APPID"
android:value="\ 2882303761518134164"/>
<meta-data
android:name="MIPUSH_APPKEY"
android:value="\ 5371813415164"/>
4. 在 Application 中初始化 XPush
初始化 XPush 的方式有两种,根据业务需要选择一种方式就行了:
- 静态注册
/**
* 静态注册初始化推送
*/
private void initPush() {XPush.debug(BuildConfig.DEBUG);
// 静态注册,指定使用友盟推送客户端
XPush.init(this, new UMengPushClient());
XPush.register();}
- 动态注册
/**
* 动态注册初始化推送
*/
private void initPush() {XPush.debug(BuildConfig.DEBUG);
// 动态注册,根据平台名或者平台码动态注册推送客户端
XPush.init(this, new IPushInitCallback() {
@Override
public boolean onInitPush(int platformCode, String platformName) {String romName = RomUtils.getRom().getRomName();
if (romName.equals(SYS_EMUI)) {return platformCode == HuaweiPushClient.HUAWEI_PUSH_PLATFORM_CODE && platformName.equals(HuaweiPushClient.HUAWEI_PUSH_PLATFORM_NAME);
} else if (romName.equals(SYS_MIUI)) {return platformCode == XiaoMiPushClient.MIPUSH_PLATFORM_CODE && platformName.equals(XiaoMiPushClient.MIPUSH_PLATFORM_NAME);
} else {return platformCode == JPushClient.JPUSH_PLATFORM_CODE && platformName.equals(JPushClient.JPUSH_PLATFORM_NAME);
}
}
});
XPush.register();}
如何使用 XPush
1、推送的注册和注销
- 通过调用
XPush.register()
,即可完成推送的注册。 - 通过调用
XPush.unRegister()
,即可完成推送的注销。 - 通过调用
XPush.getPushToken()
,即可获取消息推送的 Token(令牌)。 - 通过调用
XPush.getPlatformCode()
,即可获取当前使用推送平台的码。
2、推送的标签(tag)处理
- 通过调用
XPush.addTags()
,即可添加标签(支持传入多个)。 - 通过调用
XPush.deleteTags()
,即可删除标签(支持传入多个)。 - 通过调用
XPush.getTags()
,即可获取当前设备所有的标签。
需要注意的是,友盟推送目前暂不支持标签的获取,华为推送不支持标签的所有操作,小米推送每次只支持一个标签的操作。
3、推送的别名(alias)处理
- 通过调用
XPush.bindAlias()
,即可绑定别名。 - 通过调用
XPush.unBindAlias()
,即可解绑别名。 - 通过调用
XPush.getAlias()
,即可获取当前设备所绑定的别名。
需要注意的是,友盟推送目前暂不支持别名的获取,华为推送不支持别名的所有操作。
4、推送消息的接收
- 通过调用
XPushManager.get().register()
方法,注册消息订阅MessageSubscriber
,即可在任意地方接收到推送的消息。 - 通过调用
XPushManager.get().unregister()
方法,即可取消消息的订阅。
这里需要注意的是,消息订阅的回调并不一定是在主线程,因此在回调中如果进行了 UI 的操作,一定要确保切换至主线程。下面演示代码中使用了我的另一个开源库 XAOP, 只通过 @MainThread
注解就能自动切换至主线程, 可供参考。
/**
* 初始化监听
*/
@Override
protected void initListeners() {XPushManager.get().register(mMessageSubscriber);
}
private MessageSubscriber mMessageSubscriber = new MessageSubscriber() {
@Override
public void onMessageReceived(CustomMessage message) {showMessage(String.format("收到自定义消息:%s", message));
}
@Override
public void onNotification(Notification notification) {showMessage(String.format("收到通知:%s", notification));
}
};
@MainThread
private void showMessage(String msg) {tvContent.setText(msg);
}
@Override
public void onDestroyView() {XPushManager.get().unregister(mMessageSubscriber);
super.onDestroyView();}
5、推送消息的过滤处理
- 通过调用
XPushManager.get().addFilter()
方法,可增加对订阅推送消息的过滤处理。对于一些我们不想处理的消息,可以通过消息过滤器将它们筛选出来。 - 通过调用
XPushManager.get().removeFilter()
方法,即可去除消息过滤器。
/**
* 初始化监听
*/
@Override
protected void initListeners() {XPushManager.get().addFilter(mMessageFilter);
}
private IMessageFilter mMessageFilter = new IMessageFilter() {
@Override
public boolean filter(Notification notification) {if (notification.getContent().contains("XPush")) {showMessage("通知被拦截");
return true;
}
return false;
}
@Override
public boolean filter(CustomMessage message) {if (message.getMsg().contains("XPush")) {showMessage("自定义消息被拦截");
return true;
}
return false;
}
};
@Override
public void onDestroyView() {XPushManager.get().removeFilter(mMessageFilter);
super.onDestroyView();}
6、推送通知的点击处理
对于通知的点击事件,我们可以处理得更优雅,自定义其点击后的动作,打开我们想让用户看到的页面。
我们可以在全局消息推送的接收器 IPushReceiver
中的 onNotificationClick
回调中,增加打开指定页面的操作。
@Override
public void onNotificationClick(Context context, XPushMsg msg) {super.onNotificationClick(context, msg);
// 打开自定义的 Activity
Intent intent = IntentUtils.getIntent(context, TestActivity.class, null, true);
intent.putExtra(KEY_PARAM_STRING, msg.getContent());
intent.putExtra(KEY_PARAM_INT, msg.getId());
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
ActivityUtils.startActivity(intent);
}
需要注意的是,这需要你在消息推送平台推送的通知使用的是 自定义动作
或者 打开指定页面
类型,并且传入的 Intent uri 内容满足如下格式:
- title:通知的标题
- content:通知的内容
- extraMsg:通知附带的拓展字段,可存放 json 或其他内容
- keyValue:通知附带的键值对
xpush://com.xuexiang.xpush/notification?title= 这是一个通知 &content= 这是通知的内容 &extraMsg=xxxxxxxxx&keyValue={"param1": "1111", "param2": "2222"}
当然你也可以自定义传入的 Intent uri 格式,具体可参考项目中的 XPushNotificationClickActivity 和 AndroidManifest.xml
推送平台说明
目前已支持的推送平台
推送平台 | 平台名 | 平台码 | 模块名 | 客户端类 |
---|---|---|---|---|
极光推送 | JPush | 1000 | xpush-jpush | com.xuexiang.xpush.jpush.JPushClient |
友盟推送 | UMengPush | 1001 | xpush-umeng | com.xuexiang.xpush.umeng.UMengPushClient |
华为推送 | HuaweiPush | 1002 | xpush-huawei | com.xuexiang.xpush.huawei.HuaweiPushClient |
小米推送 | MIPush | 1003 | xpush-xiaomi | com.xuexiang.xpush.xiaomi.XiaoMiPushClient |
推送平台的注意事项
极光推送平台所有特性都支持。
友盟推送
- 友盟推送在进行 XPush 初始化的时候,除了在主进程中注册,还需要在 channel 中注册。
- 友盟推送不支持 Tag 和 alias 的获取
- 友盟推送不支持监听推送的连接状态。
华为推送
- 华为推送在注册之前需要安装最新的推送服务,否则将无法注册成功(库会自动弹出升级或者安装提示)
- 华为推送不支持所有 Tag 和 alias 的操作。
- 华为推送不支持接收通知到达事件。
小米推送
- 小米推送一次只能操作一个 Tag。
- 小米推送注销无结果反馈。
- 小米推送不支持监听推送的连接状态。
如何拓展第三方推送
由于 Android 推送平台的众多,目前本项目不可能也没必要提供所有推送平台的集成库。如果你想使用的推送平台在我这没有找到对应的集成库的话,那么就需要你自己写一个了。
其实拓展一个第三方推送库也不是很难,只要遵循以下 4 步骤就可以完成了:
- 1. 新建一个 Android Library Module,然后将你准备集成的推送平台的依赖内容导入进来。这里包括引入推送依赖库或 SDK、配置
AndroidManifest.xml
。 - 2. 创建该推送平台的客户端 XXXClient,实现 IPushClient 接口,并且重写对应的方法。其中
init
、register
、unRegister
、getPlatformCode
、getPlatformName
这 5 个方法是必须重写的。
IPushClient 接口方法详细如下:
public interface IPushClient {
/**
* 初始化【必须】*
* @param context
*/
void init(Context context);
/**
* 注册推送【必须】*/
void register();
/**
* 注销推送【必须】*/
void unRegister();
/**
* 绑定别名【别名是唯一的】*
* @param alias 别名
*/
void bindAlias(String alias);
/**
* 解绑别名
*
* @param alias 别名
*/
void unBindAlias(String alias);
/**
* 获取别名
*/
void getAlias();
/**
* 增加标签
*
* @param tag 标签
*/
void addTags(String... tag);
/**
* 删除标签
*
* @param tag 标签
*/
void deleteTags(String... tag);
/**
* 获取标签
*/
void getTags();
/**
* @return 获取推送令牌
*/
String getPushToken();
/**
* 注意千万不要重复【必须】* @return 获取平台码
*/
int getPlatformCode();
/**
* 注意千万不要重复【必须】* @return 获取平台名
*/
String getPlatformName();}
- 3. 创建和重写三方消息推送的消息接收器(一般是重写 Receiver)。重写三方推送的的接收透传消息和通知的方法,调用
XPush
的 transmitXXX 方法,将通知、透传消息、通知点击事件、以及其他事件,转发到 XPush。
主要调用以下五个方法:
(1)XPush.transmitMessage(): 转发自定义 (透传) 消息.
(2)XPush.transmitNotification(): 转发通知到达消息.
(3)XPush.transmitNotificationClick(): 转发通知点击事件.
(4)XPush.transmitCommandResult(): 转发 IPushClient 命令执行结果.
(5)XPush.transmitConnectStatusChanged(): 转发推送连接状态发生改变的事件.
- 4. 增加该推送平台对应的代码混淆配置信息。
以上即完成了推送平台的集成。剩下的就是在初始化 XPush 的时候对推送平台进行选择了. 如果你看完了还是不会的话,你可以参考项目中的 xpush-xiaomi 和 xpush-huawei.
实体介绍
XPushMsg
推送消息转译实体,携带消息的原始数据
字段名 | 类型 | 备注 |
---|---|---|
mId | int | 消息 ID / 状态 |
mTitle | String | 通知标题 |
mContent | String | 通知内容 |
mMsg | String | 自定义(透传)消息 |
mExtraMsg | String | 消息拓展字段 |
mKeyValue | String | 消息键值对 |
Notification
推送通知,由 XPushMsg 转化而来
字段名 | 类型 | 备注 |
---|---|---|
mId | int | 消息 ID / 状态 |
mTitle | String | 通知标题 |
mContent | String | 通知内容 |
mExtraMsg | String | 消息拓展字段 |
mKeyValue | String | 消息键值对 |
CustomMessage
自定义(透传)消息,由 XPushMsg 转化而来
字段名 | 类型 | 备注 |
---|---|---|
mMsg | String | 自定义(透传)消息 |
mExtraMsg | String | 消息拓展字段 |
mKeyValue | String | 消息键值对 |
XPushCommand
IPushClient 执行相关命令的结果信息实体
字段名 | 类型 | 备注 |
---|---|---|
mType | int | 命令类型 |
mResultCode | int | 结果码 |
mContent | String | 命令内容 |
mExtraMsg | String | 拓展字段 |
mError | String | 错误信息 |
常量介绍
CommandType
命令的类型
命令名 | 命令码 | 备注 |
---|---|---|
TYPE_REGISTER | 2000 | 注册推送 |
TYPE_UNREGISTER | 2001 | 注销推送 |
TYPE_ADD_TAG | 2002 | 添加标签 |
TYPE_DEL_TAG | 2003 | 删除标签 |
TYPE_GET_TAG | 2004 | 获取标签 |
TYPE_BIND_ALIAS | 2005 | 绑定别名 |
TYPE_UNBIND_ALIAS | 2006 | 解绑别名 |
TYPE_GET_ALIAS | 2007 | 获取别名 |
TYPE_AND_OR_DEL_TAG | 2008 | 添加或删除标签 |
ResultCode
命令的结果码
结果名 | 结果码 | 备注 |
---|---|---|
RESULT_OK | 0 | 成功 |
RESULT_ERROR | 1 | 失败 |
ConnectStatus
推送连接状态
状态名 | 状态码 | 备注 |
---|---|---|
DISCONNECT | 10 | 已断开 |
CONNECTING | 11 | 连接中 |
CONNECTED | 12 | 已连接 |
混淆配置
# XPush 的混淆
-keep class * extends com.xuexiang.xpush.core.IPushClient{*;}
-keep class * extends com.xuexiang.xpush.core.receiver.IPushReceiver{*;}
# 极光推送混淆
-dontwarn cn.jpush.**
-keep class cn.jpush.** {*;}
-dontwarn cn.jiguang.**
-keep class cn.jiguang.** {*;}
-keep class * extends cn.jpush.android.service.JPushMessageReceiver{*;}
# umeng 推送
-dontwarn com.umeng.**
-dontwarn com.taobao.**
-dontwarn anet.channel.**
-dontwarn anetwork.channel.**
-dontwarn org.android.**
-dontwarn org.apache.thrift.**
-dontwarn com.xiaomi.**
-dontwarn com.huawei.**
-dontwarn com.meizu.**
-keep class com.taobao.** {*;}
-keep class org.android.** {*;}
-keep class anet.channel.** {*;}
-keep class com.xiaomi.** {*;}
-keep class com.huawei.** {*;}
-keep class com.meizu.** {*;}
-keep class org.apache.thrift.** {*;}
-keep class com.alibaba.sdk.android.**{*;}
-keep class com.ut.**{*;}
-keep class com.ta.**{*;}
# 华为推送
-keep class com.huawei.hms.**{*;}
-keep class com.huawei.android.hms.agent.**{*;}
# 小米推送
-keep class * extends com.xiaomi.mipush.sdk.PushMessageReceiver{*;}
特别感谢
- OnePush
- keeplive
如果觉得项目还不错,可以考虑打赏一波
联系方式