Android8.1 MTK平台 SystemUI源码分析之 Notification流程

简介: Android8.1 MTK平台 SystemUI源码分析之 Notification流程

流程图


20190507135527399.png20190507135559757.png


20190507135620125.png


代码流程


1、先看UI显示,StatuBar加载 CollapsedStatusBarFragment 替换 status_bar_container(状态栏通知显示区域)


SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java

FragmentHostManager.get(mStatusBarWindow)
            .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
                CollapsedStatusBarFragment statusBarFragment =
                        (CollapsedStatusBarFragment) fragment;
                statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
                mStatusBarView = (PhoneStatusBarView) fragment.getView();
                mStatusBarView.setBar(this);
                mStatusBarView.setPanel(mNotificationPanel);
                mStatusBarView.setScrimController(mScrimController);
                mStatusBarView.setBouncerShowing(mBouncerShowing);
                setAreThereNotifications();
                checkBarModes();
                /// M: add for plmn display feature @{
                attachPlmnPlugin();
                ///@}
            }).getFragmentManager()
            .beginTransaction()
            .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
                    CollapsedStatusBarFragment.TAG)
            .commit();


statusBarFragment.initNotificationIconArea(mNotificationIconAreaController) 初始化通知栏区域,这是我们关心的


mStatusBarView.setBar(this) 传递statusBar处理下拉事件


mStatusBarView.setPanel(mNotificationPanel) 传递 NotificationPanelView 显示下拉UI控制


2、跟进 CollapsedStatusBarFragment 中,先看布局文件 status_bar.xml

1、notification_lights_out---ImageView默认gone
2、status_bar_contents--LinearLayout
  notification_icon_area--FrameLayout
  system_icon_area--LinearLayout
      system_icons.xml(蓝牙、wifi、VPN、网卡、SIM卡信号、飞行模式等) 电池
      clock--Clock.java 
3、emergency_cryptkeeper_text--ViewStub(延迟加载 紧急电话文字)

这就是我们看到的statusBar的布局,本篇只关心 notification_icon_area,其它的以后再进行分析。继续看到之前的 initNotificationIconArea()


SystemUI\src\com\android\systemui\statusbar\phone\CollapsedStatusBarFragment.java


public void initNotificationIconArea(NotificationIconAreaController
        notificationIconAreaController) {
    ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);
    mNotificationIconAreaInner =
            notificationIconAreaController.getNotificationInnerAreaView();
    if (mNotificationIconAreaInner.getParent() != null) {
        ((ViewGroup) mNotificationIconAreaInner.getParent())
                .removeView(mNotificationIconAreaInner);
    }
    notificationIconArea.addView(mNotificationIconAreaInner);
    // Default to showing until we know otherwise.
    showNotificationIconArea(false);
}

获取到 notification_icon_area,FrameLayout转为ViewGroup,调用 notificationIconAreaController 获取通知要显示的view(LinearLayout),


如果已经有显示的view,通过 view 父布局将其自身remove,然后再重新addView。最后将 mNotificationIconAreaInner 显示出来(设置透明度为1,visibility为VISIBLE)


可以看到 CollapsedStatusBarFragment 中定义了几个如下的方法。

 public void hideSystemIconArea(boolean animate) {
    animateHide(mSystemIconArea, animate);
}
public void showSystemIconArea(boolean animate) {
    animateShow(mSystemIconArea, animate);
}
public void hideNotificationIconArea(boolean animate) {
    animateHide(mNotificationIconAreaInner, animate);
}
public void showNotificationIconArea(boolean animate) {
    animateShow(mNotificationIconAreaInner, animate);
}

当状态栏下拉时,状态栏中的图标icon会慢慢的变成透明和不可见,就是通过hideSystemIconArea(true), hideNotificationIconArea(true)


3、接下来,我们需要跟进 getNotificationInnerAreaView()方法中看看通知栏icon对应的容器


SystemUI\src\com\android\systemui\statusbar\phone\NotificationIconAreaController.java


public View getNotificationInnerAreaView() {
    return mNotificationIconArea;
}
protected void initializeNotificationAreaViews(Context context) {
    reloadDimens(context);
    LayoutInflater layoutInflater = LayoutInflater.from(context);
    mNotificationIconArea = inflateIconArea(layoutInflater);
    mNotificationIcons = (NotificationIconContainer) mNotificationIconArea.findViewById(
            R.id.notificationIcons);
    mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout();
}
protected View inflateIconArea(LayoutInflater inflater) {
    return inflater.inflate(R.layout.notification_icon_area, null);
}
//notification_icon_area.xml
<com.android.keyguard.AlphaOptimizedLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/notification_icon_area_inner"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.android.systemui.statusbar.phone.NotificationIconContainer
    android:id="@+id/notificationIcons"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_alignParentStart="true"
    android:gravity="center_vertical"
    android:orientation="horizontal"/>
</com.android.keyguard.AlphaOptimizedLinearLayout>

好了,观察上面的代码,现在基本上已经理清 notification_icon_area 的布局结构了


notification_icon_area(FrameLayout) 中添加 notification_icon_area_inner(LinearLayout),


每一个通知对应的bean为 NotificationData,创建 Notification 添加到 NotificationIconContainer(FrameLayout)中


4、紧接着我们就来看下 Notification 的监听加载流程,回到 statusBar 的start()中注册 NotificationListenerWithPlugins 作为系统service监听通知消息

try {
        mNotificationListener.registerAsSystemService(mContext,
                new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
                UserHandle.USER_ALL);
    } catch (RemoteException e) {
        Log.e(TAG, "Unable to register notification listener", e);
    }
  private final NotificationListenerWithPlugins mNotificationListener =
        new NotificationListenerWithPlugins() {
    @Override
    public void onListenerConnected() {
        ......  services成功启动,获取当前处于活动状态的通知(没被移除的通知),添加到通知栏,此处应该是重启后重新加载
    }
    @Override
    public void onNotificationPosted(final StatusBarNotification sbn,
            final RankingMap rankingMap) {
        ...... 收到通知消息,添加或者修改
    if (isUpdate) {
            updateNotification(sbn, rankingMap);
        } else {
            addNotification(sbn, rankingMap);
        }
    }
    @Override
    public void onNotificationRemoved(StatusBarNotification sbn,
            final RankingMap rankingMap) {
        ...... 移除通知消息
    if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
            final String key = sbn.getKey();
            mHandler.post(() -> removeNotification(key, rankingMap));
        }
    }
    @Override
    public void onNotificationRankingUpdate(final RankingMap rankingMap) {
        ..... 通知的排序优先级改变,修改通知位置
    if (rankingMap != null) {
            RankingMap r = onPluginRankingUpdate(rankingMap);
            mHandler.post(() -> updateNotificationRanking(r));
        }
    }
};

继续来看下 addNotification()方法

public void addNotification(StatusBarNotification notification, RankingMap ranking)
        throws InflationException {
    String key = notification.getKey();
    if (true/**DEBUG*/) Log.d(TAG, "addNotification key=" + key);
    mNotificationData.updateRanking(ranking);
    Entry shadeEntry = createNotificationViews(notification);
    ......
}

可以看到是通过 createNotificationViews()来创建通知 View对象,内部继续调用 inflateViews()

protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
        throws InflationException {
    if (DEBUG) {
        Log.d(TAG, "createNotificationViews(notification=" + sbn);
    }
    NotificationData.Entry entry = new NotificationData.Entry(sbn);
    Dependency.get(LeakDetector.class).trackInstance(entry);
    entry.createIcons(mContext, sbn);
    // Construct the expanded view.
    inflateViews(entry, mStackScroller);
    return entry;
}
protected void inflateViews(Entry entry, ViewGroup parent) {
    PackageManager pmUser = getPackageManagerForUser(mContext,
            entry.notification.getUser().getIdentifier());
    final StatusBarNotification sbn = entry.notification;
    if (entry.row != null) {
        entry.reset();
        updateNotification(entry, pmUser, sbn, entry.row);
    } else {
        new RowInflaterTask().inflate(mContext, parent, entry,
                row -> {
                    bindRow(entry, pmUser, sbn, row);
                    updateNotification(entry, pmUser, sbn, row);
                });
    }
}


看到上面的方法中,entry在 createNotificationViews 中创建,只赋值了icons, entry.row 为null,进入 RowInflaterTask 中


SystemUI\src\com\android\systemui\statusbar\notification\RowInflaterTask.java


public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry,
        RowInflationFinishedListener listener) {
    mListener = listener;
    AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
    mEntry = entry;
    entry.setInflationTask(this);
    inflater.inflate(R.layout.status_bar_notification_row, parent, this);
}

这里我们得到了 Notification 对应的layout为 status_bar_notification_row.xml,


回调方法中将 row 和 entry 绑定,继续再调用 updateNotification(),注意这个方法是四个参数的,该类中还有重载方法是两个参数的。


private void updateNotification(Entry entry, PackageManager pmUser,
        StatusBarNotification sbn, ExpandableNotificationRow row) {
    .....
    entry.row = row;
    entry.row.setOnActivatedListener(this);
    boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
            mNotificationData.getImportance(sbn.getKey()));
    boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight && mPanelExpanded;
    row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
    row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
    row.updateNotification(entry);
}

紧接着调用了 ExpandableNotificationRow的 updateNotification(),内部继续调用 NotificationInflater.inflateNotificationViews()


SystemUI\src\com\android\systemui\statusbar\notification\NotificationInflater.java


@VisibleForTesting
void inflateNotificationViews(int reInflateFlags) {
    if (mRow.isRemoved()) {
        // We don't want to reinflate anything for removed notifications. Otherwise views might
        // be readded to the stack, leading to leaks. This may happen with low-priority groups
        // where the removal of already removed children can lead to a reinflation.
        return;
    }
    StatusBarNotification sbn = mRow.getEntry().notification;
    new AsyncInflationTask(sbn, reInflateFlags, mRow, mIsLowPriority,
            mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
            mCallback, mRemoteViewClickHandler).execute();
}

new AsyncInflationTask().execute();

@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 (mIsLowPriority) {
            int backgroundColor = mContext.getColor(
                    R.color.notification_material_background_low_priority_color);
            recoveredBuilder.setBackgroundColorHint(backgroundColor);
        }
        if (notification.isMediaNotification()) {
            MediaNotificationProcessor processor = new MediaNotificationProcessor(mContext,
                    packageContext);
            processor.setIsLowPriority(mIsLowPriority);
            processor.processNotification(notification, recoveredBuilder);
        }
        return createRemoteViews(mReInflateFlags,
                recoveredBuilder, mIsLowPriority, mIsChildInGroup,
                mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
                packageContext);
    } catch (Exception e) {
        mError = e;
        return null;
    }
}
@Override
protected void onPostExecute(InflationProgress result) {
    if (mError == null) {
        mCancellationSignal = apply(result, mReInflateFlags, mRow, mRedactAmbient,
                mRemoteViewClickHandler, this);
    } else {
        handleError(mError);
    }
}


从msbn中获取 notifaction,判断是否是媒体类型的通知,进行对应的主题背景色修改,通过传递的优先级设置通知背景色,继续看核心方法 createRemoteViews()

private static InflationProgress createRemoteViews(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_REINFLATE_CONTENT_VIEW) != 0) {
        result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);
    }
    if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
        result.newExpandedView = createExpandedView(builder, isLowPriority);
    }
    if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
        result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight);
    }
    if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
        result.newPublicView = builder.makePublicContentView();
    }
    if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
        result.newAmbientView = redactAmbient ? builder.makePublicAmbientNotification()
                : builder.makeAmbientNotification();
    }
    result.packageContext = packageContext;
    return result;
}



这里就是创建各种布局 CONTENT_VIEW、EXPANDED_VIEW、HEADS_UP_VIEW、PUBLIC_VIEW、AMBIENT_VIEW,


然后回到 AsyncInflationTask 的 onPostExecute()中执行 apply(),代码太多就不贴了, SystemUI部分的通知流程分析技术,欢迎留言讨论。


statusBar左边区域(notification_icon_area)看完了,接下来看下右边的系统图标区域(system_icon_area)


Android8.1 SystemUI源码分析之 电池时钟刷新


从根源上屏蔽Notification


frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java


注释如下代码


mHandler.post(new EnqueueNotificationRunnable(userId, r))


目录
相关文章
|
2月前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
85 6
|
2月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
2月前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
Android面试高频知识点(4) 详解Activity的启动流程
28 3
|
2月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
25 2
|
2月前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
18 0
|
9天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
15天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
16天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。
|
19天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
【10月更文挑战第35天】在数字化时代,安卓应用的开发成为了一个热门话题。本文旨在通过浅显易懂的语言,带领初学者了解安卓开发的基础知识,同时为有一定经验的开发者提供进阶技巧。我们将一起探讨如何从零开始构建第一个安卓应用,并逐步深入到性能优化和高级功能的实现。无论你是编程新手还是希望提升技能的开发者,这篇文章都将为你提供有价值的指导和灵感。
|
16天前
|
存储 API 开发工具
探索安卓开发:从基础到进阶
【10月更文挑战第37天】在这篇文章中,我们将一起探索安卓开发的奥秘。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和建议。我们将从安卓开发的基础开始,逐步深入到更复杂的主题,如自定义组件、性能优化等。最后,我们将通过一个代码示例来展示如何实现一个简单的安卓应用。让我们一起开始吧!