Notification之 – Android5.0實現原理(一)

Hly_Coder發表於2019-01-04

概述

寫這篇文章的起因是當我自定義通知欄樣式時,設定的view高度最終只能顯示為通知欄的預設高度(64dp),讓人十分困惑,於是研究了下原始碼。
除了解開真相意外,還了解到了包括bigview,pengdingintent的點選事件,通知欄限制等知識點,收穫頗多,在這裡分享給大家。希望能讓大家明白一個通知是如何新增到狀態列上面去的,如果遇到奇怪的問題不至於抓瞎。

前提

本文是以android5.0的原始碼為基礎的並且假設大家都知道notification的基本用法。

構造通知

在我們使用NotificationCompat.Builder物件設定完各種引數(小/大圖示,標題,內容等)後,最後會呼叫build方法來得到一個Notification,然後使用NotificationManager來發出通知。我們就先來看看build方法做了什麼事。
NotificationCompat是v4包中的一個類,做了android各個版本的相容,但是不論是哪個版本,最後build方法都是呼叫的Notification的build方法,所以我們直接看Notification的build方法。
frameworks/base/core/java/android/app/Notification.java

/**
 * Combine all of the options that have been set and return a new {@link Notification}
 * object.
 */
public Notification build() {
    ...    
    //設定通知的預設資訊
    Notification n = buildUnstyled();

   //設定通知的樣式資訊
    if (mStyle != null) {
        n = mStyle.buildStyled(n);
    }
    ...
    return n;
}複製程式碼

方法的註釋說得很清楚了,就是將所有的設定選項聯合在一起返回一個新的通知。

buildUnstyled

其中buildUnstyled()所說的預設資訊就是在android4.0以前還不能展開的時候所包括的所有資訊,包括大/小圖示,時間,標題,內容,自定義view等資訊。

/**
 * Apply the unstyled operations and return a new {@link Notification} object.
 * @hide
 */
public Notification buildUnstyled() {
    Notification n = new Notification();
    n.when = mWhen;
    n.icon = mSmallIcon;
    ...
    setBuilderContentView(n, makeContentView());
    n.contentIntent = mContentIntent;
    ...
    setBuilderBigContentView(n, makeBigContentView());
    setBuilderHeadsUpContentView(n, makeHeadsUpContentView());
    // Note: If you`re adding new fields, also update restoreFromNotitification().
    return n;
}複製程式碼

setBuilderContentView用於設定通知欄的ContentView屬性

private void setBuilderContentView(Notification n, RemoteViews contentView) {
    n.contentView = contentView;
    ...
}複製程式碼

makeContentView()是構造出所需要填充的view

private RemoteViews makeContentView() {
    if (mContentView != null) {
        return mContentView;
    } else {
        return applyStandardTemplate(getBaseLayoutResource());
    }
}複製程式碼

如果你沒有使用自定view,將會使用標準的模板樣式

private int getBaseLayoutResource() {
     return R.layout.notification_template_material_base;
}複製程式碼

這裡只講了setBuilderContentView(n, makeContentView());方法, 後面的setBuilderBigContentView(n, makeBigContentView());setBuilderHeadsUpContentView(n, makeHeadsUpContentView());方法與其類似都是設定通知的相應屬性,直接給出結果,不再累述

setBuilderHeadsUpContentView  -->  n.headsUpContentView
setBuilderBigContentView  --> n.bigContentView = bigContentView;

這樣,通知的顯示內容就已經構造好了。

1 .可以看出,在構造階段,並沒有對通知欄的自定義view高度做出限制,但最後顯示的時候卻是一個固定高度,why?
2.先記住這裡的bigContentView屬性,後面會在提到。

buildStyled

4.0後如果設定了BigText,BigPic等樣式,則會呼叫buildStyled方法。buildStyled是Notification.Style中的一個方法

public Notification buildStyled(Notification wip) {
    ...
    populateBigContentView(wip);
    ...
    return wip;
}複製程式碼

populateBigContentView是一個protected所修飾的方法,具體的實現是在所設定的Style中,這裡裡BigPic Style為例,在BigPictureStyle類中,重寫了該方法

@Override
public void populateBigContentView(Notification wip) {
    mBuilder.setBuilderBigContentView(wip, makeBigContentView());
}複製程式碼
private RemoteViews makeBigContentView() {
    RemoteViews contentView = getStandardView(mBuilder.getBigPictureLayoutResource());
    contentView.setImageViewBitmap(R.id.big_picture, mPicture);
    ...
    return contentView;
}複製程式碼
private int getBigPictureLayoutResource() {
    return R.layout.notification_template_material_big_picture;
}複製程式碼

如果是BigPic Style樣式的通知,其實也是呼叫了系統中設定的一個模板佈局notification_template_material_big_picture.
這裡又呼叫了一次mBuilder.setBuilderBigContentView,前面提到了該方法是給notification的bigContentView的屬性賦值。所以如果設定了樣式,則會覆蓋預設的bigContentView值

總結

在通知構造環節,我們需要記住做了最重要的2件事

  1. 給Notification的contentView屬性賦值,該值可以是自定義的view也可以是系統預設樣式的view
  2. 給Notification的bigContentView屬性賦值,該值可以使自定義的view也可以是系統預設樣式的view

相關閱讀

Notification之—-Android5.0實現原理(二)
Notification之—-自定義樣式
Notification之—-預設樣式
Notification之—-任務棧

相關文章