Android CommunityAndroid知识@IT·互联网

工信部要求国内Android统一消息推送标准

2017-06-04  本文已影响2111人  Rance935

本文同步发布至博客、公众号androidparks

昨天泰尔终端实验室发出了一条最新新闻,原文如下:

PingWest品玩6月3日报道,据工信部旗下泰尔终端实验室发布消息称,目前泰尔终端实验室联合包括华为、OPPO、vivo、小米、三星、魅族、金立、努比亚、谷歌(微博)、百度、阿里巴巴、腾讯、个推、极光等国外内主要相关企业共同制定安卓统一推送服务(Unified Push Service,简称UPS)技术标准,旨在为国内的消息推送服务建立统一的标准,为终端用户提供更好的手机使用体验,为应用开发者更好解决消息推送需求,并取得了阶段性成果。
具体来说,未来将由终端厂商提供系统级推送服务(类似APNS的唯一推送通道),确保App的推送消息接收;相应的不再允许各App在后台保留常连接,降低终端能耗、提升用户体验。与此同时,各终端厂商实现推送通道接口和功能统一,方便开发者接入。另外,第三方推送服务商原则上也遵循统一推送的标准,保证服务一致性,降低开发者学习成本。
从Android 8.0预览版的新特性来看,未来安卓App的后台活动将受到更严格的管控,消息推送将只能通过系统级推送通道下发。目前,各大手机厂商也已经提供或者正在研发基于各自系统平台的系统推送服务解决方案,如果国内安卓统一推送服务未来成为一项行业标准,无疑对于安卓手机用户是一大利好>

如果这一标准能够顺利实施对于不光对于Android开发者是一个天大的好消息,对于普通用户也是非常好的消息。大家都知道Android和iphone最大的区别就是Android比iphone要卡。其中原因之一就是推送机制的问题,接下来我就简单介绍一下目前各大平台对于推送的保活是怎样做的。

保活手段

当前业界的Android进程保活手段主要分为 黑、白、灰 三种,其大致的实现思路如下:

黑色保活:不同的app进程,用广播相互唤醒(包括利用系统提供的广播进行唤醒),所谓黑色保活,就是利用不同的app进程使用广播来进行相互唤醒。举个3个比较常见的场景:

没错,我们的Android手机就是一步一步的被上面这些场景给拖卡机的。

针对场景1,估计Google已经开始意识到这些问题,所以在最新的Android N取消了 ACTION_NEW_PICTURE(拍照),ACTION_NEW_VIDEO(拍视频),CONNECTIVITY_ACTION(网络切换)等三种广播,无疑给了很多app沉重的打击。

而开机广播的话,记得有一些定制ROM的厂商早已经将其去掉。

针对场景2场景3,因为调用SDK唤醒app进程属于正常行为,此处不讨论。但是在借助LBE分析app之间的唤醒路径的时候,发现了两个问题:

  1. 很多推送SDK也存在唤醒app的功能
  2. app之间的唤醒路径真是多,且错综复杂

我把自己使用的手机测试结果给大家围观一下(我的手机是小米4C,刷了原生的Android5.1系统,且已经获得Root权限才能查看这些唤醒路径

15组相互唤醒路径


全部唤醒路径


我们直接点开 简书 的唤醒路径进行查看

可以看到以上3条唤醒路径,但是涵盖的唤醒应用总数却达到了23+43+28款,数目真心惊人。请注意,这只是我手机上一款app的唤醒路径而已,到了这里是不是有点细思极恐。

当然,这里依然存在一个疑问,就是LBE分析这些唤醒路径和互相唤醒的应用是基于什么思路,我们不得而知。所以我们也无法确定其分析结果是否准确,如果有LBE的童鞋看到此文章,不知可否告知一下思路呢?但是,手机打开一个app就唤醒一大批,我自己可是亲身体验到这种酸爽的......

<div class="image-package">

</div>

白色保活:启动前台Service
打印出指定包名的所有进程中的Ser
vice信息,看下有没有 isForeground=true 的关键信息。如果通知栏没有看到属于app的 Notification 且又看到 isForeground=true 则说明了,此app利用了这种灰色保活的手段。
下面分别是我手机上微信、qq、支付宝、陌陌的测试结果,大家有兴趣也可以自己验证一下。


微信
白色保活手段非常简单,就是调用系统api启动一个前台的Service进程,这样会在系统的通知栏生成一个Notification,用来让用户知道有这样一个app在运行着,哪怕当前的app退到了后台。如下方的LBE和QQ音乐这样:
灰色保活:利用系统的漏洞启动前台Service
灰色保活,这种保活手段是应用范围最广泛。它是利用系统的漏洞来启动一个前台的Service进程,与普通的启动方式区别在于,它不会在系统通知栏处出现一个Notification,看起来就如同运行着一个后台Service进程一样。这样做带来的好处就是,用户无法察觉到你运行着一个前台进程(因为看不到Notification),但你的进程优先级又是高于普通后台进程的。那么如何利用系统的漏洞呢,大致的实现思路和代码如下:
思路一:API < 18,启动前台Service时直接传入new Notification();
public class GrayService extends Service {
    private final static int GRAY_SERVICE_ID = 1001;
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (Build.VERSION.SDK_INT < 18) {
            startForeground(GRAY_SERVICE_ID, new Notification());//API < 18 ,此方法能有效隐藏Notification上的图标
        } else {
            Intent innerIntent = new Intent(this, GrayInnerService.class);
            startService(innerIntent);
            startForeground(GRAY_SERVICE_ID, new Notification());
        }
        return super.onStartCommand(intent, flags, startId);
    }

思路二:API >= 18,同时启动两个id相同的前台Service,然后再将后启动的Service做stop处理;

    /**
     * 给 API >= 18 的平台上用的灰色保活手段
     */
    public static class GrayInnerService extends Service {

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            startForeground(GRAY_SERVICE_ID, new Notification());
            stopForeground(true);
            stopSelf();
            return super.onStartCommand(intent, flags, startId);
        }

    }
}

代码大致就是这样,能让你神不知鬼不觉的启动着一个前台Service。其实市面上很多app都用着这种灰色保活的手段,什么?你不信?好吧,我们来验证一下。流程很简单,打开一个app,看下系统通知栏有没有一个 Notification,如果没有,我们就进入手机的adb shell模式,然后输入下面的shell命令

dumpsys activity services PackageName

手Q

支付宝

陌陌

其实Google察觉到了此漏洞的存在,并逐步进行封堵。这就是为什么这种保活方式分 API >= 18 和 API < 18 两种情况,从Android5.0的ServiceRecord类的postNotification函数源代码中可以看到这样的一行注释

当某一天 API >= 18 的方案也失效的时候,我们就又要另谋出路了。需要注意的是,使用灰色保活并不代表着你的Service就永生不死了,只能说是提高了进程的优先级。如果你的app进程占用了大量的内存,按照回收进程的策略,同样会干掉你的app。感兴趣于灰色保活是如何利用系统漏洞不显示 Notification 的童鞋,可以研究一下系统的 ServiceRecord、NotificationManagerService 等相关源代码,因为不是本文的重点,所以不做详述。

到这里基本就介绍完了 黑、白、灰 三种实现方式,仅仅从代码层面去讲保活是不够的,我希望能够通过系统的进程回收机制来理解保活,这样能够让我们更好的避免踩到进程被杀的坑。

熟悉Android系统的童鞋都知道,系统出于体验和性能上的考虑,app在退到后台时系统并不会真正的kill掉这个进程,而是将其缓存起来。打开的应用越多,后台缓存的进程也越多。在系统内存不足的情况下,系统开始依据自身的一套进程回收机制来判断要kill掉哪些进程,以腾出内存来供给需要的app。这套杀进程回收内存的机制就叫 Low Memory Killer ,它是基于Linux内核的 OOM Killer(Out-Of-Memory killer)机制诞生。

了解完 Low Memory Killer,再科普一下oom_adj。什么是oom_adj?它是linux内核分配给每个系统进程的一个值,代表进程的优先级,进程回收机制就是根据这个优先级来决定是否进行回收。对于oom_adj的作用,你只需要记住以下几点即可:

那么我们如何查看进程的oom_adj值呢,需要用到下面的两个shell命令

ps | grep PackageName //获取你指定的进程信息

这里是以我写的demo代码为例子,红色圈中部分别为下面三个进程的ID

UI进程:com.clock.daemon
普通后台进程:com.clock.daemon:bg
灰色保活进程:com.clock.daemon:gray

当然,这些进程的id也可以通过AndroidStudio获得

<div class="image-package">

</div>

接着我们来再来获取三个进程的oom_adj

cat /proc/进程ID/oom_adj

从上图可以看到UI进程和灰色保活Service进程的oom_adj=0,而普通后台进程oom_adj=15。到这里估计你也能明白,为什么普通的后台进程容易被回收,而前台进程则不容易被回收了吧。但明白这个还不够,接着看下图

上面是我把app切换到后台,再进行一次oom_adj的检验,你会发现UI进程的值从0变成了6,而灰色保活的Service进程则从0变成了1。这里可以观察到,app退到后台时,其所有的进程优先级都会降低。但是UI进程是降低最为明显的,因为它占用的内存资源最多,系统内存不足的时候肯定优先杀这些占用内存高的进程来腾出资源。所以,为了尽量避免后台UI进程被杀,需要尽可能的释放一些不用的资源,尤其是图片、音视频之类的

从Android官方文档中,我们也能看到优先级从高到低列出了这些不同类型的进程:Foreground processVisible processService processBackground processEmpty process。而这些进程的oom_adj分别是多少,又是如何挂钩起来的呢?推荐大家阅读下面这篇文章:

http://www.cnblogs.com/angeldevil/archive/2013/05/21/3090872.html

总结

最后来做个小小的总结。进程保活的根本方案终究还是回到了性能优化上,进程永生不死终究是个彻头彻尾的伪命题!然而各个平台为保证推送及时准确费尽了心思,这也导致Android手机耗电严重,卡顿等现象。

如果这次工信部能够顺利统一Android的推送信道,对于Android开发者和Android系统使用者都是一个天大的福利。

本文作者:Rance935本文出处:工信部要求国内Android统一消息推送标准转载请在开头注明作者详细信息和本文出处
欢迎关注我的微信公众号和QQ群,分享Android 开发和互联网内容
Android技术分享:群号534813930
微信号:androidparks
公众号:AndroidParks

上一篇下一篇

猜你喜欢

热点阅读