Android通知欄踩坑記

1.動(dòng)態(tài)注冊(cè)廣播無(wú)法觸發(fā)點(diǎn)擊事件

場(chǎng)景:通知欄的點(diǎn)擊事件通常會(huì)采用PendingIntent.getBroadcast方式,這個(gè)時(shí)候如果采用動(dòng)態(tài)注冊(cè)廣播,將會(huì)導(dǎo)致,點(diǎn)擊事件無(wú)法響應(yīng)。
解決方案:不要采取動(dòng)態(tài)注冊(cè)廣播的方式,采用清單文件注冊(cè)廣播即可

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

//點(diǎn)擊事件接收的廣播
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, "點(diǎn)擊事件", Toast.LENGTH_SHORT).show();
        }
    }
}

...省略RemoteViews創(chuàng)建部分代碼
Intent clickIntent = new Intent(this, ClickReceiver.class);
clickIntent.setAction(ClickReceiver.ACTION_SWITCH_CLICK);
 //注意:getBroadcast第二個(gè)參數(shù) requestCode 值一定不能重復(fù),否則會(huì)導(dǎo)后面intent覆蓋掉前面的intent
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, requestCode, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
contentView.setOnClickPendingIntent(R.id.tv_notification_name, pendingIntent);
點(diǎn)擊事件_廣播.jpg

2.通知欄頻繁刷新導(dǎo)致 ANR、Crash、手機(jī)卡頓

使用場(chǎng)景:比如下載進(jìn)度條頻繁更新。

解決方式:最短刷新時(shí)間限制(比如刷新時(shí)機(jī)大于1s) + 進(jìn)度值更新時(shí)再更新通知欄(減少無(wú)用刷新)

3.部分手機(jī)不支持style api

比如金立S5、魅族MX5(顯示不全、文字遮擋)、小米系統(tǒng)、華為Mate系列。這里因?yàn)槭掷餀C(jī)型限制,style測(cè)試并不能覆蓋到大量的測(cè)試機(jī),這邊感興趣的話(huà)可以參考網(wǎng)易考拉關(guān)于style的機(jī)型支持統(tǒng)計(jì)。

魅族MX5_大圖模式.jpg

4.部分手機(jī)不支持BigPictureStyle

比如魅族、小米系統(tǒng),可以采用CustomBigContentView進(jìn)行適配

魅族手機(jī)_BigPictureStyle.jpg
小米手機(jī)_BigPictureStyle.png

CustomBigContentView適配后:

魅族手機(jī)_CustomBigContentView.jpg

5.自定義布局的背景色適配

常見(jiàn)方案主要有以下兩種。
a)比如360和西瓜視頻,設(shè)置固定背景色方式。這種固定背景色方式在部分風(fēng)格不搭的手機(jī)上面顯示如下這種效果,可以看出明顯比較突兀。


三星SM-N9008.jpg

b)根據(jù)手機(jī)通知欄背景色進(jìn)行動(dòng)態(tài)適配方式

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;
    }

調(diào)用方式

boolean isDark = isDarkNotificationTheme(this);
if (isDark) {
    //其中MIUI系統(tǒng)需要單獨(dú)兼容
    if (Utils.isMIUI(this)) {
        contentView = 透明布局,深色字體
     } else {
       contentView = 白色背景,深色字體
      }
  } else {
        contentView = 透明布局,淺色字體
  }

適配后的三星SM-N9008:


三星SM-N9008.png

6.小圖標(biāo)適配

小圖標(biāo)各個(gè)版本可能會(huì)有所區(qū)別,不同廠商之間的默認(rèn)樣式也會(huì)存在一些差別。這里統(tǒng)計(jì)了小圖標(biāo)版本歷史上的具體樣式
android <5.0

5.0<= android <7.0

android >= 7.0

關(guān)于小圖標(biāo)適配,官方已有詳細(xì)文檔說(shuō)明,現(xiàn)摘錄一些重點(diǎn)如下:

  • small icon 必須是帶 Alpha 透明通道的 PNG 圖片。

  • 背景必須是透明的。

  • 圖形必須是白色。不要上傳其他顏色的圖形,而應(yīng)該通過(guò) setColor 來(lái)染色

  • 周?chē)灰肆暨^(guò)多的 padding

官方推薦小圖標(biāo)樣式.jpg

不采用上述方法,可能會(huì)導(dǎo)致適配失敗案例:

小圖標(biāo).jpg
狀態(tài)欄圖標(biāo).jpg

6.android O新增渠道概念,即設(shè)置targetSDK >=26 如果不使用渠道,將無(wú)法展示通知欄

clipboard.png

7.OPPO為防止第三方應(yīng)用推送過(guò)多無(wú)效通知,OPPO手機(jī)將默認(rèn)關(guān)閉應(yīng)用消息推送,用戶(hù)可根據(jù)自己需求,通過(guò)開(kāi)關(guān)手動(dòng)開(kāi)啟。

OPPO會(huì)對(duì)某些app開(kāi)啟通知欄白名單,以下統(tǒng)計(jì)了部分app

  • 默認(rèn)開(kāi)啟的app:今日頭條、騰訊新聞、天天快報(bào)
  • 默認(rèn)關(guān)閉的app:新浪新聞、網(wǎng)易新聞、趣頭條、搜狐新聞、澎湃新聞、鳳凰新聞、惠頭條

接下來(lái),我們對(duì)非白名單app如何適配權(quán)限進(jìn)行研究。

新浪新聞 有彈框提示,引導(dǎo)用戶(hù)打開(kāi)通知欄權(quán)限。如下圖所示:

網(wǎng)易新聞、搜狐新聞 打開(kāi)app提示使用移動(dòng)網(wǎng)絡(luò)權(quán)限,用戶(hù)點(diǎn)擊“始終允許”后,開(kāi)啟通知欄的權(quán)限

這里參考了新浪新聞的做法,主動(dòng)引導(dǎo)用戶(hù)開(kāi)啟權(quán)限

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, "您已打開(kāi)通知欄權(quán)限,無(wú)需再次打開(kāi)", Toast.LENGTH_SHORT).show();
      }
   }
screenshot_2018-09-26-16-43-45-85.png

8.小米MIUI9之后增加了通知過(guò)濾功能,通過(guò)算法將一些不常用、不重要的通知欄自動(dòng)降級(jí)收縮到通知欄底部“不重要通知”里面。

通知過(guò)濾功能的評(píng)分模型會(huì)通過(guò)多個(gè)維度判斷通知重要與否,包括但不限于:

  • 通知的文本:即標(biāo)題(title) 和描述(description)
  • 該應(yīng)用的通知在該設(shè)備上的歷史點(diǎn)擊率
  • 用戶(hù)的屬性(如年齡、性別、地域等)

在這些維度中,目前影響最大的維度是歷史點(diǎn)擊率,所以?xún)?yōu)化方向:

  • app前期的歷史點(diǎn)擊率非常重要,app安裝后,前期盡量減少推送頻率,提高內(nèi)容質(zhì)量等,只要前期這些通知有1條被點(diǎn)擊,后續(xù)通知就有很大概率成為重要通知。比如熱點(diǎn)事件、紅包領(lǐng)取之類(lèi)。

  • 歷史點(diǎn)擊率針對(duì)的是設(shè)備。這就要求業(yè)務(wù)使用個(gè)性化的推送策略,避免全量推送,以減小被列為不重要通知的概率。

關(guān)于通知過(guò)濾

  • 通知過(guò)濾是指將不重要的通知收納進(jìn)一個(gè)統(tǒng)一的頁(yè)面。點(diǎn)擊不重要通知可直接查看所有被折疊的通知。范圍包括所有預(yù)裝軟件和非預(yù)裝軟件。

  • 從小米目前的數(shù)據(jù)統(tǒng)計(jì)情況來(lái)看,即便是在二級(jí)頁(yè),也有很多曝光、點(diǎn)擊行為,如果用戶(hù)點(diǎn)擊率回升,系統(tǒng)會(huì)再次將該app通知欄設(shè)置為重要通知。

  • 同一個(gè)app的不同通知欄,也有可能有的在重要通知欄,有的在不重要通知欄里面

點(diǎn)開(kāi)“不重要通知”,展開(kāi)列表

9.關(guān)于自定義布局的限制:

  • Android系統(tǒng)可以將自定義布局通過(guò)setContent(7.X系統(tǒng)推薦使用setCustomContentView)設(shè)置到Notification.Builder中,來(lái)實(shí)現(xiàn)樣式的更變。

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

FrameLayout
LinearLayout
RelativeLayout
GridLayout

這些布局下面只支持幾種視圖控件:

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

10.android O增加了桌面角標(biāo)的原生支持

注意:國(guó)產(chǎn)手機(jī)在這方面的適配尚未統(tǒng)一,大多采用自己的設(shè)計(jì)風(fēng)格。比如小米采用仿蘋(píng)果的 紅點(diǎn)數(shù)字顯示,不支持長(zhǎng)按顯示詳情。OPPO 支持桌面圓角小圖標(biāo),但不支持長(zhǎng)按顯示通知詳情

桌面圖標(biāo)小圓點(diǎn).png
長(zhǎng)按桌面圖標(biāo).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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,355評(píng)論 25 708
  • 用兩張圖告訴你,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 14,121評(píng)論 2 59
  • 最近在讀《滄浪之水》,很適合我們這種即將步入社會(huì)的傻白甜。從小都是受著正面教育長(zhǎng)大,接觸到的社會(huì)現(xiàn)實(shí)也確實(shí)與理想存...
    園文蕤閱讀 345評(píng)論 0 0
  • 2018年3月15日 星期四 陰轉(zhuǎn)晴 好些日子沒(méi)寫(xiě)日記了,心里有點(diǎn)不好意思,有時(shí)覺(jué)得自己這個(gè)當(dāng)媽...
    宋雯楠媽媽閱讀 257評(píng)論 0 0
  • 易效能第二階段畢業(yè)論文 短暫而又有意義的28天踐行時(shí)間管理二階段又結(jié)束了,總結(jié)了自己的收獲和進(jìn)步,具體如下: 1....
    共贏_0d67閱讀 203評(píng)論 0 1

友情鏈接更多精彩內(nèi)容