欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-05-13 21:10:33
代码流程 1、先看UI显示,StatuBar加载 CollapsedStatusBarFragment 替换 status_bar_container(状态栏通知显示区域) SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.jav ......

代码流程

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))