100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > Android 8.1 SystemUI之状态栏 下拉菜单通知 导航栏分析(一)

Android 8.1 SystemUI之状态栏 下拉菜单通知 导航栏分析(一)

时间:2018-10-28 07:43:23

相关推荐

Android 8.1 SystemUI之状态栏 下拉菜单通知 导航栏分析(一)

一、SystemUI简要介绍

作为Android系统核心应用,SystemUI负责反馈系统及应用状态并与用户保持大量的交互,功能代码逻辑也非常复杂。功能代码模块如下图:

StatusBar:通知消息提示和状态展现

NavigationBar:返回,HOME,Recent

KeyGuard:锁屏模块可以看做单独的应用,提供基本的手机个人隐私保护

Recents:近期应用管理,以堆叠栈的形式展现。

Notification Panel:展示系统或应用通知内容。提供快速系统设置开关。

VolumeUI:来用展示或控制音量的变化:媒体音量、铃声音量与闹钟音量

截屏界面:长按电源键+音量下键后截屏,用以展示截取的屏幕照片/内容

PowerUI:主要处理和Power相关的事件,比如省电模式切换、电池电量变化和开关屏事件等。

RingtonePlayer:铃声播放

StackDivider:控制管理分屏

PipUI:提供对于画中画模式的管理

接下来我们针对状态栏、导航栏、下拉列表通知栏做简要分析。

二、SystemUI代码功能模块简要说明

代码路径:framework/base/packages/SystemUI。那SystemUI是什么时候启动的呢,由谁来调用,答案是在 SystemServer 进程服务里。我们看一下相关代码:

mActivityManagerService.systemReady(() -> {........................................traceBeginAndSlog("StartSystemUI");try {startSystemUi(context, windowManagerF);} catch (Throwable e) {reportWtf("starting System UI", e);}static final void startSystemUi(Context context, WindowManagerService windowManager) {Intent intent = new Intent();intent.setComponent(new ComponentName("com.android.systemui","com.android.systemui.SystemUIService"));intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);//Slog.d(TAG, "Starting service: " + intent);context.startServiceAsUser(intent, UserHandle.SYSTEM);windowManager.onSystemUiStarted();}

可以看到在系统启动完成后或调用startSystemUi方法。然后启动SystemUI。接下来看开SystemUIService中做了哪些操作。看onCreate方法里有一句 ((SystemUIApplication) getApplication()).startServicesIfNeeded(); 在看SystemUIApplication中的这个方法

/*** The classes of the stuff to start.*/private final Class<?>[] SERVICES = new Class[] {Dependency.class, //包含了很多各种数据操作类,单例获取NotificationChannels.class,mandQueueStart.class,KeyguardViewMediator.class,Recents.class, //近期任务VolumeUI.class,//音量相关Divider.class,//分屏SystemBars.class,//状态栏导航栏,下拉菜单通知面板StorageNotification.class,PowerUI.class, //电源界面RingtonePlayer.class,KeyboardUI.class,PipUI.class, //画中画ShortcutKeyDispatcher.class,VendorServices.class,GarbageMonitor.Service.class,LatencyTester.class,GlobalActionsComponent.class,RoundedCorners.class,};public void startServicesIfNeeded() {startServicesIfNeeded(SERVICES);}private void startServicesIfNeeded(Class<?>[] services) {if (mServicesStarted) {return;}if (!mBootCompleted) {// check to see if maybe it was already completed long before we began// see ActivityManagerService.finishBooting()if ("1".equals(SystemProperties.get("sys.boot_completed"))) {mBootCompleted = true;if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent");}}Log.v(TAG, "Starting SystemUI services for user " +Process.myUserHandle().getIdentifier() + ".");TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",Trace.TRACE_TAG_APP);log.traceBegin("StartServices");final int N = services.length;for (int i = 0; i < N; i++) {Class<?> cl = services[i];if (DEBUG) Log.d(TAG, "loading: " + cl);log.traceBegin("StartServices" + cl.getSimpleName());long ti = System.currentTimeMillis();try {Object newService = SystemUIFactory.getInstance().createInstance(cl);mServices[i] = (SystemUI) ((newService == null) ? cl.newInstance() : newService);} catch (IllegalAccessException ex) {throw new RuntimeException(ex);} catch (InstantiationException ex) {throw new RuntimeException(ex);}mServices[i].mContext = this;mServices[i].mComponents = mComponents;if (DEBUG) Log.d(TAG, "running: " + mServices[i]);mServices[i].start();log.traceEnd();// Warn if initialization of component takes too longti = System.currentTimeMillis() - ti;if (ti > 1000) {Log.w(TAG, "Initialization of " + cl.getName() + " took " + ti + " ms");}if (mBootCompleted) {mServices[i].onBootCompleted();}}log.traceEnd();....}

上面的操作主要对各个模块功能进行创建启动,调用start方法。 这篇文章主要对SystemBars.class做主要分析,这个模块也是 SystemUI中最为复杂的模块,熟悉了找个模块基本了一半的功能。刚提到会调用SystemBar中的onCreate方法。我们看看onCreate方法中做了哪些操作

@Overridepublic void start() {if (DEBUG) Log.d(TAG, "start");createStatusBarFromConfig();}private void createStatusBarFromConfig() {if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");final String clsName = mContext.getString(R.string.config_statusBarComponent);if (clsName == null || clsName.length() == 0) {throw andLog("No status bar component configured", null);}Class<?> cls = null;try {cls = mContext.getClassLoader().loadClass(clsName);} catch (Throwable t) {throw andLog("Error loading status bar component: " + clsName, t);}try {mStatusBar = (SystemUI) cls.newInstance();} catch (Throwable t) {throw andLog("Error creating status bar component: " + clsName, t);}mStatusBar.mContext = mContext;mStatusBar.mComponents = mComponents;mStatusBar.start();if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());}

这个还是很简单的主要创建了StatusBar对象并调用其start方法。所以针对本篇文章主题主要对StartBar这个类扩展然后进行简要分析。

三、状态栏、通知逻辑分析

接着上节的分析,我们看StartBar 的start方法做了哪些操作,这个类非常复杂,有7000多行代码。所以我简要挑重点来讲一下。

@Overridepublic void start() {mNetworkController = Dependency.get(NetworkController.class); //网络控制,实现类NetworkControllerImpl 主要对手机wifi、移动网络、以太网、是否飞行模式状态逻辑处理判断mUserSwitcherController = Dependency.get(UserSwitcherController.class);用户切换功能逻辑控制,安卓是多用户系统,里面有用户添加切换删除等逻辑判断mScreenLifecycle = Dependency.get(ScreenLifecycle.class);//屏幕周期判断,是否屏幕亮屏关屏mScreenLifecycle.addObserver(mScreenObserver);mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);//手机休眠状态回调判断mWakefulnessLifecycle.addObserver(mWakefulnessObserver);mBatteryController = Dependency.get(BatteryController.class);//电池电量,是否省电模式mAssistManager = Dependency.get(AssistManager.class);//SystemUI帮助类,在这主要是一些动画控制mSystemServicesProxy = SystemServicesProxy.getInstance(mContext);在这判断是否在dreaming状态,手机空闲状态mOverlayManager = IOverlayManager.Stub.asInterface(ServiceManager.getService(Context.OVERLAY_SERVICE)); //判断设置安卓是否是暗黑模式mColorExtractor = Dependency.get(SysuiColorExtractor.class);mColorExtractor.addOnColorsChangedListener(this);//壁纸颜色监听mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);mForegroundServiceController = Dependency.get(ForegroundServiceController.class);//前台服务通知管理mDisplay = mWindowManager.getDefaultDisplay();updateDisplaySize();Resources res = mContext.getResources();mScrimSrcModeEnabled = res.getBoolean(R.bool.config_status_bar_scrim_behind_use_src);//这个带有scrim的都是一些阴影效果功能控制mClearAllEnabled = res.getBoolean(R.bool.config_enableNotificationsClearAll);//是否有删除所有通知功能mAlwaysExpandNonGroupedNotification =res.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications);同一个组的通知是否要一直展开DateTimeView.setReceiverHandler(Dependency.get(Dependency.TIME_TICK_HANDLER));//日期putComponent(StatusBar.class, this);// start old BaseStatusBar.start().mWindowManagerService = WindowManagerGlobal.getWindowManagerService();mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);//锁屏中的一些逻辑判断,锁屏是否可以打开摄像头等mNotificationData = new NotificationData(this);//通知的实体类,所有通知的数据存储mMessagingUtil = new NotificationMessagingUtil(mContext);//通知信息帮助工具mAccessibilityManager = (AccessibilityManager)mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); //主要一些辅助性的功能,帮助盲人mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);//观察用户是否完成了新手引导mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,mSettingsObserver);//勿打扰模式观察mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,mLockscreenSettingsObserver,UserHandle.USER_ALL);if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),false,mSettingsObserver,UserHandle.USER_ALL);//锁屏是否显示通知观察}mContext.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),true,mLockscreenSettingsObserver,UserHandle.USER_ALL);//锁屏通知隐藏一些重要数据观察mBarService = IStatusBarService.Stub.asInterface(ServiceManager.getService(Context.STATUS_BAR_SERVICE));//这个重点了,这个是我们监听StausBarService的一些数据回调mRecents = getComponent(Recents.class);//近期任务final Configuration currentConfig = res.getConfiguration();mLocale = currentConfig.locale;mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);//layout方向,从左到右,从右到左mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);//安卓用户管理mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);//锁屏管理mLockPatternUtils = new LockPatternUtils(mContext);// Connect in to the status bar manager servicemCommandQueue = getComponent(CommandQueue.class);//继承 IStatusBar.StubmCommandQueue.addCallbacks(this);//添加数据监听回调int[] switches = new int[9];//一些禁用列表ArrayList<IBinder> binders = new ArrayList<>();ArrayList<String> iconSlots = new ArrayList<>();图标名称ArrayList<StatusBarIcon> icons = new ArrayList<>();图标Rect fullscreenStackBounds = new Rect();Rect dockedStackBounds = new Rect();try {mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,fullscreenStackBounds, dockedStackBounds);//向服务注册} catch (RemoteException ex) {// If the system process isn't there we're doomed anyway.}createAndAddWindows();//这个重点方法,创建相关的视图mSettingsObserver.onChange(false); // set upmCommandQueue.disable(switches[0], switches[6], false /* animate */);setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,fullscreenStackBounds, dockedStackBounds);topAppWindowChanged(switches[2] != 0);// StatusBarManagerService has a back up of IME token and it's restored here.setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);// Set up the initial icon stateint N = iconSlots.size();for (int i=0; i < N; i++) {mCommandQueue.setIcon(iconSlots.get(i), icons.get(i));} // Set up the initial notification state. 重点注册通知,其他程序通过NotificationManager发送通知我这里接受到,注册监听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);}if (DEBUG) {Log.d(TAG, String.format("init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",icons.size(),switches[0],switches[1],switches[2],switches[3]));}mCurrentUserId = ActivityManager.getCurrentUser();setHeadsUpUser(mCurrentUserId);//用户状态广播IntentFilter filter = new IntentFilter();filter.addAction(Intent.ACTION_USER_SWITCHED);filter.addAction(Intent.ACTION_USER_ADDED);filter.addAction(Intent.ACTION_USER_PRESENT);mContext.registerReceiver(mBaseBroadcastReceiver, filter);//通知相关广播IntentFilter internalFilter = new IntentFilter();internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);internalFilter.addAction(BANNER_ACTION_CANCEL);internalFilter.addAction(BANNER_ACTION_SETUP);mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null);//屏幕锁屏状态IntentFilter allUsersFilter = new IntentFilter();allUsersFilter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);allUsersFilter.addAction(Intent.ACTION_DEVICE_LOCKED_CHANGED);mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,null, null);updateCurrentProfilesCache();IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(Context.VR_SERVICE));try {vrManager.registerListener(mVrStateCallbacks);} catch (RemoteException e) {Slog.e(TAG, "Failed to register VR mode state listener: " + e);}mNonBlockablePkgs = new HashSet<>();Collections.addAll(mNonBlockablePkgs, res.getStringArray(com.android.internal.R.array.config_nonBlockableNotificationPackages));// end old BaseStatusBar.start().mMediaSessionManager= (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);// TODO: use MediaSessionManager.SessionListener to hook us up to future updates// in session state//系统图标管理也比较重要,如wifi、蓝牙、闹钟等// Lastly, call to the icon policy to install/update all the icons.mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);mSettingsObserver.onChange(false); // set up//弹出式的通知mHeadsUpObserver.onChange(true); // set upif (ENABLE_HEADS_UP) {mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true,mHeadsUpObserver);mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,mHeadsUpObserver);}mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);mUnlockMethodCache.addListener(this);startKeyguard();KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);putComponent(DozeHost.class, mDozeServiceHost);notifyUserAboutHiddenNotifications();//屏幕pinning请求mScreenPinningRequest = new ScreenPinningRequest(mContext);mFalsingManager = FalsingManager.getInstance(mContext);Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(this);//屏幕配置简体Dependency.get(ConfigurationController.class).addCallback(this);}

在onCreate中做了很多初始化的操作,里面包含了很多种状态的监听控制还是很复杂的,接下来我们分析状态栏是怎么加载处理的,还有通知的一些控制实现。进入到 createAndAddWindows()开始分析吧。

public void createAndAddWindows() {addStatusBarWindow();}private void addStatusBarWindow() {makeStatusBarView();mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);//管理状态栏,导航栏、面板的状态切换mRemoteInputController = new RemoteInputController(mHeadsUpManager);//弹出式通知快捷输入mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());//这里把整个View添加到window中去}

我们重点看看makeStatusBarView() 这个方法

protected void makeStatusBarView() {final Context context = mContext;updateDisplaySize(); // populates mDisplayMetricsupdateResources();updateTheme();inflateStatusBarWindow(context); //mStatusBarWindow = (StatusBarWindowView) View.inflate(context, R.layout.super_status_bar, null); 创建个布局,总的布局,等下我们具体分析下.....mNotificationIconAreaController = SystemUIFactory.getInstance().createNotificationIconAreaController(context, this);//状态栏左半部分的通知栏控制器inflateShelf();//初始化面板通知栏的子布局mNotificationIconAreaController.setupShelf(mNotificationShelf);Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController); //暗黑主题监听控制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();...}

我们先看上面这一小段代码,其他先不分析,上面可以看到把super_status_bar中的status_bar_container替换成了CollapsedStatusBarFragment,这个Fragment其实就是状态栏相关的布局了。状态栏中布局主要包含以下

//根布局 status_bar.xml<com.android.systemui.statusbar.phone.PhoneStatusBarViewxmlns:android="/apk/res/android"xmlns:systemui="/apk/res/com.android.systemui"android:layout_width="match_parent"android:layout_height="@dimen/status_bar_height"android:id="@+id/status_bar"android:background="@drawable/system_bar_background"android:orientation="vertical"android:focusable="false"android:descendantFocusability="afterDescendants"><ImageViewandroid:id="@+id/notification_lights_out"android:layout_width="@dimen/status_bar_icon_size"android:layout_height="match_parent"android:paddingStart="6dip"android:paddingBottom="2dip"android:src="@drawable/ic_sysbar_lights_out_dot_small"android:scaleType="center"android:visibility="gone"/><LinearLayout android:id="@+id/status_bar_contents"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingStart="6dp"android:paddingEnd="8dp"android:orientation="horizontal"><!-- The alpha of this area is controlled from both PhoneStatusBarTransitions andPhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->//状态栏坐车通知栏<com.android.systemui.statusbar.AlphaOptimizedFrameLayoutandroid:id="@+id/notification_icon_area"android:layout_width="0dip"android:layout_height="match_parent"android:layout_weight="1"android:orientation="horizontal" /><com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"android:layout_width="wrap_content"android:layout_height="match_parent"android:orientation="horizontal">//系统图标相关的布局<include layout="@layout/system_icons" />//时间<com.android.systemui.statusbar.policy.Clockandroid:id="@+id/clock"android:textAppearance="@style/TextAppearance.StatusBar.Clock"android:layout_width="wrap_content"android:layout_height="match_parent"android:singleLine="true"android:paddingStart="@dimen/status_bar_clock_starting_padding"android:paddingEnd="@dimen/status_bar_clock_end_padding"android:gravity="center_vertical|start"/></com.android.keyguard.AlphaOptimizedLinearLayout></LinearLayout><ViewStubandroid:id="@+id/emergency_cryptkeeper_text"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout="@layout/emergency_cryptkeeper_text"/></com.android.systemui.statusbar.phone.PhoneStatusBarView>

首先分析通知图标怎么加载出来,看上面代码有一句

statusBarFragment.initNotificationIconArea(mNotificationIconAreaController); 我们跟进去看这里面的操作public void initNotificationIconArea(NotificationIconAreaControllernotificationIconAreaController) {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);}可以看到NotificationIconAreaController 有个getNotificationInnerAreaView()方法把通知的View添加进去了,我简要的分析找个类的一些功能public NotificationIconAreaController(Context context, StatusBar statusBar) {mStatusBar = statusBar;mNotificationColorUtil = NotificationColorUtil.getInstance(context);mContext = context;initializeNotificationAreaViews(context);}protected View inflateIconArea(LayoutInflater inflater) {return inflater.inflate(R.layout.notification_icon_area, null);}/*** Initializes the views that will represent the notification area.*/protected void initializeNotificationAreaViews(Context context) {reloadDimens(context);LayoutInflater layoutInflater = LayoutInflater.from(context);mNotificationIconArea = inflateIconArea(layoutInflater);//这个就是具体的去加载的通知区域的View了.即上面返回的View,这个是个自定义的FrameLayoutmNotificationIcons = (NotificationIconContainer) mNotificationIconArea.findViewById(R.id.notificationIcons);mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout();}protected View inflateIconArea(LayoutInflater inflater) {return inflater.inflate(R.layout.notification_icon_area, null);}

我们看看通知的数据从哪里来的,我们发现了这个类的下面方法

/*** Updates the notifications with the given list of notifications to display. NotificationData前面已经说过了所有通知数据都封装在找个类中*/public void updateNotificationIcons(NotificationData notificationData) {updateIconsForLayout(notificationData, entry -> entry.icon, mNotificationIcons,false /* showAmbient */);updateIconsForLayout(notificationData, entry -> entry.expandedIcon, mShelfIcons,NotificationShelf.SHOW_AMBIENT_ICONS);applyNotificationIconsTint();}//这个就是具体的根据数据来更新通知icon了 private void updateIconsForLayout(NotificationData notificationData,Function<NotificationData.Entry, StatusBarIconView> function,NotificationIconContainer hostLayout, boolean showAmbient) {ArrayList<StatusBarIconView> toShow = new ArrayList<>(mNotificationScrollLayout.getChildCount());// Filter out ambient notifications and notification children.for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) {View view = mNotificationScrollLayout.getChildAt(i);if (view instanceof ExpandableNotificationRow) {NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();if (shouldShowNotificationIcon(ent, notificationData, showAmbient)) {toShow.add(function.apply(ent));}}}// In case we are changing the suppression of a group, the replacement shouldn't flicker// and it should just be replaced instead. We therefore look for notifications that were// just replaced by the child or vice-versa to suppress this.ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons = new ArrayMap<>();ArrayList<View> toRemove = new ArrayList<>();for (int i = 0; i < hostLayout.getChildCount(); i++) {View child = hostLayout.getChildAt(i);if (!(child instanceof StatusBarIconView)) {continue;}if (!toShow.contains(child)) {boolean iconWasReplaced = false;StatusBarIconView removedIcon = (StatusBarIconView) child;String removedGroupKey = removedIcon.getNotification().getGroupKey();for (int j = 0; j < toShow.size(); j++) {StatusBarIconView candidate = toShow.get(j);if (candidate.getSourceIcon().sameAs((removedIcon.getSourceIcon()))&& candidate.getNotification().getGroupKey().equals(removedGroupKey)) {if (!iconWasReplaced) {iconWasReplaced = true;} else {iconWasReplaced = false;break;}}}if (iconWasReplaced) {ArrayList<StatusBarIcon> statusBarIcons = replacingIcons.get(removedGroupKey);if (statusBarIcons == null) {statusBarIcons = new ArrayList<>();replacingIcons.put(removedGroupKey, statusBarIcons);}statusBarIcons.add(removedIcon.getStatusBarIcon());}toRemove.add(removedIcon);}}// removing all duplicatesArrayList<String> duplicates = new ArrayList<>();for (String key : replacingIcons.keySet()) {ArrayList<StatusBarIcon> statusBarIcons = replacingIcons.get(key);if (statusBarIcons.size() != 1) {duplicates.add(key);}}replacingIcons.removeAll(duplicates);hostLayout.setReplacingIcons(replacingIcons);final int toRemoveCount = toRemove.size();for (int i = 0; i < toRemoveCount; i++) {hostLayout.removeView(toRemove.get(i));}final FrameLayout.LayoutParams params = generateIconLayoutParams();for (int i = 0; i < toShow.size(); i++) {View v = toShow.get(i);// The view might still be transiently added if it was just removed and added againhostLayout.removeTransientView(v);if (v.getParent() == null) {hostLayout.addView(v, i, params);}}hostLayout.setChangingViewPositions(true);// Re-sort notification iconsfinal int childCount = hostLayout.getChildCount();for (int i = 0; i < childCount; i++) {View actual = hostLayout.getChildAt(i);StatusBarIconView expected = toShow.get(i);if (actual == expected) {continue;}hostLayout.removeView(expected);hostLayout.addView(expected, i);}hostLayout.setChangingViewPositions(false);hostLayout.setReplacingIcons(null);}谁去调用找个方法呢,答案是StatuBar这个类中,这个类在第一节onCreate中初始化了一堆数据,其中就包含了注册用户发送notification的监听回调private final NotificationListenerWithPlugins mNotificationListener =new NotificationListenerWithPlugins() {//每次有新的通知都会回调找个方法,添加或者更新通知@Overridepublic void onNotificationPosted(final StatusBarNotification sbn,final RankingMap rankingMap) {if (true/**DEBUG*/) Log.d(TAG, "onNotificationPosted: " + sbn);if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {mHandler.post(new Runnable() {@Overridepublic void run() {processForRemoteInput(sbn.getNotification());String key = sbn.getKey();mKeysKeptForRemoteInput.remove(key);boolean isUpdate = mNotificationData.get(key) != null;// In case we don't allow child notifications, we ignore children of// notifications that have a summary, since we're not going to show them// anyway. This is true also when the summary is canceled,// because children are automatically canceled by NoMan in that case.if (!ENABLE_CHILD_NOTIFICATIONS&& mGroupManager.isChildInGroupWithSummary(sbn)) {if (DEBUG) {Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);}// Remove existing notification to avoid stale data.if (isUpdate) {removeNotification(key, rankingMap);} else {mNotificationData.updateRanking(rankingMap);}return;}try {if (isUpdate) {updateNotification(sbn, rankingMap);} else {addNotification(sbn, rankingMap);}} catch (InflationException e) {handleInflationException(sbn, e);}}});}}//通知移除,@Overridepublic void onNotificationRemoved(StatusBarNotification sbn,final RankingMap rankingMap) {if (true/**DEBUG*/) Log.d(TAG, "onNotificationRemoved: " + sbn);if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {final String key = sbn.getKey();mHandler.post(() -> removeNotification(key, rankingMap));}}//通知排序更新@Overridepublic void onNotificationRankingUpdate(final RankingMap rankingMap) {if (DEBUG) Log.d(TAG, "onRankingUpdate");if (rankingMap != null) {RankingMap r = onPluginRankingUpdate(rankingMap);mHandler.post(() -> updateNotificationRanking(r));}}};

大家感兴趣再去研究 removeNotification、addNotification等相关方法,反正最终会调用上面提到的方法去更新状态栏通知,找个逻辑也要讲半天时间紧迫。再接下来看开系统通知怎么加载的吧。

system_icons.xml<LinearLayout xmlns:android="/apk/res/android"android:id="@+id/system_icons"android:layout_width="wrap_content"android:layout_height="match_parent"android:gravity="center_vertical">//系统icon容器<com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/statusIcons"android:layout_width="wrap_content"android:layout_height="match_parent"android:gravity="center_vertical"android:orientation="horizontal"/>//信号相关,手机卡信号,是否飞行模式,wifi<include layout="@layout/signal_cluster_view"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="@dimen/signal_cluster_margin_start"/>//电池<com.android.systemui.BatteryMeterView android:id="@+id/battery"android:layout_height="match_parent"android:layout_width="wrap_content"/></LinearLayout>

我简要分析系统icon数据怎么加载到这个容器中,其他的不做讲解了。看到 CollapsedStatusBarFragment中的onViewCreated方法

@Overridepublic 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));//把系统icon容器传到DarkIConManager中,Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager); 然后StatusBarIconControl添加找个icon管理器,这两句代码很重要,我们从这两句代码去分析整个加载流程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();}首先看开DarkIconManager,这个是StatusBarIconController的内部类,主要管理创建系统icon控件StatusBarIconView,并把它添加到容器中去public static class DarkIconManager extends IconManager {private final DarkIconDispatcher mDarkIconDispatcher;private int mIconHPadding;public DarkIconManager(LinearLayout linearLayout) {super(linearLayout);mIconHPadding = mContext.getResources().getDimensionPixelSize(R.dimen.status_bar_icon_padding);mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);}//根据系统icon名称 icon数据添加创建View@Overrideprotected void onIconAdded(int index, String slot, boolean blocked,StatusBarIcon icon) {StatusBarIconView v = addIcon(index, slot, blocked, icon);mDarkIconDispatcher.addDarkReceiver(v);}@Overrideprotected LayoutParams onCreateLayoutParams() {LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);lp.setMargins(mIconHPadding, 0, mIconHPadding, 0);return lp;}@Overrideprotected void destroy() {for (int i = 0; i < mGroup.getChildCount(); i++) {mDarkIconDispatcher.removeDarkReceiver((ImageView) mGroup.getChildAt(i));}mGroup.removeAllViews();}@Overrideprotected void onRemoveIcon(int viewIndex) {mDarkIconDispatcher.removeDarkReceiver((ImageView) mGroup.getChildAt(viewIndex));super.onRemoveIcon(viewIndex);}@Overridepublic void onSetIcon(int viewIndex, StatusBarIcon icon) {super.onSetIcon(viewIndex, icon);mDarkIconDispatcher.applyDark((ImageView) mGroup.getChildAt(viewIndex));}}/*** Turns info from StatusBarIconController into ImageViews in a ViewGroup.*/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;}@VisibleForTestingprotected StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) {return new StatusBarIconView(mContext, slot, null, blocked);}protected LinearLayout.LayoutParams onCreateLayoutParams() {return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);}protected void destroy() {mGroup.removeAllViews();}protected void onIconExternal(int viewIndex, int height) {ImageView imageView = (ImageView) mGroup.getChildAt(viewIndex);imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);imageView.setAdjustViewBounds(true);setHeightAndCenter(imageView, height);}protected void onDensityOrFontScaleChanged() {for (int i = 0; i < mGroup.getChildCount(); i++) {View child = mGroup.getChildAt(i);LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);child.setLayoutParams(lp);}}private void setHeightAndCenter(ImageView imageView, int height) {ViewGroup.LayoutParams params = imageView.getLayoutParams();params.height = height;if (params instanceof LinearLayout.LayoutParams) {((LinearLayout.LayoutParams) params).gravity = Gravity.CENTER_VERTICAL;}imageView.setLayoutParams(params);}protected void onRemoveIcon(int viewIndex) {mGroup.removeViewAt(viewIndex);}public void onSetIcon(int viewIndex, StatusBarIcon icon) {StatusBarIconView view = (StatusBarIconView) mGroup.getChildAt(viewIndex);view.set(icon);}}那我们哪些系统icon数据怎么产生的呢,在第一节StatusBar中onCreate中有一句mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);PhoneStatusBarPolicy这个类就管理了一系列的系统相关图标的状态加载,我们进去看这个类做了哪些操作在构造方法中初始化了许多操作,并监听系统交互各种广播状态变化,以便对其显示或隐藏iconpublic PhoneStatusBarPolicy(Context context, StatusBarIconController iconController) {mContext = context;mIconController = iconController;mCast = Dependency.get(CastController.class);//是否有截屏录屏、无线投屏这两个功能状态的监听mHotspot = Dependency.get(HotspotController.class);//热点状态控制监听mBluetooth = Dependency.get(BluetoothController.class);//蓝牙功能状态监听mNextAlarm = Dependency.get(NextAlarmController.class);//闹钟功能状态监听mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);mUserInfoController = Dependency.get(UserInfoController.class);//用户信息获取监听mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);mRotationLockController = Dependency.get(RotationLockController.class);//是否是自动旋屏mDataSaver = Dependency.get(DataSaverController.class);//后台限制数据流量mZenController = Dependency.get(ZenModeController.class);//勿打扰模式监听控制mProvisionedController = Dependency.get(DeviceProvisionedController.class);//用户是否完成新手引导监听mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);//锁屏状态监听mLocationController = Dependency.get(LocationController.class);//定位服务状态监听控制//下面这些状态就是系统图标的标识字符串mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);mSlotBluetooth = context.getString(com.android.internal.R.string.status_bar_bluetooth);mSlotTty = context.getString(com.android.internal.R.string.status_bar_tty);mSlotZen = context.getString(com.android.internal.R.string.status_bar_zen);mSlotVolume = context.getString(com.android.internal.R.string.status_bar_volume);mSlotAlarmClock = context.getString(com.android.internal.R.string.status_bar_alarm_clock);mSlotManagedProfile = context.getString(com.android.internal.R.string.status_bar_managed_profile);mSlotRotate = context.getString(com.android.internal.R.string.status_bar_rotate);mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver);mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location);//监听一些广播与上面状态有关// listen for broadcastsIntentFilter filter = new IntentFilter();filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_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);// listen for user / profile change.try {ActivityManager.getService().registerUserSwitchObserver(mUserSwitchListener, TAG);} catch (RemoteException e) {// Ignore}//文字传语音状态// TTY statusupdateTTY();//蓝牙// bluetooth statusupdateBluetooth();//这个mIconController就是StatusBarIconControllerImpl对象。设置icon和控制icon是否可见,等下我们跟进去看看具体的怎么添加// Alarm clockmIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null);mIconController.setIconVisibility(mSlotAlarmClock, false);// zenmIconController.setIcon(mSlotZen, R.drawable.stat_sys_zen_important, null);mIconController.setIconVisibility(mSlotZen, false);// volumemIconController.setIcon(mSlotVolume, R.drawable.stat_sys_ringer_vibrate, null);mIconController.setIconVisibility(mSlotVolume, false);updateVolumeZen();// castmIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast, null);mIconController.setIconVisibility(mSlotCast, false);// hotspotmIconController.setIcon(mSlotHotspot, R.drawable.stat_sys_hotspot,mContext.getString(R.string.accessibility_status_bar_hotspot));mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled());// managed profilemIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status,mContext.getString(R.string.accessibility_managed_profile));mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible);// data savermIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver,context.getString(R.string.accessibility_data_saver_on));mIconController.setIconVisibility(mSlotDataSaver, false);//对上面的添加状态回调,以便设置icon是否可见mRotationLockController.addCallback(this);mBluetooth.addCallback(this);mProvisionedController.addCallback(this);mZenController.addCallback(this);mCast.addCallback(mCastCallback);mHotspot.addCallback(mHotspotCallback);mNextAlarm.addCallback(mNextAlarmCallback);mDataSaver.addCallback(this);mKeyguardMonitor.addCallback(this);mLocationController.addCallback(this);SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskListener);// Clear out all old notifications on startup (only present in the case where sysui dies)NotificationManager noMan = mContext.getSystemService(NotificationManager.class);for (StatusBarNotification notification : noMan.getActiveNotifications()) {if (notification.getId() == SystemMessage.NOTE_INSTANT_APPS) {noMan.cancel(notification.getTag(), notification.getId());}}DockedStackExistsListener.register(exists -> {mDockedStackExists = exists;updateForegroundInstantApps();});}

接下来看看setIcon方法怎么设置的,先看其构造方法,这个类继承自StatusBarIconList,管理系统图标、系统标识的添加移除

public StatusBarIconControllerImpl(Context context) {//设置有哪些系统图标标识,从配置文件中加载出来super(context.getResources().getStringArray(com.android.internal.R.array.config_statusBarIcons));Dependency.get(ConfigurationController.class).addCallback(this);//这个是是否是暗黑主题相关,以便更新系统图标颜色mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);mContext = context;loadDimens();SysUiServiceProvider.getComponent(context, CommandQueue.class).addCallbacks(this);Dependency.get(TunerService.class).addTunable(this, ICON_BLACKLIST);}然后去看setIcon方法@Overridepublic void setIcon(String slot, int resourceId, CharSequence contentDescription) {//根据标识文字获取到标识的下标,根据标识的下标来得到一个图标数据封装类StatusBarIconint 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);}}我们在看看setIcon方法@Overridepublic void setIcon(String slot, StatusBarIcon icon) {setIcon(getSlotIndex(slot), icon);}@Overridepublic void setIcon(int index, StatusBarIcon icon) {if (icon == null) {removeIcon(index);return;}//如果icon没有添加就去添加,否则只是更新这个状态boolean isNew = getIcon(index) == null;super.setIcon(index, icon);//保存在List中以便下次使用if (isNew) {addSystemIcon(index, icon);} else {handleSet(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);//这个就是在Fragment设置进去的DarkIconManager,调用其onIconAdded方法实现添加mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, icon));}我们看看setIconVisibility方法,其实根据标识找到icon然后更新其的可见性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);}//更新iconprivate void handleSet(int index, StatusBarIcon icon) {int viewIndex = getViewIndex(index);mIconLogger.onIconVisibility(getSlot(index), icon.visible);mIconGroups.forEach(l -> l.onSetIcon(viewIndex, icon));}

到目前为止我们就分析完了状态栏的相关功能流程了。还是比较复杂的。由于时间问题,对下拉通知栏和导航栏等下次再讲了

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。