Android通知栏踩坑记

1.动态注册广播无法触发点击事件

场景:通知栏的点击事件通常会采用PendingIntent.getBroadcast方式,这个时候如果采用动态注册广播,将会导致,点击事件无法响应。
解决方案:不要采取动态注册广播的方式,采用清单文件注册广播即可

//AndroidManifest清单文件配置
<receiver android:name=".ClickReceiver"/>

//点击事件接收的广播
public class ClickReceiver extends BroadcastReceiver {
    public static final String ACTION_SWITCH_CLICK = "notification.toutiao.com.notificationapp.CLICK";

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (ACTION_SWITCH_CLICK.equals(action)) {
            Toast.makeText(context, "点击事件", Toast.LENGTH_SHORT).show();
        }
    }
}

...省略RemoteViews创建部分代码
Intent clickIntent = new Intent(this, ClickReceiver.class);
clickIntent.setAction(ClickReceiver.ACTION_SWITCH_CLICK);
 //注意:getBroadcast第二个参数 requestCode 值一定不能重复,否则会导后面intent覆盖掉前面的intent
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, requestCode, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
contentView.setOnClickPendingIntent(R.id.tv_notification_name, pendingIntent);
点击事件_广播.jpg

2.通知栏频繁刷新导致 ANR、Crash、手机卡顿

使用场景:比如下载进度条频繁更新。

解决方式:最短刷新时间限制(比如刷新时机大于1s) + 进度值更新时再更新通知栏(减少无用刷新)

3.部分手机不支持style api

比如金立S5、魅族MX5(显示不全、文字遮挡)、小米系统、华为Mate系列。这里因为手里机型限制,style测试并不能覆盖到大量的测试机,这边感兴趣的话可以参考网易考拉关于style的机型支持统计。

魅族MX5_大图模式.jpg

4.部分手机不支持BigPictureStyle

比如魅族、小米系统,可以采用CustomBigContentView进行适配

魅族手机_BigPictureStyle.jpg
小米手机_BigPictureStyle.png

CustomBigContentView适配后:

魅族手机_CustomBigContentView.jpg

5.自定义布局的背景色适配

常见方案主要有以下两种。
a)比如360和西瓜视频,设置固定背景色方式。这种固定背景色方式在部分风格不搭的手机上面显示如下这种效果,可以看出明显比较突兀。


三星SM-N9008.jpg

b)根据手机通知栏背景色进行动态适配方式

public static boolean isDarkNotificationTheme(Context context) {
        return isSimilarColor(Color.BLACK, getNotificationColor(context));
    }

    /**
     * 获取通知栏颜色
     * @param context
     * @return
     */
    public static int getNotificationColor(Context context) {
        Notification notification = null;
        try {
            NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
            notification = builder.build();
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (notification = null || notification.contentView==null){
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                return Color.BLACK;
            } else {
                return Color.WHITE;
            }
        }

        int layoutId=notification.contentView.getLayoutId();
        ViewGroup viewGroup= (ViewGroup) LayoutInflater.from(context).inflate(layoutId, null, false);
        if (viewGroup.findViewById(android.R.id.title)!=null) {
            return ((TextView) viewGroup.findViewById(android.R.id.title)).getCurrentTextColor();
        }
        return findColor(viewGroup);
    }

    private static boolean isSimilarColor(int baseColor, int color) {
        int simpleBaseColor=baseColor|0xff000000;
        int simpleColor=color|0xff000000;
        int baseRed= Color.red(simpleBaseColor)- Color.red(simpleColor);
        int baseGreen= Color.green(simpleBaseColor)- Color.green(simpleColor);
        int baseBlue= Color.blue(simpleBaseColor)- Color.blue(simpleColor);
        double value= Math.sqrt(baseRed*baseRed+baseGreen*baseGreen+baseBlue*baseBlue);
        if (value<180.0) {
            return true;
        }
        return false;
    }
    private static int findColor(ViewGroup viewGroupSource) {
        int color= Color.TRANSPARENT;
        LinkedList<ViewGroup> viewGroups=new LinkedList<>();
        viewGroups.add(viewGroupSource);
        while (viewGroups.size()>0) {
            ViewGroup viewGroup1=viewGroups.getFirst();
            for (int i = 0; i < viewGroup1.getChildCount(); i++) {
                if (viewGroup1.getChildAt(i) instanceof ViewGroup) {
                    viewGroups.add((ViewGroup) viewGroup1.getChildAt(i));
                }
                else if (viewGroup1.getChildAt(i) instanceof TextView) {
                    if (((TextView) viewGroup1.getChildAt(i)).getCurrentTextColor()!=-1) {
                        color=((TextView) viewGroup1.getChildAt(i)).getCurrentTextColor();
                    }
                }
            }
            viewGroups.remove(viewGroup1);
        }
        return color;
    }

调用方式

boolean isDark = isDarkNotificationTheme(this);
if (isDark) {
    //其中MIUI系统需要单独兼容
    if (Utils.isMIUI(this)) {
        contentView = 透明布局,深色字体
     } else {
       contentView = 白色背景,深色字体
      }
  } else {
        contentView = 透明布局,浅色字体
  }

适配后的三星SM-N9008:


三星SM-N9008.png

6.小图标适配

小图标各个版本可能会有所区别,不同厂商之间的默认样式也会存在一些差别。这里统计了小图标版本历史上的具体样式
android <5.0

5.0<= android <7.0

android >= 7.0

关于小图标适配,官方已有详细文档说明,现摘录一些重点如下:

  • small icon 必须是带 Alpha 透明通道的 PNG 图片。

  • 背景必须是透明的。

  • 图形必须是白色。不要上传其他颜色的图形,而应该通过 setColor 来染色

  • 周围不宜留过多的 padding

官方推荐小图标样式.jpg

不采用上述方法,可能会导致适配失败案例:

小图标.jpg
状态栏图标.jpg

6.android O新增渠道概念,即设置targetSDK >=26 如果不使用渠道,将无法展示通知栏

clipboard.png

7.OPPO为防止第三方应用推送过多无效通知,OPPO手机将默认关闭应用消息推送,用户可根据自己需求,通过开关手动开启。

OPPO会对某些app开启通知栏白名单,以下统计了部分app

  • 默认开启的app:今日头条、腾讯新闻、天天快报
  • 默认关闭的app:新浪新闻、网易新闻、趣头条、搜狐新闻、澎湃新闻、凤凰新闻、惠头条

接下来,我们对非白名单app如何适配权限进行研究。

新浪新闻 有弹框提示,引导用户打开通知栏权限。如下图所示:

网易新闻、搜狐新闻 打开app提示使用移动网络权限,用户点击“始终允许”后,开启通知栏的权限

这里参考了新浪新闻的做法,主动引导用户开启权限

public void openPermission() {
     if (!NotificationManagerCompat.from(this).areNotificationsEnabled()) {
         Intent localIntent = new Intent();
          localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
          localIntent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
          localIntent.setData(Uri.fromParts("package", BuildConfig.APPLICATION_ID, null));
          startActivity(localIntent);
      } else {
          Toast.makeText(this, "您已打开通知栏权限,无需再次打开", Toast.LENGTH_SHORT).show();
      }
   }
screenshot_2018-09-26-16-43-45-85.png

8.小米MIUI9之后增加了通知过滤功能,通过算法将一些不常用、不重要的通知栏自动降级收缩到通知栏底部“不重要通知”里面。

通知过滤功能的评分模型会通过多个维度判断通知重要与否,包括但不限于:

  • 通知的文本:即标题(title) 和描述(description)
  • 该应用的通知在该设备上的历史点击率
  • 用户的属性(如年龄、性别、地域等)

在这些维度中,目前影响最大的维度是历史点击率,所以优化方向:

  • app前期的历史点击率非常重要,app安装后,前期尽量减少推送频率,提高内容质量等,只要前期这些通知有1条被点击,后续通知就有很大概率成为重要通知。比如热点事件、红包领取之类。

  • 历史点击率针对的是设备。这就要求业务使用个性化的推送策略,避免全量推送,以减小被列为不重要通知的概率。

关于通知过滤

  • 通知过滤是指将不重要的通知收纳进一个统一的页面。点击不重要通知可直接查看所有被折叠的通知。范围包括所有预装软件和非预装软件。

  • 从小米目前的数据统计情况来看,即便是在二级页,也有很多曝光、点击行为,如果用户点击率回升,系统会再次将该app通知栏设置为重要通知。

  • 同一个app的不同通知栏,也有可能有的在重要通知栏,有的在不重要通知栏里面

点开“不重要通知”,展开列表

9.关于自定义布局的限制:

  • Android系统可以将自定义布局通过setContent(7.X系统推荐使用setCustomContentView)设置到Notification.Builder中,来实现样式的更变。

  • RemoteViews只支持4种基本的布局:

FrameLayout
LinearLayout
RelativeLayout
GridLayout

这些布局下面只支持几种视图控件:

AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView
ViewFlipper
ListView
GridView
StackView
AdapterViewFlipper

10.android O增加了桌面角标的原生支持

注意:国产手机在这方面的适配尚未统一,大多采用自己的设计风格。比如小米采用仿苹果的 红点数字显示,不支持长按显示详情。OPPO 支持桌面圆角小图标,但不支持长按显示通知详情

桌面图标小圆点.png
长按桌面图标.png

参考链接
1.https://dev.mi.com/console/doc/
2.https://open.oppomobile.com/wiki/doc#id=10171
3.https://developer.android.com/guide/practices/ui_guidelines/icon_design_status_bar
4.https://iluhcm.com/2017/03/12/experience-of-adapting-to-android-notifications/
5.https://mp.weixin.qq.com/s/Ez-G_9hzUCOjU8rRnsW8SA

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,384评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,680评论 2 59
  • 最近在读《沧浪之水》,很适合我们这种即将步入社会的傻白甜。从小都是受着正面教育长大,接触到的社会现实也确实与理想存...
    园文蕤阅读 287评论 0 0
  • 2018年3月15日 星期四 阴转晴 好些日子没写日记了,心里有点不好意思,有时觉得自己这个当妈...
    宋雯楠妈妈阅读 234评论 0 0
  • 易效能第二阶段毕业论文 短暂而又有意义的28天践行时间管理二阶段又结束了,总结了自己的收获和进步,具体如下: 1....
    共赢_0d67阅读 149评论 0 1