当前位置: 移动技术网 > 移动技术>移动开发>Android > Android8.1 SystemUI源码分析之 Notification流程

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

2019年06月01日  | 移动技术网移动技术  | 我要评论

代码流程

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网