手记 | Android MobPush 接入总结

Posted HLQ_Struggle

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手记 | Android MobPush 接入总结相关的知识,希望对你有一定的参考价值。


PS:这不仅仅是一篇简单 MobPush 接入总结,同时也会掺杂个人理解以及不成熟的想法。接入的本身很 easy,总要透过现象看本质,去深入了解其他的内容。

手记开始,记录项目点滴,一起加油~

上来还是简单叨叨几句,不吐不快。

推送的目的是什么?在地铁上我想了很多:

  • 拉活,通过产品各种特性(活动)吸引用户点击使用,进一步验证产品思路、方向;
  • 促进用户粘性,好比今天收到通知,鸡老大直播 ing,马不停蹄的点击进去开始膜拜;
  • 重大事件通知与防范,科技的力量,在某些方面,总会统一战线,无时无刻提示风险以及加强警惕;
  • 。。。

其实推送最终的目的,个人更大的感觉还是将用户进一步留存,无论采取任何方式方法。

而相比 ios 的推送,则 android 要痛苦的很多。Android 的开放性使其自身逐步发展壮大,同时开放性也衍生了国内各种厂商的定制化。各种 “系统” 层出不穷,百花齐放的场景也给 Android 开发小伙伴带来了很多兼容性的问题。iOS 只需要发送苹果服务器,然后进行后续处理 (个人理解),而 Android?这里特意搜索了下国内几大厂商的历史图:

瞧瞧,目前五大厂商赫然在目!!!我们再看看截止 2021 年国内手机出货量:


除了魅族暂时未上榜单,其余依旧坚挺。这里排除华为鸿蒙,这个不在这篇文章考虑范围内。

那么对于我们原生 Android 开发而言,如何处理,能达到最好的推送效果?我想那可能是依次对主流厂商进行兼容适配,但是同时也带来了成倍的接入工作。

较好的一点是,有困难,便有专业解决困难提供商。对于小公司而言,一键接入成型并且经过市场检验的三方推送服务便是上上策。而国内众多推送服务商,我们又该选择哪儿个?接着往下看~

Push 厂商对比

我一直都是小公司,小公司里面对于方案的选择,大部分是 直系领导 直接拍板决定,少部分是看 主程 更倾向于哪儿家就用哪儿家。

下面从我个人关注的几个维度进行极光、友盟、Mob、个推这四个厂商简单的对比吧。 (❌ 代表不支持,✅ 代表支持。特殊情况单独注明) (截止到 2021 年 8 月 10 日):

极光友盟Mob个推
支持消息格式通知栏通知、自定义消息(透传)、富媒体、本地通知通知栏通知、自定义消息(透传)、本地通知通知栏通知、自定义消息(透传)、本地通知通知消息、自定义消息(透传)、分组对比
离线厂商通道支持华为、小米、OPPO、vivo、魅族、华硕、FCM华为、小米、OPPO、vivo、魅族华为、小米、魅族、OPPO、vivo小米、华为、OPPO、vivo、魅族、坚果、海信、索尼等
别名、标签支持
移除关联启动需要开通 VIP
消息撤回
卸载统计高级版支持
支持平台Android、iOS、Windows Phone、QuickAppAndroid、iOS、FlutterAndroid、iOS、Flutter、Unity、javascript、Cocos2d-XAndroid、iOS

分析对比,找瞎眼,如果不对,欢迎拍砖~

上图中只是个人简单对比,部分详细内容未考虑,当然也包括默认支持服务端 Web 调用,例如每个厂商对某个功能特性支持是否完整,这里就不一一诉说了。

方案很多,各位按照自身需求进行对应接入即可,下面开始个人习惯的 MobPush 集成啦~

MobPush 集成

从下面官网进入,准备开始接入:

登录成功后选择工作台:

Mob 后台创建 App 信息

创建应用:

  • 上传 Icon
  • 填写 App 名称


随后选择「立即接入」:

这里选择 MobPush:

添加应用包名以及选择接入厂商通道:

接入指南:

1、合规处理

由于目前针对用户个人信息/数据进一步管控加强,首要保证 App 符合合规。这里根据 Mob 官方进行部分整理:

  • 确保App有《隐私政策》,并且在用户首次启动App时就弹出《隐私政策》取得用户同意;
  • 务必告知用户选择 MobSDK 服务,并在《隐私政策》中增加参考条款;
  • 务必确保用户同意《隐私政策》之后,调用 MobSDK 提交隐私协议接口。

2、项目根目录 build 中添加 MobPush 依赖:

buildscript {
    repositories { 
        // 配置Mob Maven库
        maven {
            url "https://mvn.mob.com/android"
        }
    }
    dependencies { 
        // 集成MobPush
        classpath "com.mob.sdk:MobSDK:2018.0319.1724" 
    }
}

3、app 下 build 中新增 Mob 插件和扩展:

这里建议单独提出一个 Mob gradle,避免后续维护混乱。

// 调用MobTech SDK
apply plugin: 'com.mob.sdk'

// 在 MobSDK 的扩展中注册 MobPush 的相关信息
MobSDK {
    appKey "appKey"
    appSecret "appSecret"
    MobPush {
        // 集成其他推送通道(可选)
        devInfo {
            // 华为推送配置信息
            HUAWEI {
                appId "华为的appid"
            }

            // 魅族推送配置信息
            MEIZU {
                appId "魅族的appid"
                appKey "魅族的appkey"
            }

            // 小米推送配置信息
            XIAOMI {
                appId "小米的appid"
                appKey "小米的appkey"
            }

            // FCM 推送通道配置
            FCM {
                // 设置默认推送通知显示图标
                iconRes "@mipmap/ic_launcher"
            }

            // OPPO 推送配置信息
            OPPO {
                appKey "OPPO的appKey"
                appSecret "OPPO的appSecret"
            }

            // VIVO 推送配置信息
            VIVO {
                appId "应用对应的vivo appID"
                appKey "应用对应的vivo appKey"
            }
        }
    }
}

随后在主 app 下 build 文件中进行引用:

// 引入 Mob 配置
apply from: 'MobSettings.gradle'

4、gradle.properties 中配置隐私协议适配版本

# 设定 MobSDK 为隐私协议适配版本
MobSDK.spEdition=FP

5、添加混淆代码

这块可以按照实际需求进行对应 CV 即可~

# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

# Mob 混淆代码
-keep class com.mob.**{*;}
-dontwarn com.mob.**

#厂商的混淆规则
-keep class android.os.SystemProperties
-dontwarn android.os.SystemProperties
-keep class com.huawei.**{*;}
-keep class com.meizu.**{*;}
-keep class com.xiaomi.**{*;}

-dontwarn com.huawei.**
-dontwarn com.meizu.**
-dontwarn com.xiaomi.**

-keepclasseswithmembernames class * {
    native <methods>;
}

-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet);
}

-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}

-keep class com.huawei.hms.**{*;}
-keep class com.meizu.cloud.**{*;}
-keep class com.xiaomi.mipush.sdk.**{*;}
-keep class org.apache.thrift.**{*;}
-keep class com.google.** {*;}
-keep class com.coloros.** {*;}
-keep class com.mob.tools.* {*;}
-keep class com.mob.wrappers.* {*;}

-dontwarn com.mob.commons.**
-dontwarn com.mob.wrappers.**
-dontwarn com.huawei.hms.**
-dontwarn com.meizu.cloud.**
-dontwarn com.xiaomi.mipush.sdk.**
-dontwarn org.apache.thrift.**
-dontwarn com.google.**
-dontwarn com.coloros.**

-dontwarn com.vivo.push.**
-keep class com.vivo.push.**{*; }
-keep class com.vivo.vms.**{*; }
-keep class com.mob.pushsdk.plugins.vivo.PushVivoReceiver{*;}

-keep class com.meizu.cloud.pushsdk.MzPushMessageReceiver{
public *;
}

-keep class com.mob.pushsdk.plugins.xiaomi.PushXiaoMiRevicer {*;}
-dontwarn com.xiaomi.push.**

#这是oppo的混淆规则
-keep public class * extends android.app.Service
-keep class com.heytap.msp.** { *;}
-keep class com.mob.pushsdk.plugins.oppo.** { *;}

6、番外

当然,也可以选择在 AndroidManifest 中选择开启 Debug 功能:

<!-- 开启日志调试功能 -->
<meta-data
    android:name="com.mob.mobpush.debugLevel"
    android:value="4" />

7、Test 一波,发个通知

在 Mob 后台选择发送一条通知测试下~


结果如下:

再 Test 一波,这次我们将应用回到后台,再来测试下。


那如果我们现在应用被杀了呢?再来测试下?

在这里明显可以观察到,当应用被杀(模拟用户非在线时刻),Mob 后台推送并没有及时下发客户端。而当用户再次打开 App 时,才能收到之前的推送。那么,如果我时隔几天后打开呢?消息的及时性又如何保证呢?当然,什么事情也不是绝对的。

那我们现在配置下华为厂商推送再试试?

8、华为厂商推送

这块较为简单,MobPush 写的也狠明确,按照要求进行操作就可以了。简单重复性的东西这里就不一一贴图了。

MobSettings 中填写对应的 appId:

// 调用MobTech SDK
apply plugin: 'com.mob.sdk'

// 在 MobSDK 的扩展中注册 MobPush 的相关信息
MobSDK {
    appKey "appKey"
    appSecret "appSecret"
    MobPush {
        // 集成其他推送通道(可选)
        devInfo {
            // 华为推送配置信息
            HUAWEI { 
                appId "appId"
            } 
        }
    }
}

随后我们在 Mob 后台进行测试离线推送测试:


经过如上几个测试小 case,我想大家对于接入离线厂商的必要性不言而喻了吧。

其实对于这块,个人理解是:当 Mob 自有通道开启状态,也就是 App 在线时,Mob 后台通过 Mob 自有通道进行消息通知下发。而如果 Mob 通道被关闭,也就是 App 被杀死,这块我猜测可能是心跳包检测?说实话还真没去了解过。关闭时通过设备对应不同厂商通道进行消息下发。从而保证了消息下发并不依赖 App 是否存活,比较厂商(系统)通道,肯定不会被干死,从而进一步的保证了 App 的消息接受性。

其它厂商这里就不一一进行对接测试了,大家前提记得注册好相关厂商帐号,随后按照 MobPush 进行配置即可。

9、再来个指定用户推送并传递对应 key

首先在 App 获取注册 ID:

MobPush.getRegistrationId {
    Log.e("HLQ", "------> getRegistrationId $it")
}

随后获取到注册 ID 并设置 Mob 后台,接着选择自定义消息,并设置如下几个内容:

  • content:This is content.
  • hlqType:1
  • hlqCode:666




对应的消息监听如下:

MobPush.addPushReceiver(object : MobPushReceiver {

    /**
     * 接收到自定义消息(透传消息)
     */
    override fun onCustomMessageReceive(
        p0: Context?,
        p1: MobPushCustomMessage?
    ) {
        Log.e(TAG, "----> onCustomMessageReceive $p1")
    }

    /**
     * 接收到通知消息
     */
    override fun onNotifyMessageReceive(
        p0: Context?,
        p1: MobPushNotifyMessage?
    ) {
        Log.e(TAG, "----> onNotifyMessageReceive $p1")
    }

    /**
     * 通知被点击事件
     */
    override fun onNotifyMessageOpenedReceive(
        p0: Context?,
        p1: MobPushNotifyMessage?
    ) {
        Log.e(TAG, "----> onNotifyMessageOpenedReceive $p1")
    }

    /**
     * 标签操作回调
     */
    override fun onTagsCallback(
        p0: Context?,
        p1: Array<out String>?,
        p2: Int,
        p3: Int
    ) {
        Log.e(TAG, "----> onTagsCallback $p1")
    }

    /**
     * 别名操作回调
     */
    override fun onAliasCallback(
        p0: Context?,
        p1: String?,
        p2: Int,
        p3: Int
    ) {
        Log.e(TAG, "----> onAliasCallback $p1")
    }
})

当在 Mob 后台点击发送时,App 是没有显式通知的,而在回调接口中,则输出我们刚刚设置的内容:

onCustomMessageReceive 
    messageId={4bp6i0t0m5xnyia9ds},
    content={This is content.},
    offlineFlag={0},
    extrasMap={{hlqCode=666, hlqType=1, 
    pushData={"hlqCode":"666","hlqType":"1"}, 
    channel=mobpush, 
    id=4bp6i0t0m5xnyia9ds}},
    timestamp={1629216359101}

好咧,针对 MobPush 推送呢,代码部分到此结束了。大部分官方都有,就不充斥太多的重复性的代码段了。

MobPush 小结

从个人角度而言,每一次都需要有些收获,或者来说,把自己遇到的问题暴露出来,万一官方不好意思调整了呢?

O(∩_∩)O哈哈~

那就从以下几点,简单说说吧。

1、技术保障

周末的时候,出现 MobSDK 拉取不下来的情况,梯子、切换网络、热点,均无效。大周末本来不好意思打扰,结果直接拉群解决,虽然最后还是按照自己降低 gradle 版本处理,但是态度上让人觉得很不错。

周三的时候,中午特意给我反馈,说他们的技术测试正常,并提供了对应的测试 Demo。虽然因为工作原因没能认真看看,但是这个认真负责的态度,赞一波~

一句话,免费的东西,这就不错了。莫抬杠~

2、文档友好度

从 MobPush 的集成来看,如果前期准备工作都已完成(这里指的账号,我从来没遇到过账号准备齐全的,都是自己折腾去),在线推送集成很 Easy,似乎就是分分钟,Mob 后台创建应用,拷贝依赖到根 build,然后跟着官方继续拷贝,剩下就是根据产品进行必要的授权。

而后期厂商集成,相对折腾的点,个人感觉更多的是各个厂商折腾相关信息,开通相关服务。比如华为乱七八糟一大堆,还要下载文件。这块其实对于 Mob 后台配置较为简单,那个 key 之类的就好了,本地项目同理。

这里简单小总结下个人感观:

“文档简洁”.apply{
    便于接入
    不便于获取详细日志情况,例如华为集成是否成功?
    针对某些失败情况,未能提供更多的解决示例
    官方文档更新较慢
}

关于第二点,这里我拿接入华为离线厂商推送举个例子:

以上两个链接是 MobPush 官方提供,但是我怎么能再接入的同时就了解到我华为渠道接入成功了呢?相关的说明请原谅我没找到。这里我随便找个正面示例:


其实针对文档友好度,个人觉得,最简单的方式就是让开发者能否最快速的接入所需要的功能?并且能对于接入内容,官方给出详细说明文档,好比怎么算成功失败了?我怎么能第一时间了解到这些?而不是需要个人实际发送通知测试,才能得出结论。(Mob 小哥哥别打我~ -_-)

关于这个文档更新这块,其实还是会导致人产生误区,简单举几个例子:

  • 官方针对 MobSDK 依赖地址,集成文档是 https 打头,下载 SDK 却写着 http。虽然后续咨询官方,官方说都支持,但是还是上来给人产生误区的感觉。
  • 目前个人测试看是根据配置的厂商渠道自动配置对应的厂商,有个疑问是,假设某天 MobPush 未能主动更新厂商或者开发者想自己替换呢?挺好奇这块的。

人呐,总想得到更多,O(∩_∩)O哈哈~

深入 SDK 稍微看看

整套集成下来,不知道大家注意到我们从始至终都没有调用过 init 方法,那么它在哪儿完成的初始化呢?

会不会是 ContentProvider?印象中此物调用时机介于 Application 的 attachBaseContext 和 onCreate 之间。

盲猜还是不如直接进入 SDK 瞅瞅~

深入 SDK,发现目前 Mob 提供了两种方式:

  • MobApplication:onCreate() init
  • ContentProvider:onCreate() init

反编译生成的 Apk,发生 Mob 将 application name 替换为 MobApplication,如下所示:

<application 
    android:name="com.mob.MobApplication" >

这里我不仅微笑一下,那如果我要是 application 继承 MobApplication 呢?还会给我替换么?

class BaseApplication : MobApplication() {

    override fun onCreate() {
        super.onCreate()
    }

}

再次反编译 apk 查看下:

<application 
    android:name="com.hlq.mob.BaseApplication" >

也是,都继承了 MobApplication 还替换个锤锤?

还别说,这块动态替换还是蛮有意思的,比如说,我集成的过程并没有添加权限,那这些权限什么时候被注入的呢?

奇怪的是在 AndroidManifest 中也看到了上文说到的 ContentProvider:

<provider
    android:name="com.mob.MobProvider"
    android:exported="false"
    android:multiprocess="true"
    android:authorities="com.pwccn.klcore.com.mob.MobProvider" />

神奇…

期间还发现个比较 6 的操作:

  • 当我配置文件中未配置渠道时,SDK 自动拉取 Mob 相关依赖;
  • 而当我配置文件中只有华为时,SDK 自动拉取华为相关依赖。

这个操作很神奇啊,有没有老哥指导的点拨一下下那~

各种混淆,看的茫然,下次有机会再说吧~

Error

  • 如果当前 AS 版本最新,并且 gradle 版本为 7.0 时,出现 MobSDK 拉取失败的情况,直接降低 gradle 版本即可。当然官方最后提供了同样 gradle 7.0 的 Demo,因为个人懒得原因就没有细细研究了。

  • 针对厂商推送失败的情况,认真检查相关包名等是否按照官方要求配置。我这就坑了自己一把。再不行给官方丢个 Demo,让官方帮忙处理下。O(∩_∩)O哈哈~

THK

以上是关于手记 | Android MobPush 接入总结的主要内容,如果未能解决你的问题,请参考以下文章

手记 | Android MobPush 接入总结

手记 | Android MobPush 接入总结

Gradle 手记|记录我使用过的 build 基本配置(不断更新中。。。

Gradle 手记|记录我使用过的 build 基本配置(不断更新中。。。

Gradle 手记|记录我使用过的 build 基本配置(不断更新中。。。

Gradle 手记|记录我使用过的 build 基本配置(不断更新中。。。