基于个推多产商sdk封装踩坑实录

最近整合了一下公司这边基于个推的推送功能,原来公司这边有3个app,每个app都有固定的人员来进行维护,3个app都有基于个推的推送功能,但由于是不同开发人员维护导致的一个问题就是,每个app都有自己的一套代码来实现推送功能没,实际上3个app涉及到的推送功能并没有特殊的定制化,完全可以考虑由固定的开发人员编写一个基于个推sdk的推送模块,一来减少相同功能的开发人力成本,二来可以规范代码减少后期的维护成本。因此才有了这篇文章,基于个推sdk的推送模块。

官网下载sdk

这一步应该是比较简单的,相关的个推推送可以在个推开发者网站找到相应的文档,集成过推送功能的应该都比较熟悉这个网站。注册完成之后,可以在sdk下载页面找到对应的android版本,下载解压后即可看到这样的结构

QQ截图20190711143758.png

demo工程:对于文档中的文件还是想着重说一下,虽然在接入文档中已经有相应的接入说明,但从自己实际的接入经历来看,接入文档存在过于老旧和实际最终的集成效果存在一定的差异。先来大致讲下各个文档作用,demo工程打开之后如图所示
QQ截图20190711144706.png

,直接关注最后一个文档即可,第一个文档是集成到eclipse使用的,现在android开发早已迁移到studio。第二个文档是本地集成使用的,也就是不依赖gralde的dependencies功能,将个推sdk所需的jar包都保存到本地,包括使用到的一些资料文件,也是一种过时的做法。只有第三个文档才最符合平常的开发习惯,以gradle依赖的形式集成sdk。

接入文档:存放了接入sdk时的文档说明,一般的集成关注这几个即可
QQ截图20190711145938.png

中间两个文档实际上大同小异都是接入说明,但两个文档从我实际接入来看都有点过时,存在一定的误导性,强烈建议官方能重视下文档的编写。比如文档中会有说明在集成的时候需要导入一些so库,导入特定的资源,布局等,但在实际集成中并不需要用到这些,只要你导入demo工程中的代码就可以发现,demo和实际的集成文档说明存在较大的出入,自己实际集成还是以demo工程为准,文档起到辅助查看的作用。文档存在一个比较大的问题就是会把三种集成方式(maven,eclipse,offical)全部混杂在一起,导致集成的时候引入一些不必要的资源。

资源文档:存在了各种架构下的so库,资源文档,jar等,实际上该文档是针对demo工程中的offical和clipse来使用的,如果你直接使用的maven集成,这个文档是可以直接忽略的。是的so库也是可以直接忽略的,新版的个推sdk已经不需要导入这些so,看下demo工程里面的代码就可以知道。

个人建议集成文档不可不看,但需要结合demo工程查看。demo代码还是比较直观,没什么坑的地方,先把demo调试通,能够收到使用个推开发者平台收到的消息就算成功了,关于在个推创建app应用,如何推送可以在文档找到相关操作,照做即可。这里说下个推开发者平台提供的两种推送形式.
QQ截图20190711153110.png

两种通知

推送通知:个推默认的推送形式,输入标题内容后正常接入情况下即可收到推送。点击通知会跳转到app的启动页。一般这种通知用来测试接入sdk是否成功。
透传消息:推送的消息可以自定义,可以传送json格式,客户端在````onReceiveMessageData```即可收到消息,这种推送最大的特点就是只会调用onReceiveMessageData收到消息,收到后的具体行为可以由用户定制,包括弹通知,跳转到什么页面都需要客户端自己来处理。

正常情况下接入个推sdk后,使用以上两种形式都是可以收到消息的。按照自己的集成经验这里没有什么坑,如果收不到消息,查看下手机设置中的推送通知是否打开。

个推sdk免费版问题

上面所述都是基于免费版sdk,免费版比较大的一个问题就是杀死进程后再进行推送基本无法收到消息。所以公司这边在接入免费版sdk后运行一段时间后开始接入个推的收费版sdk,基于多渠道的个推sdk,最大的特点就是保障了推送到达率,在杀死进程后仍然可以收到推送消息。多渠道推送按照个推的文档来说就是在杀死进程后,如果个推sdk无法收到消息就是根据不同的手机品牌产商使用他们的推送方案,从而保障推送的到达率。

多渠道个推sdk

据我了解实际上免费版的个推sdk已经具备了多渠道推送的能力,但是如果要开通该功能是需要和个推官方协商谈价,可以和个推技术支持人员沟通开通一个免费试用的应用,
QQ截图20190711161247.png

开通多渠道功能之后的应用会具备特殊机型这个选项,在内部进行配置即可让app具备该平台的推送能力。一般如果你要开通多渠道推送功能,技术支持人员会另外发送一份技术文档,里面会包括各个手机品牌商的接入文档
QQ截图20190711161528.png
,具体如何接入对应平台可以参考这些文档。

和集成免费的个推sdk文档存在一样的问题就是多渠道集成的文档一样存在混杂的情况,文档中的很多配置根据自己实际开发都是冗余的,直接看个推技术支持发送的demo工程。

对比免费版个推sdk

收费版的demo工程和免费版的demo工程,整体代码相似度非常高,不同的地方在于gradle的dependencies中包含了

    compile 'com.getui:hwp:1.0.5'
    compile 'com.assist:oppo:1.0.2'
    compile 'com.assist:vivo:1.0.1'
    compile 'com.getui:mzp:1.0.7'
    compile 'com.getui:xmp:1.0.4'

以及manifestPlaceholders中key的配置。

集成多渠道推送之后仍然收不到消息

这是在接入过程中遇到的一个问题,不管是接入文档还是demo工程都没有明确指出这个需要注意的地方,都想吐槽官方写文档的技术人员,可能他自己都没注意到这个问题吧,不管是接入免费版的demo工程还是收费版的demo工程你可以发现就是工程是不带keystore的,但在个推注册对应的app时需要提供一个sha256,这就需要使用到keystore,通过studio即可生成一个。生成完毕之后一定要记得在对应的debug或者release下进行签名的配置!!!,如果忘记配置会导致什么现象,那就是在应用进程存在的情况下不管有没有配置签名推送都可以正常收到,一旦杀死进程即使接入了多渠道推送也无法收到消息。照理来说应该是如果没有配置签名,不管进程是否存在均应该收不到消息才对,个推sdk就神奇在没有配置签名只会对多渠道推送造成影响,而且不管是demo代码还是接入文档都没有明确指出该问题。导致自己在接入多渠道推送在这个坑里转了半天,和个推技术支持多次沟通,他们也没发现这个问题!!,最终还是自己灵光乍现把这个问题给解决掉。

另一个比价坑的地方在于在集成个推多渠道推送,以华为为例,在杀死进程收到推送之后,点击通知消息会跳转到一个自定义的activity中去处理消息,对于这个activity需要在manifest中进行相应的配置

<activity
            android:name="相应activity名字"
            android:configChanges="orientation|keyboardHidden|screenSize"
            android:exported="true">
            <intent-filter>
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

否则华为手机将存在收不到消息的情况,但是这么个重要的配置信息并不存在客户端文档中,而是存在服务端文档中!!!,这种骚操作不太明白个推的开发是怎么想的,所以记得及时和服务端人员沟通。

如果解决掉上述两个问题,那么其余的问题应该就不大了,记得到特定的开发者平台注册你的demo应用,那么你的demo就具备了杀死进程也能保证推送到达率。这部分直接看文档即可。应该说华为开发者平台注册是最方便的,使用支付宝的芝麻信用即可注册成功,其余开发者平台需要手持身份证,需要填写银行卡,人工核审都比较麻烦。

如果上述问题都解决了那么推送应该是能收到消息的,但自己这边因为封装模块实现逻辑上的关系,出现了一个在8.0以上手机上无法收到消息的问题,这个问题不一定都会遇到,只有你在使用广播没有做好8.0系统兼容处理的时候可能会遇到,简单来说就是8.0系统上不支持隐式广播,发送的广播无法接受到onreceive回调。因为在封装推送的时候使用到了隐式广播所以导致8.0手机接受不到消息的问题,解决办法就是使用显式广播,如

Intent broadCast = new Intent();
        broadCast.setAction(activity.getString(R.string.custom_transit_action));
        broadCast.setPackage(activity.getPackageName());      //指定包名后隐式广播就变成了显式广播
        broadCast.putExtra(TRANSIT_MSG, msg);
        activity.sendBroadcast(broadCast);

发送推送

上述问题都解决完毕之后发送消息就很简单了,
QQ截图20190711170958.png

,关于intent如何填写,可以在服务端开发者文档找到,不难。如果杀死进程之后,点击推送能收到消息那么集成算是成功了。

中转activiy配置

杀死进程之后能收到消息就已经说明我们的推送功能可以正常工作了,接下来就是对一些细节问题的处理,比如处理消息的activity如何设置,因为当收到推送消息之后一般需要一个中转activity对收到的消息进行处理,处理完毕之后跳转到对应的详情页面,默认情况下如果不对该中转activity进行处理,就会出现点击消息后短时间的黑屏,原因就是中转activity没有设置background。根据自己理解应该有四种做法,一:中转activity即开屏页,点击之后就会启动app,在开屏页进行中转逻辑处理。
二:中转activity是一个特定的页面,可以考虑让ui做成一个正在跳转的页面。三:中转activity是一个透明的页面,点击之后用户无感知。四:中转activity是一个"不展示"的页面
第一种做法,需要修改原有的逻辑,开销比较大。第二种做法应该是成本最低实现起来最简单的方式。第三种,就略坑一点了,可能会出现展示的透明页面并非全透明。 如系统主题Theme.Trunslucent.NoTitleBar实际上的效果是系统状态栏一直为黑色,效果不理想,参考了一些透明主题的设置

<style name="TranslucentTheme" parent="Theme.AppCompat.NoActionBar">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>

实测之后发现在demo中确实可以起作用,但是在应用到推送模块之后就发现有问题,每次点击通知栏会出现状态栏闪一下,有一个状态栏从黑色到消失的过程,虽然时间很短但看着很不舒服,着实让人头大,不过最终结果还算满意经过不断测试发现了一个靠谱的设置,公司这边测试机测试后均正常,纠结了半天的问题总算解决了。完整的配置如下

<style name="TranslucentTheme" parent="Theme.AppCompat.NoActionBar">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:statusBarColor">@android:color/transparent</item>
        <item name="android:windowAnimationStyle">@null</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:backgroundDimEnabled">false</item>
    </style>

第四种做法自己也是事后才知道的方法,还未验证是否可行,但感觉应该可靠,所以写到了文章里面,这种方法其实很简单给中转的activity配置一个"@android:style/Theme.NoDisplay"主题即可,具体可以看下做了什么

<style name="Theme.NoDisplay">
        <item name="windowBackground">@null</item>
        <item name="windowContentOverlay">@null</item>
        <item name="windowIsTranslucent">true</item>
        <item name="windowAnimationStyle">@null</item>
        <item name="windowDisablePreview">true</item>
        <item name="windowNoDisplay">true</item>
    </style>

有点类似于透明activity的配置,但有一个比较大的区别在于,使用了这种方法的activity务必在onresume之前需要调用finish方法,否则会导致直接崩溃的问题。该主题的activity主要的作用就是处理一些逻辑,逻辑处理完毕那么该activity的任务也就完成了,比如airbnb的路由库DeepLinkDispatch,在得到路径path之后,会在这种主题的activity中决定最终的路由activity,然后finish掉中转activity

@DeepLinkHandler({SampleModule.class, LibraryDeepLinkModule.class})
public class DeepLinkActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DeepLinkDelegate deepLinkDelegate = new DeepLinkDelegate(
                new SampleModuleLoader(), new LibraryDeepLinkModuleLoader());
        deepLinkDelegate.dispatchFrom(this);        //路由到指定的activity
        finish();                                                         //完成任何finish掉自身
    }
}

透明activity 8.0手机崩溃问题

这个问题比较好解决,如何你的透明activity进行了横竖屏方向的配置在8.0的手机会直接发送崩溃,崩溃消息如下

java.lang.IllegalStateException: Only fullscreen activities can request orientation

这个问题很容易复现,解决方法也很简单,去除透明activity的android:screenOrientation="portrait"配置即可,最好加上android:configChanges="orientation|keyboardHidden|screenSize"这个配置,防止透明activity横竖屏切换导致的重建问题。

巧用startActivities

startActivity都使用过,但是startActivities可能很多人并不一定熟悉,startActivities需要传入一个intent数组,然后根据intent数组中的对象依次启动activity,这是一个非常好用的api。比如以下场景收到推送消息之后,点击消息后跳转到了新闻页面,这时点击新闻页面的关闭按钮会返回到app的首页,这是一个非常常见的功能。如果不使用startActivities该如何实现。

直接启动首页activity和新闻activity,这样点击关闭之后就回到了首页。但这种做法有一点问题的地方在于如果首页的逻辑比较复杂的情况下会导致开启首页耗时比较大,间接影响倒新闻页面的启动。另一种做法就是先开启新闻页面,在新闻页面点击关闭的时候再启动首页,这种做法引起的一个问题就是需要在新闻页面的关闭按钮进行额外的逻辑处理,需要判断是否是推送消息拉起的该页面,处理起来也会略麻烦。基于上述考虑我查了下系统的api发现了这么个方法,使用该api可以优雅的处理上述问题,只需传递首页intent和新闻intent作为参数即可,startActivities的启动页面的顺序和intent数组中存入的顺序是相反的,比如存入intentA,intentB,系统会先启动B,等B关闭之后再启动A,完美契合这里的需求。

FLAG_ACTIVITY_NEW_TASK

这个标志位应该说都非常的熟悉,当启动一个activity是通过非Activity调用的时候如application,广播,contentprovider则需要为intent加上FLAG_ACTIVITY_NEW_TASK,否则会出现这样的崩溃

throw new AndroidRuntimeException(
                    "Calling startActivity() from outside of an Activity "
                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                    + " Is this really what you want?");

原因主要就是非activity的context是通过contextimpl来调用startactivity方法,该方法内部会对intent的FLAG_ACTIVITY_NEW_TASK标志位进行判断,如果没有对应标志位则抛出异常。

但关于这个标志位其实有一个非常有意思的事情就是,在sdk N 到sdk O-MR1之间如果intent不添加FLAG_ACTIVITY_NEW_TASK其实也不会崩溃!!,关于这个解释可以在高于sdk O-MR1的版本中找到答案,以9.0源码为例


QQ截图20190718092824.png

官方注释中已经写得很明白了,在sdk N 到sdk O-MR1之间是存在bug的!!那这个具体的bug是什么,以sdk 27中源码对比下就知道了


QQ截图20190718093225.png

注意红框中的判断条件是非空,而在高版本的源码中可以发现这个判断变成了为空。实际上调用startactivity(intent)默认传入的这个options就是为空,而google开发人员在sdk N 到sdk O-MR1之间居然手抖将这个条件写成了非空!!!所以闹出了sdk N 到sdk O-MR1这么个乌龙出来。

但不管是否有乌龙加上FLAG_ACTIVITY_NEW_TASK肯定是没有问题的。

路由跳转问题

推送模块作为一个单独的模块必须做到和其他业务模块的解耦,根据推送到达的消息跳转到指定的页面,本身是和业务逻辑有一定的关联,如何让这块逻辑独立于业务模块,这里采用了deeplink方案,简单的说就是给每个需要跳转的activity配置相应的data
,类似

<intent-filter>
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.BROWSABLE" />
                <category android:name="android.intent.category.DEFAULT" />

                <data
                    android:host="www.baidu.com"
                    android:path="/test/test2"
                    android:scheme="customScheme" />
            </intent-filter>

通过data中的唯一路径找到相应的activity,这个原理就有点类似一个ip网址对应一个具体页面,具体代码

            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_VIEW);
            String newPath = handlePath(path);
            intent.setData(Uri.parse(newPath));
            startActivity(intent);

根据推送得到的path转换成intent-filter能识别的path,实测发现 intent.setAction(Intent.ACTION_VIEW);必须添加,否则个别手机将导致无法跳转到对应activity的情况。

android:scheme中的值不要使用http或者https,建议使用可用来标志自身应用的唯一名称。scheme使用http或者https将导致的一个问题就是即使newPath是一个唯一标志Activity的路径,也会导致startActivity不能跳转到该activity。scheme是http或者https的情况下不仅会对startActivity的最终跳转产生影响,在进行queryIntentActivities查询具体的ResolveInfo时也会造成影响。关于这一点自己参考的是Android M queryIntentActivities return null list 蹲坑记,也是自己在实际开发中遇到的问题,解决方法见作者文章。

总结

上述就是自己在基于个推的多渠道推送封装过程中遇到的一些问题,包括了从集成sdk到封装模块遇到的几个典型问题。目前已经在其中一个公司app正式集成了该模块,具体效果如何还需等待线上验证的最终效果。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,302评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,232评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,337评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,977评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,920评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,194评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,638评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,319评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,455评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,379评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,426评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,106评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,696评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,786评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,996评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,467评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,043评论 2 341

推荐阅读更多精彩内容