Android10 notification创建发送流程-framework篇分析了通知是如何从framework发送到NotificationListenerService的onNotificationPosted方法的,这个方法其实并没有具体实现,需要子类实现具体逻辑,这篇文章接着上一篇分析SystemUI部分通知的处理流程。
NotificationListenerWithPlugins继承了NotificationListenerService类
onNotificationPosted的具体实现在NotificationListenerWithPlugins子类NotificationListener
NotificationListener
NotificationListener这个类对NotificationManagerService的onNotificationPosted方法做了具体实现
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
public class NotificationListener extends NotificationListenerWithPlugins {
......
@Override
public void onNotificationPosted(final StatusBarNotification sbn,
final RankingMap rankingMap) {
if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
//相当于handler.post,"main_handler",post到主线程执行
Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
processForRemoteInput(sbn.getNotification(), mContext);
String key = sbn.getKey();
boolean isUpdate =
mEntryManager.getNotificationData().get(key) != null;
......
//判断到来的通知是需要更新还是添加
if (isUpdate) {
mEntryManager.updateNotification(sbn, rankingMap);
} else {
mEntryManager.addNotification(sbn, rankingMap);
}
});
}
}
......
}
主要来看下isUpdate
这里初始化NotificationEntryManager对象时用到了Dagger框架的懒加载Lazy和依赖注入@Inject,具体的没去研究过不清楚,我们只需要知道就是创建了NotificationEntryManager对象就行了,这里以key为键获取NotificationData的值,key就是之前分析的StatusBarNotification的key方法,对一条通知唯一标识
private final NotificationEntryManager mEntryManager = Dependency.get(NotificationEntryManager.class);
boolean isUpdate = mEntryManager.getNotificationData().get(key) != null;
我们第一次创建通知的时候get到的getNotificationData一定是null,因为还没有放进去,所以isUpdate = false,走添加通知的流程
addNotification
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@Override
public void addNotification(StatusBarNotification notification,
NotificationListenerService.RankingMap ranking) {
try {
addNotificationInternal(notification, ranking);
} catch (InflationException e) {
handleInflationException(notification, e);
}
}
private void addNotificationInternal(StatusBarNotification notification,
NotificationListenerService.RankingMap rankingMap) throws InflationException {
//获取唯一标识key,key打印出来是这样的:0|com.example.app3|110|null|10161
String key = notification.getKey();
mNotificationData.updateRanking(rankingMap);
//创建通知的相关排名类
NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
rankingMap.getRanking(key, ranking);
//创建NotificationEntry
NotificationEntry entry = new NotificationEntry(notification, ranking);
Dependency.get(LeakDetector.class).trackInstance(entry);
// 构造通知View
requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
REASON_CANCEL));
abortExistingInflation(key);
mPendingNotifications.put(key, entry);
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onPendingEntryAdded(entry);
}
}
requireBinder其实是返回NotificationRowBinder实例,上面requireBinder().inflateViews调到了NotificationRowBinder的inflateViews方法,NotificationRowBinder是一个接口,找它的实现类NotificationRowBinderImpl
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
/**
* Inflates the views for the given entry (possibly asynchronously).
*/
@Override
public void inflateViews(
NotificationEntry entry,
Runnable onDismissRunnable)
throws InflationException {
ViewGroup parent = mListContainer.getViewParentForNotification(entry);
PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
entry.notification.getUser().getIdentifier());
final StatusBarNotification sbn = entry.notification;
if (entry.rowExists()) {
entry.updateIcons(mContext, sbn);
entry.reset();
updateNotification(entry, pmUser, sbn, entry.getRow());
} else {
entry.createIcons(mContext, sbn);
new RowInflaterTask().inflate(mContext, parent, entry,
row -> {
bindRow(entry, pmUser, sbn, row, onDismissRunnable);
updateNotification(entry, pmUser, sbn, row);
});
}
}
onDismissRunnable是NotificationEntryManager传过来的一个匿名函数,看名字是移除通知的操作
requireBinder().inflateViews(entry,
() -> performRemoveNotification(notification,
REASON_CANCEL))
public void performRemoveNotification(StatusBarNotification n, int reason) {
final NotificationVisibility nv = obtainVisibility(n.getKey());
removeNotificationInternal(
n.getKey(), null, nv, false /* forceRemove */, true /* removedByUser */,
reason);
}
接着回到NotificationRowBinderImpl的inflateViews方法,
mListContainer是通过NotificationRowBinderImpl的setUpWithPresenter传递过来的,类型是NotificationListContainer,它的实现类NotificationStackScrollLayout
/**
* Inflates the views for the given entry (possibly asynchronously).
*/
@Override
public void inflateViews(
NotificationEntry entry,
Runnable onDismissRunnable)
throws InflationException {
//getViewParentForNotification返回this即NotificationStackScrollLayout
ViewGroup parent = mListContainer.getViewParentForNotification(entry);
PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
entry.notification.getUser().getIdentifier());
final StatusBarNotification sbn = entry.notification;
if (entry.rowExists()) {
entry.updateIcons(mContext, sbn);
entry.reset();
updateNotification(entry, pmUser, sbn, entry.getRow());
} else {
//看方法名,创建通知图标
entry.createIcons(mContext, sbn);
new RowInflaterTask().inflate(mContext, parent, entry,
row -> {
bindRow(entry, pmUser, sbn, row, onDismissRunnable);
updateNotification(entry, pmUser, sbn, row);
});
}
}
重点:通知View的构造
new RowInflaterTask().inflate(mContext, parent, entry,
row -> {
bindRow(entry, pmUser, sbn, row, onDismissRunnable);
updateNotification(entry, pmUser, sbn, row);
});
inflate
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
public void inflate(Context context, ViewGroup parent, NotificationEntry entry,
RowInflationFinishedListener listener) {
if (TRACE_ORIGIN) {
mInflateOrigin = new Throwable("inflate requested here");
}
mListener = listener;
//异步Inflater
AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
mEntry = entry;
entry.setInflationTask(this);
inflater.inflate(R.layout.status_bar_notification_row, parent, this);
}
加载了一个R.layout.status_bar_notification_row的布局文件,父容器是传过来的parent,即NotificationStackScrollLayout
看一下status_bar_notification_row
/frameworks/base/packages/SystemUI/res/layout/status_bar_notification_row.xml
<com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:clickable="true"
>
<!-- Menu displayed behind notification added here programmatically -->
<com.android.systemui.statusbar.notification.row.NotificationBackgroundView
android:id="@+id/backgroundNormal"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.android.systemui.statusbar.notification.row.NotificationBackgroundView
android:id="@+id/backgroundDimmed"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.android.systemui.statusbar.notification.row.NotificationContentView
android:id="@+id/expanded"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.android.systemui.statusbar.notification.row.NotificationContentView
android:id="@+id/expandedPublic"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/veto"
android:layout_width="48dp"
android:layout_height="0dp"
android:gravity="end"
android:layout_marginEnd="-80dp"
android:background="@null"
android:paddingEnd="8dp"
android:paddingStart="8dp"
/>
<ViewStub
android:layout="@layout/notification_children_container"
android:id="@+id/child_container_stub"
android:inflatedId="@+id/notification_children_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<ViewStub
android:layout="@layout/notification_guts"
android:id="@+id/notification_guts_stub"
android:inflatedId="@+id/notification_guts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<com.android.systemui.statusbar.notification.FakeShadowView
android:id="@+id/fake_shadow"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.android.systemui.statusbar.notification.row.ExpandableNotificationRow>
采用异步加载的方式AsyncLayoutInflater,布局文件status_bar_notification_row加载完之后回调onInflateFinished,从视图树看这整个布局文件就是一条通知ExpandableNotificationRow
onInflateFinished
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
@Override
public void onInflateFinished(View view, int resid, ViewGroup parent) {
//加载任务没被取消
if (!mCancelled) {
try {
mEntry.onInflationTaskFinished();
mListener.onInflationFinished((ExpandableNotificationRow) view);
} catch (Throwable t) {
if (mInflateOrigin != null) {
Log.e(TAG, "Error in inflation finished listener: " + t, mInflateOrigin);
t.addSuppressed(mInflateOrigin);
}
throw t;
}
}
}
mEntry.onInflationTaskFinished
public void onInflationTaskFinished() {
mRunningTask = null;
}
mRunningTask是之前inflate方法传过去的entry.setInflationTask(this);
mListener.onInflationFinished
NotificationRowBinderImpl传递过来的mListener是一个lambda表达式,调用mListener.onInflationFinished其实就是执行了此lambda表达式,row就是onInflationFinished传入的参数ExpandableNotificationRow
new RowInflaterTask().inflate(mContext, parent, entry,
row -> {
bindRow(entry, pmUser, sbn, row, onDismissRunnable);
updateNotification(entry, pmUser, sbn, row);
});
bindRow
此方法会填充ExpandableNotificationRow的数据,并且再将ExpandableNotificationRow数据绑定到StatusBarNotificationPresenter
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
private void bindRow(NotificationEntry entry, PackageManager pmUser,
StatusBarNotification sbn, ExpandableNotificationRow row,
Runnable onDismissRunnable) {
row.setExpansionLogger(mExpansionLogger, entry.notification.getKey());
row.setGroupManager(mGroupManager);
row.setHeadsUpManager(mHeadsUpManager);
row.setOnExpandClickListener(mPresenter);
row.setInflationCallback(mInflationCallback);
if (mAllowLongPress) {
row.setLongPressListener(mGutsManager::openGuts);
}
//将ExpandableNotificationRow绑定到NotificationStackScrollLayout
mListContainer.bindRow(row);
getRemoteInputManager().bindRow(row);
final String pkg = sbn.getPackageName();
String appname = pkg;
......
row.setAppName(appname);
row.setOnDismissRunnable(onDismissRunnable);
row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
if (ENABLE_REMOTE_INPUT) {
row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
}
row.setAppOpsOnClickListener(mOnAppOpsClickListener);
//StatusBarNotificationPresenter实现了mBindRowCallback
mBindRowCallback.onBindRow(entry, pmUser, sbn, row);
}
继续回到之前lambda表达式里面
updateNotification
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
private void updateNotification(
NotificationEntry entry,
PackageManager pmUser,
StatusBarNotification sbn,
ExpandableNotificationRow row) {
row.setIsLowPriority(entry.ambient);
row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
&& entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
.....
entry.setRow(row);
row.setOnActivatedListener(mPresenter);
row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
row.setEntry(entry);
......
row.setNeedsRedaction(
Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry));
//重点
row.inflateViews();
// bind the click event to the content area
checkNotNull(mNotificationClicker).register(row, sbn);
}
row.inflateViews
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
public void inflateViews() {
mNotificationInflater.inflateNotificationViews();
}
inflateNotificationViews
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
public void inflateNotificationViews() {
inflateNotificationViews(mInflationFlags);
}
private void inflateNotificationViews(@InflationFlag int reInflateFlags) {
//如果通知已被删除
if (mRow.isRemoved()) {
return;
}
// Only inflate the ones that are set.
reInflateFlags &= mInflationFlags;
StatusBarNotification sbn = mRow.getEntry().notification;
// To check if the notification has inline image and preload inline image if necessary.
mRow.getImageResolver().preloadImages(sbn.getNotification());
AsyncInflationTask task = new AsyncInflationTask(
sbn,
mInflateSynchronously,
reInflateFlags,
mCachedContentViews,
mRow,
mIsLowPriority,
mIsChildInGroup,
mUsesIncreasedHeight,
mUsesIncreasedHeadsUpHeight,
mRedactAmbient,
mCallback,
mRemoteViewClickHandler);
if (mInflateSynchronously) {
task.onPostExecute(task.doInBackground());
} else {
task.execute();
}
}
这里通知视图的加载都是通过异步的
来看这个AsyncInflationTask,首先看doInBackground
public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>
implements InflationCallback, InflationTask {
......
@Override
protected InflationProgress doInBackground(Void... params) {
try {
final Notification.Builder recoveredBuilder
= Notification.Builder.recoverBuilder(mContext,
mSbn.getNotification());
Context packageContext = mSbn.getPackageContext(mContext);
Notification notification = mSbn.getNotification();
//如果是多媒体类型的通知
if (notification.isMediaNotification()) {
MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
packageContext);
processor.processNotification(notification, recoveredBuilder);
}
//构造通知View
InflationProgress inflationProgress = createRemoteViews(mReInflateFlags,
recoveredBuilder, mIsLowPriority,
mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight,
mRedactAmbient, packageContext);
return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mRow.getEntry(),
mRow.getContext(), mRow.getHeadsUpManager(),
mRow.getExistingSmartRepliesAndActions());
} catch (Exception e) {
mError = e;
return null;
}
}
......
}
先看下InflationProgress,NotificationContentInflater的静态内部类,用于构造通知View,RemoteViews用于跨进程显示视图
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
static class InflationProgress {
private RemoteViews newContentView;
private RemoteViews newHeadsUpView;
private RemoteViews newExpandedView;
private RemoteViews newAmbientView;
private RemoteViews newPublicView;
@VisibleForTesting
Context packageContext;
private View inflatedContentView;
private View inflatedHeadsUpView;
private View inflatedExpandedView;
private View inflatedAmbientView;
private View inflatedPublicView;
private CharSequence headsUpStatusBarText;
private CharSequence headsUpStatusBarTextPublic;
private InflatedSmartReplies expandedInflatedSmartReplies;
private InflatedSmartReplies headsUpInflatedSmartReplies;
}
createRemoteViews
此方法是构造显示出来的通知View的核心方法,会返回通知布局文件,如果没有自定义通知布局将返回系统默认的布局
private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags,
Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup,
boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
Context packageContext) {
InflationProgress result = new InflationProgress();
isLowPriority = isLowPriority && !isChildInGroup;
if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
result.newExpandedView = createExpandedView(builder, isLowPriority);
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight);
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
result.newPublicView = builder.makePublicContentView();
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
result.newAmbientView = redactAmbient ? builder.makePublicAmbientNotification()
: builder.makeAmbientNotification();
}
result.packageContext = packageContext;
result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */);
result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText(
true /* showingPublic */);
return result;
}
上面这些if判断就是根据不同flag即不同通知类型加载不同通知布局,我们这里以最简单的默认布局(createContentView)来看,可以看到直接通过Notification.Builder来创建通知View
private static RemoteViews createContentView(Notification.Builder builder,
boolean isLowPriority, boolean useLarge) {
if (isLowPriority) {
return builder.makeLowPriorityContentView(false /* useRegularSubtext */);
}
return builder.createContentView(useLarge);
}
builder.createContentView
/frameworks/base/core/java/android/app/Notification.java
//构造最终的通知UI布局
public RemoteViews createContentView() {
return createContentView(false /* increasedheight */ );
}
public RemoteViews createContentView(boolean increasedHeight) {
/*mN.contentView其实就是我们需要自定义通知View时调用
setContent方法设置RemoteView,如果没有设置就加载系统默认布局*/
if (mN.contentView != null && useExistingRemoteView()) {
return mN.contentView;
} else if (mStyle != null) {
final RemoteViews styleView = mStyle.makeContentView(increasedHeight);
if (styleView != null) {
return styleView;
}
}
return applyStandardTemplate(getBaseLayoutResource(), null /* result */);
}
private int getBaseLayoutResource() {
return R.layout.notification_template_material_base;
}
默认加载布局就是R.layout.notification_template_material_base,applyStandardTemplate这个方法就是给此布局设置属性,寬高颜色字体等
private RemoteViews applyStandardTemplate(int resId, StandardTemplateParams p,
TemplateBindResult result) {
RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
resetStandardTemplate(contentView);
final Bundle ex = mN.extras;
updateBackgroundColor(contentView, p);
bindNotificationHeader(contentView, p);
bindLargeIconAndReply(contentView, p, result);
boolean showProgress = handleProgressBar(contentView, ex, p);
if (p.title != null) {
contentView.setViewVisibility(R.id.title, View.VISIBLE);
contentView.setTextViewText(R.id.title, processTextSpans(p.title));
setTextViewColorPrimary(contentView, R.id.title, p);
contentView.setViewLayoutWidth(R.id.title, showProgress
? ViewGroup.LayoutParams.WRAP_CONTENT
: ViewGroup.LayoutParams.MATCH_PARENT);
}
if (p.text != null) {
int textId = showProgress ? com.android.internal.R.id.text_line_1
: com.android.internal.R.id.text;
contentView.setTextViewText(textId, processTextSpans(p.text));
setTextViewColorSecondary(contentView, textId, p);
contentView.setViewVisibility(textId, View.VISIBLE);
}
setContentMinHeight(contentView, showProgress || mN.hasLargeIcon());
return contentView;
}
NotificationContentInflater的createContentView就已经将通知View创建了出来,继续回到doInBackground
inflateSmartReplyViews
这个方法是我们创建通知时如果调用了makeHeaderExpanded或者createHeadsUpContentView设置通知头或者弹出式通知就会去具体构造这两种通知,具体不进一步去看了
private static InflationProgress inflateSmartReplyViews(InflationProgress result,
@InflationFlag int reInflateFlags, NotificationEntry entry, Context context,
HeadsUpManager headsUpManager, SmartRepliesAndActions previousSmartRepliesAndActions) {
SmartReplyConstants smartReplyConstants = Dependency.get(SmartReplyConstants.class);
SmartReplyController smartReplyController = Dependency.get(SmartReplyController.class);
if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0 && result.newExpandedView != null) {
result.expandedInflatedSmartReplies =
InflatedSmartReplies.inflate(
context, entry, smartReplyConstants, smartReplyController,
headsUpManager, previousSmartRepliesAndActions);
}
if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0 && result.newHeadsUpView != null) {
result.headsUpInflatedSmartReplies =
InflatedSmartReplies.inflate(
context, entry, smartReplyConstants, smartReplyController,
headsUpManager, previousSmartRepliesAndActions);
}
return result;
}
onPostExecute
doInBackground执行完之后接着到onPostExecute
protected void onPostExecute(InflationProgress result) {
if (mError == null) {
mCancellationSignal = apply(mInflateSynchronously, result, mReInflateFlags,
mCachedContentViews, mRow, mRedactAmbient, mRemoteViewClickHandler, this);
} else {
handleError(mError);
}
}
apply
根据不同的通知类型做不同的事,只选一个最普通的类型FLAG_CONTENT_VIEW_CONTRACTED
public static CancellationSignal apply(
boolean inflateSynchronously,
InflationProgress result,
@InflationFlag int reInflateFlags,
ArrayMap<Integer, RemoteViews> cachedContentViews,
ExpandableNotificationRow row,
boolean redactAmbient,
RemoteViews.OnClickHandler remoteViewClickHandler,
@Nullable InflationCallback callback) {
//加载这个R.id.expanded
NotificationContentView privateLayout = row.getPrivateLayout();
//加载这个R.id.expandedPublic
NotificationContentView publicLayout = row.getPublicLayout();
final HashMap<Integer, CancellationSignal> runningInflations = new HashMap<>();
int flag = FLAG_CONTENT_VIEW_CONTRACTED;
if ((reInflateFlags & flag) != 0) {
//是否是新增加的View
boolean isNewView =
!canReapplyRemoteView(result.newContentView,
cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED));
//回调
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
result.inflatedContentView = v;
}
@Override
public RemoteViews getRemoteView() {
return result.newContentView;
}
};
applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, cachedContentViews,
row, redactAmbient, isNewView, remoteViewClickHandler, callback, privateLayout,
privateLayout.getContractedChild(), privateLayout.getVisibleWrapper(
NotificationContentView.VISIBLE_TYPE_CONTRACTED),
runningInflations, applyCallback);
}
......
......
//通知View加载完成之后
finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations, callback, row,
redactAmbient);
CancellationSignal cancellationSignal = new CancellationSignal();
cancellationSignal.setOnCancelListener(
() -> runningInflations.values().forEach(CancellationSignal::cancel));
return cancellationSignal;
}
applyRemoteView
static void applyRemoteView(
boolean inflateSynchronously,
final InflationProgress result,
final @InflationFlag int reInflateFlags,
@InflationFlag int inflationId,
final ArrayMap<Integer, RemoteViews> cachedContentViews,
final ExpandableNotificationRow row,
final boolean redactAmbient,
boolean isNewView,
RemoteViews.OnClickHandler remoteViewClickHandler,
@Nullable final InflationCallback callback,
NotificationContentView parentLayout,
View existingView,
NotificationViewWrapper existingWrapper,
final HashMap<Integer, CancellationSignal> runningInflations,
ApplyCallback applyCallback) {
......
//新增加通知的情况
if (isNewView) {
View v = newContentView.apply(
result.packageContext,
parentLayout,
remoteViewClickHandler);
v.setIsRootNamespace(true);
applyCallback.setResultView(v);
} else {
newContentView.reapply(
result.packageContext,
existingView,
remoteViewClickHandler);
existingWrapper.onReinflated();
}
......
}
finishIfDone
private static boolean finishIfDone(InflationProgress result,
@InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews,
HashMap<Integer, CancellationSignal> runningInflations,
@Nullable InflationCallback endListener, ExpandableNotificationRow row,
boolean redactAmbient) {
......
NotificationEntry entry = row.getEntry();
NotificationContentView privateLayout = row.getPrivateLayout();
NotificationContentView publicLayout = row.getPublicLayout();
if (runningInflations.isEmpty()) {
if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
if (result.inflatedContentView != null) {
/*result.inflatedContentView就是RemoteViews
将result.inflatedContentView添加到
privateLayout(R.id.expanded)*/
privateLayout.setContractedChild(result.inflatedContentView);
cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
} else if (cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED) != null) {
cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
}
}
......
if (endListener != null) {
endListener.onAsyncInflationFinished(row.getEntry(), reInflateFlags);
}
}
setContractedChild
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
public void setContractedChild(@Nullable View child) {
if (mContractedChild != null) {
mContractedChild.animate().cancel();
removeView(mContractedChild);
}
if (child == null) {
mContractedChild = null;
mContractedWrapper = null;
if (mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED) {
mTransformationStartVisibleType = UNDEFINED;
}
return;
}
//添加RemoteViews
addView(child);
mContractedChild = child;
mContractedWrapper = NotificationViewWrapper.wrap(getContext(), child,
mContainingNotification);
}
RemoteView加载到NotificationContentView之后接着回调onAsyncInflationFinished
onAsyncInflationFinished
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
public void onAsyncInflationFinished(NotificationEntry entry,
@InflationFlag int inflatedFlags) {
mPendingNotifications.remove(entry.key);
if (!entry.isRowRemoved()) {
boolean isNew = mNotificationData.get(entry.key) == null;
//新增通知
if (isNew) {
......
updateNotifications();
.....
} else {
.....
}
}
}
updateNotifications
public void updateNotifications() {
//对通知进行过滤和排序
mNotificationData.filterAndSort();
if (mPresenter != null) {
mPresenter.updateNotificationViews();
}
}
updateNotificationViews
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
public void updateNotificationViews() {
if (mScrimController == null) return;
if (isCollapsing()) { mShadeController.addPostCollapseAction(this::updateNotificationViews);
return;
}
mViewHierarchyManager.updateNotificationViews();
mNotificationPanel.updateNotificationViews();
}
mViewHierarchyManager.updateNotificationViews
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
public void updateNotificationViews() {
//拿到所有的NotificationEntry,封装了整个通知的信息
ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData()
.getActiveNotifications();
//创建ExpandableNotificationRow,用来承载每条通知RemoteView
ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
final int N = activeNotifications.size();
//遍历全部通知
for (int i = 0; i < N; i++) {
......
//经过过滤之后全部添加到toShow
toShow.add(ent.getRow());
......
}
......
//遍历需要显示的通知,添加到NotificationStackScrollLayout
for (int i = 0; i < toShow.size(); i++) {
View v = toShow.get(i);
//如果是新增通知则需要添加,addContainerView
if (v.getParent() == null) {
mVisualStabilityManager.notifyViewAddition(v);
mListContainer.addContainerView(v);
//如果是此通知已经添加过了,移除toShow
} else if (!mListContainer.containsView(v)) {
toShow.remove(v);
i--;
}
}
......
//后面会对通知排序等操作
}
addContainerView
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
public void addContainerView(View v) {
Assert.isMainThread();
addView(v);
}
到此通知已经全部的创建,加载以及添加完成,这篇文章仅仅大概梳理了通知布局的加载,数据填充即添加到显示UI,很多细节代码需要遇到具体问题在具体分析,整个流程大概就是从framework拿到StatusBarNotification,封装成NotificationEntry,之后去根据不同通知类型加载不同布局,如果没有自定义布局则加载默认布局,创建RemoteView,添加到ExpandableNotificationRow的中的id为R.id.expanded的NotificationContentView,之后再将ExpandableNotificationRow添加到NotificationStackScrollLayout,整个通知布局如下图: