博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SystemUI之状态栏status icon加载流程
阅读量:2071 次
发布时间:2019-04-29

本文共 11880 字,大约阅读时间需要 39 分钟。

引言

今天我们主要讲的是SystemUI状态栏里面常见的一排icons——status icons,该icons主要用于显示Bluetooth、Location、Headset等等系统状态的icon,表示当前手机蓝牙、定位、耳机等的功能开启了,以达到提示用户的目的。

正文

本文主要从两个方面讲述下status icon功能,主要分为初始化流程和状态显示流程

话不多说,我们开始吧。

初始化流程

首先我们看下状态栏的布局文件 status_bar.xml

其中我们今天讲的status_icons就藏身于 system_icons.xml 之中

android:id="@+id/statusIcons"出现了,人如其名,它其实是一个自定义的LinearLayout——AlphaOptimizedLinearLayout,这个Viewt也是非常简单的,只是实现了如下方法

@Override    public boolean hasOverlappingRendering() {        return false;    }

该方法用来标记当前view是否存在过度绘制,存在返回ture,不存在返回false,而api里面默认返回为true,诚然我们status icon是个好同学,不存在过度绘制。

接下来我们看下SystemUI是怎么加载这个AlphaOptimizedLinearLayout

@Override    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {        super.onViewCreated(view, savedInstanceState);        mStatusBar = (PhoneStatusBarView) view;        if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {            mStatusBar.go(savedInstanceState.getInt(EXTRA_PANEL_STATE));        }        mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons));        Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);        mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);        mSignalClusterView = mStatusBar.findViewById(R.id.signal_cluster);        Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mSignalClusterView);        // Default to showing until we know otherwise.        showSystemIconArea(false);        initEmergencyCryptkeeperText();    }

在 CollapsedStatusBarFragment.java 中我们见到了初始化,这个类相信有状态栏开发经验的都很熟悉了,与本文相关的代码如下

mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons));
我们接着看这个DarkIconManager初始化里做了什么

public DarkIconManager(LinearLayout linearLayout) {            super(linearLayout);            mIconHPadding = mContext.getResources().getDimensionPixelSize(                    R.dimen.status_bar_icon_padding);            mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);        }public static class IconManager {        protected final ViewGroup mGroup;        protected final Context mContext;        protected final int mIconSize;        public IconManager(ViewGroup group) {            mGroup = group;            mContext = group.getContext();            mIconSize = mContext.getResources().getDimensionPixelSize(                    com.android.internal.R.dimen.status_bar_icon_size);        }        protected void onIconAdded(int index, String slot, boolean blocked,                StatusBarIcon icon) {            addIcon(index, slot, blocked, icon);        }        protected StatusBarIconView addIcon(int index, String slot, boolean blocked,                StatusBarIcon icon) {            StatusBarIconView view = onCreateStatusBarIconView(slot, blocked);            view.set(icon);            mGroup.addView(view, index, onCreateLayoutParams());            return view;        }         public void onSetIcon(int viewIndex, StatusBarIcon icon) {            StatusBarIconView view = (StatusBarIconView) mGroup.getChildAt(viewIndex);            view.set(icon);        }    }

在StatusBarIconController.java中可以看到,在onIconAdded--->addIcon两个函数里面实现了AlphaOptimizedLinearLayout子view即icon的增加,onSetIcon可能就是刷新icon状态的,请记住这两张面孔

onIconAdded(int index, String slot, boolean blocked,StatusBarIcon icon)

onSetIcon(int viewIndex, StatusBarIcon icon)

到这里我们就知道了icon添加的流程。

onIconAdded(int index, String slot, boolean blocked,StatusBarIcon icon)
而且在这个函数里面有参数index和slot,我们应该可以猜到,每个icon应该就是对应着代表顺序的index和数据类型为String的slot,那么到底是不是这样呢?index和slot又是从哪里来的呢?我们接着往下看。

在StatusBarIconControllerImpl.java构造函数里我们找到了实现

public StatusBarIconControllerImpl(Context context) {        super(context.getResources().getStringArray(                com.android.internal.R.array.config_statusBarIcons));

StatusBarIconControllerImpl的父类是StatusBarIconList

protected ArrayList
mSlots = new ArrayList<>(); protected ArrayList
mIcons = new ArrayList<>(); public StatusBarIconList(String[] slots) { final int N = slots.length; for (int i=0; i < N; i++) { mSlots.add(slots[i]); mIcons.add(null); } }

在这里我们就找到了index和slot的出处,原来在初始化的时候就已经定义好了所有的slots,然后从framework中加载出来,index就是string-array中的顺序。

@string/status_bar_rotate
@string/status_bar_headset
@string/status_bar_data_saver
@string/status_bar_managed_profile
@string/status_bar_ime
@string/status_bar_sync_failing
@string/status_bar_sync_active
@string/status_bar_cast
@string/status_bar_hotspot
@string/status_bar_location
@string/status_bar_bluetooth
@string/status_bar_nfc
@string/status_bar_tty
@string/status_bar_speakerphone
@string/status_bar_zen
@string/status_bar_mute
@string/status_bar_volume
@string/status_bar_vpn
@string/status_bar_ethernet
@string/status_bar_wifi
@string/status_bar_mobile
@string/status_bar_airplane
@string/status_bar_cdma_eri
@string/status_bar_data_connection
@string/status_bar_phone_evdo_signal
@string/status_bar_phone_signal
@string/status_bar_battery
@string/status_bar_alarm_clock
@string/status_bar_secure
@string/status_bar_clock
rotate
headset
data_saver
managed_profile
ime
sync_failing
sync_active
cast
hotspot
location
bluetooth
nfc
tty
speakerphone
zen
mute
volume
wifi
cdma_eri
data_connection
phone_evdo_signal
phone_signal
battery
alarm_clock
secure
clock
mobile
vpn
ethernet
airplane

好了,到这里我们的第一部分初始化流程就讲完了

状态显示流程

由上面的初始化流程我们可以知道,每个icon都对应了slot,slot数量比较多,我们就挑一个常见的Headset讲下,其他的流程都是大致一样的。

PhoneStatusBarPolicy.java这个类在初始化的时候注册了大量的监听

public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController) {        mContext = context;        //  初始化headset的slot        mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);        // listen for broadcasts        IntentFilter filter = new IntentFilter();        filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);        filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);        //  注册headset状态变化的action        filter.addAction(AudioManager.ACTION_HEADSET_PLUG);        filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);        filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);        filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);        filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);        filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);        mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);

这个类就是完成icon添加和状态监听的地方,然后当收到对应的action变化的时候,更新headset icon

private void updateHeadsetPlug(Intent intent) {        boolean connected = intent.getIntExtra("state", 0) != 0;        boolean hasMic = intent.getIntExtra("microphone", 0) != 0;        if (connected) {            String contentDescription = mContext.getString(hasMic                    ? R.string.accessibility_status_bar_headset                    : R.string.accessibility_status_bar_headphones);            mIconController.setIcon(mSlotHeadset, hasMic ? R.drawable.ic_headset_mic                    : R.drawable.ic_headset, contentDescription);            mIconController.setIconVisibility(mSlotHeadset, true);        } else {            mIconController.setIconVisibility(mSlotHeadset, false);        }    }

其中主要的方法如下

mIconController.setIcon(mSlotHeadset, hasMic ? R.drawable.ic_headset_mic
: R.drawable.ic_headset, contentDescription);
mIconController.setIconVisibility(mSlotHeadset, true);
这两个函数里面setIcon负责设置icon,而setIconVisibility则根据connected状态设置icon的可见性,下面我们就看下这里面的流程是怎么和初始化流程连接在一起的。

来到StatusBarIconControllerImpl.java这个类中的实现

@Override    public void setIcon(String slot, int resourceId, CharSequence contentDescription) {        int index = getSlotIndex(slot);        StatusBarIcon icon = getIcon(index);        if (icon == null) {            icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),                    Icon.createWithResource(mContext, resourceId), 0, 0, contentDescription);            setIcon(slot, icon);        } else {            icon.icon = Icon.createWithResource(mContext, resourceId);            icon.contentDescription = contentDescription;            handleSet(index, icon);        }    }

逻辑很清晰,首先根据slot找到对应的index,然后用index取得对应的icon,诚然第一次的时候,icon == null,所以通过new StatusBarIcon创建一个,然后调用 setIcon(slot, icon),我们接着往下看

@Override    public void setIcon(String slot, StatusBarIcon icon) {        setIcon(getSlotIndex(slot), icon);    }    @Override    public void setIcon(int index, StatusBarIcon icon) {        if (icon == null) {            removeIcon(index);            return;        }        boolean isNew = getIcon(index) == null;        super.setIcon(index, icon);        if (isNew) {            addSystemIcon(index, icon);        } else {            handleSet(index, icon);        }    }

因为我们之前已经添加了icon,此处icon非空,所以不会走return,但是我们刚才getIcon(index) == null的,所以主要看的是addSystemIcon(index, icon)方法

private void addSystemIcon(int index, StatusBarIcon icon) {        String slot = getSlot(index);        int viewIndex = getViewIndex(index);        boolean blocked = mIconBlacklist.contains(slot);        mIconLogger.onIconVisibility(getSlot(index), icon.visible);        mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, icon));    }

有没有看到一个熟悉的面孔——onIconAdded,对了这个就是我们初始化流程里面StatusBarIconController中添加icon的入口,好了到这里setIcon就添加完毕了。

接着就是setIconVisibility函数了,我们再回到StatusBarIconControllerImpl.java中

public void setIconVisibility(String slot, boolean visibility) {        int index = getSlotIndex(slot);        StatusBarIcon icon = getIcon(index);        if (icon == null || icon.visible == visibility) {            return;        }        icon.visible = visibility;        handleSet(index, icon);    }

icon已经创建成功了,icon非空,并且第一次是icon.visible != visibility的,自然就会顺利的走到handleSet(index, icon)

private void handleSet(int index, StatusBarIcon icon) {        int viewIndex = getViewIndex(index);        mIconLogger.onIconVisibility(getSlot(index), icon.visible);        mIconGroups.forEach(l -> l.onSetIcon(viewIndex, icon));    }

好了又是一个熟悉的面孔——onSetIcon,这个也是我们初始化流程里面StatusBarIconController中setIcon的地方,setIconVisibility也设置完毕了。

到这里,status icon加载流程已经讲完,后面有时间还会讲下notification icon和signal icon的加载流程,敬请关注。

如有什么问题欢迎指正。

本文章已经独家授权ApeClub公众号使用。

 

转载地址:http://rpvmf.baihongyu.com/

你可能感兴趣的文章
【English】【托业】【四六级】写译高频词汇
查看>>
【托业】【新东方全真模拟】01~02-----P5~6
查看>>
【托业】【新东方全真模拟】03~04-----P5~6
查看>>
【托业】【新东方托业全真模拟】TEST05~06-----P5~6
查看>>
【托业】【新东方托业全真模拟】TEST09~10-----P5~6
查看>>
【托业】【新东方托业全真模拟】TEST07~08-----P5~6
查看>>
solver及其配置
查看>>
JAVA多线程之volatile 与 synchronized 的比较
查看>>
Java集合框架知识梳理
查看>>
笔试题(一)—— java基础
查看>>
Redis学习笔记(二)— 在linux下搭建redis服务器
查看>>
Redis学习笔记(三)—— 使用redis客户端连接windows和linux下的redis并解决无法连接redis的问题
查看>>
Intellij IDEA使用(一)—— 安装Intellij IDEA(ideaIU-2017.2.3)并完成Intellij IDEA的简单配置
查看>>
Intellij IDEA使用(二)—— 在Intellij IDEA中配置JDK(SDK)
查看>>
Intellij IDEA使用(三)——在Intellij IDEA中配置Tomcat服务器
查看>>
Intellij IDEA使用(四)—— 使用Intellij IDEA创建静态的web(HTML)项目
查看>>
Intellij IDEA使用(五)—— Intellij IDEA在使用中的一些其他常用功能或常用配置收集
查看>>
Intellij IDEA使用(六)—— 使用Intellij IDEA创建Java项目并配置jar包
查看>>
Eclipse使用(十)—— 使用Eclipse创建简单的Maven Java项目
查看>>
Eclipse使用(十一)—— 使用Eclipse创建简单的Maven JavaWeb项目
查看>>