接手的项目里面有一个小米和华为混用的推送功能,虽然能用,但是代码写的非常乱,正好最近又看了点设计模式,就花了点时间重构了一下,顺便做一个小结!
具体如何导入各个厂商的 SDK,文章末尾会讲下小米的,这里先把如何混用先说完,下面给出华为小米得官网链接,需要的话可以根据官网教程导入:
- 小米推送
https://dev.mi.com/console/appservice/push.html - 华为推送
https://developer.huawei.com/consumer/cn/hms/huawei-pushkit
下面先讲如何混用两个推送功能的思路
整体思路
因为涉及到多个推送服务的使用(考虑后面扩展),并且有类似的地方,所以我这抽象了一个推送服务接口(IPushClient),具体实现再根据厂商去实现。对于生成不同的推送服务,首先想到了工厂模式,下面也是这么做的,最后又写了一个 PushClientProxy 类来集中处理推送服务的功能,几个文件如下:
- IPushClient 推送服务接口
- PushClientProxy 代理类
- HuaweiPushClient 华为推送服务
- XiaomiPushClient 小米推送服务
- PushClientFactory 用来生成推送服务的工厂类
对于接受消息,都是通过广播来接受,这里我没有整合在一起,所以有两个广播接收器要编写:
- HuaweiPushReceiver
- XiaomiPushReceiver
推送服务
public interface IPushClient {
void bind();
void unbind();
}
推送服务接口设计了两个功能,绑定和解绑推送功能,下面时华为和小米对应的推送服务。
- 华为
public class HuaweiPushClient implements IPushClient, HuaweiApiClient.ConnectionCallbacks, HuaweiApiClient.OnConnectionFailedListener {
private final HuaweiApiClient mClient;
private WeakReference<Activity> currentActivity;
private String mToken = null;
public HuaweiPushClient(Context context) {
mClient = new HuaweiApiClient.Builder(context)
.addApi(HuaweiPush.PUSH_API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
//在activity修改时需要调用此方法更新currentActivity,降低了耦合性,却增加了复杂度
public void setCurrentActivity(Activity activity) {
//这里WeakReference里面居然不让修改,只能每次重建一个
currentActivity = new WeakReference<>(activity);
}
@Override
public void bind() {
//通过下面方
mClient.connect(currentActivity.get());
//下面通过BaseActivity拿到当前activity比较方便,却耦合了
//mClient.connect(BaseActivity.getCurrentAty());
}
@Override
public void unbind() {
mClient.disconnect();
}
@Override
public void onConnected() {
//client连接到华为移动服务,获取token
PendingResult<TokenResult> tokenResult = HuaweiPush.HuaweiPushApi.getToken(mClient);
tokenResult.setResultCallback(result -> mToken = result.getTokenRes().getToken());
}
@Override
public void onConnectionSuspended(int cause) {
//当client变成断开状态时,重新连接
mClient.connect(currentActivity.get());
}
@Override
public void onConnectionFailed(ConnectionResult result) {
//建立client到service的连接失败时调用
}
}
- 小米
public class XiaomiPushClient implements IPushClient {
private final Context mContext;
//填入官网申请到的APP_ID
private static final String APP_ID = "";
//填入官网申请到的APP_KEY
private static final String APP_KEY = "";
public XiaomiPushClient(Context context) {
mContext = context;
}
@Override
public void bind() {
//初始化push推送服务
MiPushClient.registerPush(mContext, APP_ID, APP_KEY);
}
@Override
public void unbind() {
//关闭推送服务
MiPushClient.unregisterPush(mContext);
}
}
可能 SDK 版本不同会有所出入,但是这里我觉得应该不难。
推送服务创建
对于推送服务的创建,我们是需要根据手机型号去判断的,这里先写了一个工具类来判断应该使用何种推送服务:
public class BrandUtils {
public static final int HUAWEI = 1;
public static final int XIAOMI = 2;
//获取厂商
public static int getBrand(Context context) {
if (isHuaweiDevice(context)) {
return HUAWEI;
}else {
//这里默认使用了小米
return XIAOMI;
}
}
//判断是否时华为手机
private static boolean isHuaweiDevice(Context context) {
if ("huawei".equalsIgnoreCase(Build.BRAND) || "HONOR".equalsIgnoreCase(Build.BRAND)) {
//EMUI版本号
int emuiApiLevel = 0;
//华为移动服务版本号
int mobileServiceVersionCode = 0;
try {
@SuppressLint("PrivateApi")
Class<?> cls = Class.forName("android.os.SystemProperties");
Method method = cls.getDeclaredMethod("get", String.class);
emuiApiLevel = Integer.parseInt((String) method.invoke(cls, "ro.build.hw_emui_api_level"));
PackageManager pm = context.getPackageManager();
PackageInfo pi = pm.getPackageInfo("com.huawei.hwid", 0);
if (pi != null) {
mobileServiceVersionCode = pi.versionCode;
}
} catch (Exception e) {
e.printStackTrace();
}
//在低版本EMUI和华为移动服务上推送不太行
return emuiApiLevel > 9 && mobileServiceVersionCode >= 20401300;
}
return false;
}
}
然后是推送服务的工厂,根据使用的推送种类生成对应的推送服务实现类
public class PushClientFactory {
//通过type生成推送服务
public IPushClient getPushClient(int type, Context context) {
switch (type) {
case BrandUtils.HUAWEI:
return new HuaweiPushClient(context);
case BrandUtils.XIAOMI:
default:
return new XiaomiPushClient(context);
}
}
}
使用推送服务
因为我们需要根据不同的手机型号生成不同的推送服务,所以这里使用了代理模式
public class PushClientProxy implements IPushClient {
private final IPushClient mClient;
private final Context mContext;
public PushClientProxy(Context context) {
//获取上下文
mContext = context;
//获取厂商
int brand = BrandUtils.getBrand(context);
//推送服务工厂类
PushClientFactory factory = new PushClientFactory();
//根据厂商获得推送服务
this.mClient = factory.getPushClient(brand, context);
}
@Override
public void bind() {
//判断是否要注册推送服务
if (shouldInit(mContext)) {
mClient.bind();
}
}
@Override
public void unbind() {
mClient.unbind();
}
//来自小米demo,判断当前应用是否在运行
private boolean shouldInit(Context context) {
ActivityManager am = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE));
List<ActivityManager.RunningAppProcessInfo> processInfos = am.getRunningAppProcesses();
String mainProcessName = context.getPackageName();
int myPid = android.os.Process.myPid();
for (ActivityManager.RunningAppProcessInfo info : processInfos) {
if (info.pid == myPid && mainProcessName.equals(info.processName)) {
return true;
}
}
return false;
}
}
代理类创建的时候,会拿着上下文对象获得厂商类别,并生成对应的推送服务,这里需要在 application 中创建,并调用 bind 方法。
消息接受
这里写了两种广播接收器,具体看下面代码:
- 华为
public class HuaweiPushReceiver extends PushReceiver {
@Override
public void onToken(Context context, String token, Bundle extras){
//用来接收token和附加信息
String belongId = extras.getString("belongId");
}
@Override
public boolean onPushMsg(Context context, byte[] msg, Bundle bundle) {
//用来接收透传消息
String content = new String(msg, StandardCharsets.UTF_8);
return false;
}
public void onEvent(Context context, Event event, Bundle extras) {
//Push事件回调方法入口,目前支持的回调事件有通知栏消息点击事件回调、通知栏扩展消息按钮点击事件回调
super.onEvent(context, event, extras);
}
@Override
public void onPushState(Context context, boolean pushState) {
//接收push连接状态
}
}
- 小米
public class XiaomiPushReceiver extends PushMessageReceiver {
private String mRegId;
@Override
public void onNotificationMessageArrived(Context context, MiPushMessage message) {
//获取通知栏推送消息
String content = message.getContent();
}
@Override
public void onReceivePassThroughMessage(Context context, MiPushMessage message) {
//获取透传消息
String content = message.getContent();
}
@Override
public void onNotificationMessageClicked(Context context, MiPushMessage message) {
//通知栏消息点击
}
@Override
public void onReceiveRegisterResult(Context context, MiPushCommandMessage message) {
//注册结果,可以拿到mRegId
String command = message.getCommand();
List<String> arguments = message.getCommandArguments();
String cmdArg1 = ((arguments != null && arguments.size() > 0) ? arguments.get(0) : null);
if (MiPushClient.COMMAND_REGISTER.equals(command)) {
if (message.getResultCode() == ErrorCode.SUCCESS) {
mRegId = cmdArg1;
}
}
}
}
这里应该也可以把各个接收的消息统一起来,需要和业务挂钩吧,读者自行处理喽!
接入小米推送
本文开头说了会介绍一下接入小米推送,其他的应该都差不多,下面看步骤:
-
在小米推送运营平台创建应用,地址点这里, 获取到 AppID 和 AppKey
-
把从小米下载的 jar 导入到安卓项目中
-
在 AndroidManifest.xml 中添加权限
<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"/> <permission android:name="com.xxx.xxx.permission.MIPUSH_RECEIVE" android:protectionLevel="signature" /> <uses-permission android:name="com.xxx.xxx.permission.MIPUSH_RECEIVE" />
-
在 AndroidManifest.xml 中配置推送服务需要的service和receiver
<service android:enabled="true" android:process=":pushservice" android:name="com.xiaomi.push.service.XMPushService"/> <service android:name="com.xiaomi.push.service.XMJobService" android:enabled="true" android:exported="false" android:permission="android.permission.BIND_JOB_SERVICE" android:process=":pushservice" /> <!--注:此service必须在3.0.1版本以后(包括3.0.1版本)加入--> <service android:enabled="true" android:exported="true" android:name="com.xiaomi.mipush.sdk.PushMessageHandler" /> <service android:enabled="true" android:name="com.xiaomi.mipush.sdk.MessageHandleService" /> <!--注:此service必须在2.2.5版本以后(包括2.2.5版本)加入--> <receiver android:exported="true" android:name="com.xiaomi.push.service.receivers.NetworkStatusReceiver" > <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver> <receiver android:exported="false" android:process=":pushservice" android:name="com.xiaomi.push.service.receivers.PingReceiver" > <intent-filter> <action android:name="com.xiaomi.push.PING_TIMER" /> </intent-filter> </receiver>
-
自定义广播接收器继承 PushMessageReceiver 类接收推送消息(前面已写)
-
在 AndroidManifest.xml 中注册该广播
<receiver android:name=".XiaomiPushReceiver" android:exported="true"> <intent-filter> <action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE"/> </intent-filter> <intent-filter> <action android:name="com.xiaomi.mipush.MESSAGE_ARRIVED"/> </intent-filter> <intent-filter> <action android:name="com.xiaomi.mipush.ERROR"/> </intent-filter> </receiver>
-
在 Application 中初始化推送服务(前面代理类作用)
结语
写下来感觉自己文笔不是很流畅,请读者见谅,不过我自己觉得设计模式写的还可以。
end