1. SystemUI 系统框架

1.1 启动流程

在 SystemServer 启动时:startBootstrapServices();startCoreServices();startOtherServices();
SystemUI 在 startOtherServices 中启动:
先启动与 SystemUI 的服务statusBar = new StatusBarManagerService(context, wm);ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
再启动 SystemUI                    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();}

在 SystemUI 模块中
启动组件如下:

    <string-array name="config_systemUIServiceComponents" translatable="false"><item>com.android.systemui.Dependency</item><item>com.android.systemui.util.NotificationChannels</item><item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item><item>com.android.systemui.keyguard.KeyguardViewMediator</item><item>com.android.systemui.recents.Recents</item><item>com.android.systemui.volume.VolumeUI</item><item>com.android.systemui.stackdivider.Divider</item><item>com.android.systemui.SystemBars</item><item>com.android.systemui.usb.StorageNotification</item><item>com.android.systemui.power.PowerUI</item><item>com.android.systemui.media.RingtonePlayer</item><item>com.android.systemui.keyboard.KeyboardUI</item><item>com.android.systemui.pip.PipUI</item><item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item><item>@string/config_systemUIVendorServiceComponent</item><item>com.android.systemui.util.leak.GarbageMonitor$Service</item><item>com.android.systemui.LatencyTester</item><item>com.android.systemui.globalactions.GlobalActionsComponent</item><item>com.android.systemui.ScreenDecorations</item><item>com.android.systemui.fingerprint.FingerprintDialogImpl</item><item>com.android.systemui.SliceBroadcastRelayHandler</item></string-array>为单个用户启动的组件 (是全部组件的子类)<string-array name="config_systemUIServiceComponentsPerUser" translatable="false"><item>com.android.systemui.Dependency</item><item>com.android.systemui.util.NotificationChannels</item><item>com.android.systemui.recents.Recents</item></string-array>

所有组件都是 SystemUI 的子类, 通过列表来初始化启动组件

         String clsName = services[i];Class cls;try {cls = Class.forName(clsName);mServices[i] = (SystemUI) cls.newInstance();} catch(ClassNotFoundException ex){throw new RuntimeException(ex);} 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();

自此, SystemUI 完在启动和各个模块的初始化动作.

2.系统模块分析

2.1 通知

通知系统涉及内容较多, framework 部分单独成文介绍, 这里仅介绍 SystemUI部分.

通知注册监听:

创建一个 NotificationListenerService 子类, 主要用于 NotificationListenerService 的具体实现
public class NotificationListenerWithPlugins extends NotificationListenerService继承自 NotificationListenerWithPlugins, 主要用于通知的分发
public class NotificationListener extends NotificationListenerWithPlugins将通和相关的组件注册到 Dependency 组件中.
providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context));
providers.put(NotificationBlockingHelperManager.class,() -> new NotificationBlockingHelperManager(context));
providers.put(NotificationRemoteInputManager.class,() -> new NotificationRemoteInputManager(context));
providers.put(SmartReplyConstants.class,() -> new SmartReplyConstants(Dependency.get(Dependency.MAIN_HANDLER), context));
providers.put(NotificationListener.class, () -> new NotificationListener(context));

将通知监听注册为系统服务

 mNotificationListener =  Dependency.get(NotificationListener.class);mNotificationListener.setUpWithPresenter(this, mEntryManager);

到这里, 通知的注册监听就完在了.

那么当一条通知来的时候, SystemUI 是如何将它显示在界面上呢?
从前面的监听服务可以知道, 当一个通知将要显示出来, 源头便是监听服务

通过 onNotificationPosted 接口监听来到的通知

if (isUpdate) {mEntryManager.updateNotification(sbn, rankingMap);// 已经在显地, 只需要更新一下
} else {mEntryManager.addNotification(sbn, rankingMap); // 新增通知
}

以新增通知为例来看通知流程

1. 排序mNotificationData.updateRanking(ranking);在通知排序前需要对通知进行过滤 shouldFilterOut(Entry entry), 过滤规则来自于 StatusBar 实现的 Environment 接口排序规则:悬浮通知 > 媒体通知 > Rank排序 > 发送设置时间.private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {private final Ranking mRankingA = new Ranking();private final Ranking mRankingB = new Ranking();@Overridepublic int compare(Entry a, Entry b) {final StatusBarNotification na = a.notification;final StatusBarNotification nb = b.notification;int aImportance = NotificationManager.IMPORTANCE_DEFAULT;int bImportance = NotificationManager.IMPORTANCE_DEFAULT;int aRank = 0;int bRank = 0;if (mRankingMap != null) {// RankingMap as received from NoMangetRanking(a.key, mRankingA);getRanking(b.key, mRankingB);aImportance = mRankingA.getImportance();bImportance = mRankingB.getImportance();aRank = mRankingA.getRank();bRank = mRankingB.getRank();}String mediaNotification = mEnvironment.getCurrentMediaNotificationKey();// IMPORTANCE_MIN media streams are allowed to drift to the bottomfinal boolean aMedia = a.key.equals(mediaNotification)&& aImportance > NotificationManager.IMPORTANCE_MIN;final boolean bMedia = b.key.equals(mediaNotification)&& bImportance > NotificationManager.IMPORTANCE_MIN;boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH &&isSystemNotification(na);boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH &&isSystemNotification(nb);boolean isHeadsUp = a.row.isHeadsUp();if (isHeadsUp != b.row.isHeadsUp()) {return isHeadsUp ? -1 : 1;} else if (isHeadsUp) {// Provide consistent ranking with headsUpManagerreturn mHeadsUpManager.compare(a, b);} else if (aMedia != bMedia) {// Upsort current media notification.return aMedia ? -1 : 1;} else if (aSystemMax != bSystemMax) {// Upsort PRIORITY_MAX system notificationsreturn aSystemMax ? -1 : 1;} else if (aRank != bRank) {return aRank - bRank;} else {return Long.compare(nb.getNotification().when, na.getNotification().when);}}};
2. 创建通知视图createNotificationViews(StatusBarNotification sbn)初始化加载视图:new RowInflaterTask().inflate(mContext, parent, entry,row -> {bindRow(entry, pmUser, sbn, row);updateNotification(entry, pmUser, sbn, row);});  ExpandableNotificationRow 负责视图显示逻辑

2.2 近期任务

入口:

    private void onRecentsClick(View v) {if (LatencyTracker.isEnabled(getContext())) {LatencyTracker.getInstance(getContext()).onActionStart(LatencyTracker.ACTION_TOGGLE_RECENTS);}mStatusBar.awakenDreams();mCommandQueue.toggleRecentApps();}

获取系统有没有其它 Recents 支持

外部 RecentsIOverviewProxy overviewProxy = mOverviewProxyService.getProxy();mOverviewProxyService.getProxy().onOverviewToggle();
内部RecentsmImpl.toggleRecents(growTarget);

Recents 本身是一个独立的 Activity, 与 SystemUI 本身关联性并不是很大, 这里以 SystemUI 内部的 recents 为例进行分析.
从SystemUI中启动 Recents

//toggleRecentsprotected void startRecentsActivityAndDismissKeyguardIfNeeded(final ActivityManager.RunningTaskInfo runningTask, final boolean isHomeStackVisible,final boolean animate, final int growTarget) {// Preload only if device for current user is unlockedfinal StatusBar statusBar = getStatusBar();if (statusBar != null && statusBar.isKeyguardShowing()) {statusBar.executeRunnableDismissingKeyguard(() -> {// Flush trustmanager before checking device locked per user when preloadingmTrustManager.reportKeyguardShowingChanged();mHandler.post(() -> startRecentsActivity(runningTask, isHomeStackVisible,animate, growTarget));}, null,  true /* dismissShade */, false /* afterKeyguardGone */,true /* deferred */);} else {startRecentsActivity(runningTask, isHomeStackVisible, animate, growTarget);}}

加载近期作务

sInstanceLoadPlan = new RecentsTaskLoadPlan(mContext);mRawTasks = ActivityManagerWrapper.getInstance().getRecentTasks(ActivityManager.getMaxRecentTasksStatic(), currentUserId);
获取近期任务图标
图片有缓存, 直接加载图片,图片没有缓存, 重新获取
Recents图片缓存路径:
/data/system_ce/0/snapshots
没有图片缓存ThumbnailData thumbnailData = ActivityManagerWrapper.getInstance().getTaskThumbnail(taskKey.id, true /* reducedResolution */);

getTaskThumbnail 流程

WindowManagerService.java
public TaskSnapshot getTaskSnapshot(int taskId, int userId, boolean reducedResolution) {return mTaskSnapshotController.getSnapshot(taskId, userId, true /* restoreFromDisk */,reducedResolution);}TaskSnapShotCache.java
@Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,boolean reducedResolution) {return mCache.getSnapshot(taskId, userId, restoreFromDisk, reducedResolution|| DISABLE_FULL_SIZED_BITMAPS);}获取并缓存图片private TaskSnapshot tryRestoreFromDisk(int taskId, int userId, boolean reducedResolution) {final TaskSnapshot snapshot = mLoader.loadTask(taskId, userId, reducedResolution);if (snapshot == null) {return null;}return snapshot;}

2.3 状态栏

状态栏显示, 在 SystemUI中占比了很大一部分, 其结构如下:
super_status_bar.xml:
其它 status_bar 通过 CollapsedStatusBarFragment 完成对 status_bar_container 的替换.

StatusBarWindowView 的加载:

StatusBar 组件启动时  createAndAddWindows();加载布局
protected void inflateStatusBarWindow(Context context) {mStatusBarWindow = (StatusBarWindowView) View.inflate(context,R.layout.super_status_bar, null);}
将窗口加入到 PhoneWindow 中, 窗口类型为 TYPE_STATUS_BAR
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());mLp = new WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,barHeight,WindowManager.LayoutParams.TYPE_STATUS_BAR,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,PixelFormat.TRANSLUCENT);mLp.token = new Binder();mLp.gravity = Gravity.TOP;mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;mLp.setTitle("StatusBar");mLp.packageName = mContext.getPackageName();mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;mStatusBarView = statusBarView;mBarHeight = barHeight;mWindowManager.addView(mStatusBarView, mLp);mLpChanged = new WindowManager.LayoutParams();mLpChanged.copyFrom(mLp);mStatusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);

2.4 锁屏

先来看一下,手机是如何锁屏的
手机的锁屏事件来源于 Power 的Notifier 线程的通知, 当按下电源键或自动灭屏会触发这个事件.

从上述源程中, 可以清楚的看到整个锁屏的结构图, 在 framework 层, 有一个 KeyguardService 的代理, 通过这个代理接口, 实现和 SystemUI 的交互

解锁流程:
android 提供的安全验证集在中 LockPatternUtils 中, 通过其中的接口来获取加密类型和解锁正确与否.

解锁成功public void reportSuccessfulPasswordAttempt(int userId) {if (userId == USER_FRP && frpCredentialEnabled(mContext)) {return;}getDevicePolicyManager().reportSuccessfulPasswordAttempt(userId);getTrustManager().reportUnlockAttempt(true /* authenticated */, userId);}

2.5 QS 面板

QS Panel 是 StatusBar 中的一个模块, 将它单独列出来, 主要是它有一个快捷入口的功能, 提供一些快捷事件.
一个自定义 TileService 例子

@Implements(TileService.class)
public class ShadowTileService extends ShadowService {@RealObject TileService realService;private Tile mTile;public void __constructor__() { }@Implementationpublic final Tile getQsTile() {return mTile;}@Implementationpublic final void startActivityAndCollapse(Intent intent) {realService.startActivity(intent);}// Non-Android setter.public void setTile(Tile tile) {mTile = tile;}
}

QS Panel的视图结构:

SystemUI 的下拉面板由两个部分构成, 一个是 QsPanel, 另一个是通知面板
QSTileHost 管理当前的Tile
加载默认Tiles: tileList = res.getString(R.string.quick_settings_tiles);

2.6 分屏

进入分屏模式

    protected boolean toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) {if (mRecents == null) {return false;}int dockSide = WindowManagerProxy.getInstance().getDockSide();if (dockSide == WindowManager.DOCKED_INVALID) {final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition();if (navbarPos == NAV_BAR_POS_INVALID) {return false;}int createMode = navbarPos == NAV_BAR_POS_LEFT? ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT: ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;return mRecents.splitPrimaryTask(NavigationBarGestureHelper.DRAG_MODE_NONE, createMode,null, metricsDockAction);} else {Divider divider = getComponent(Divider.class);if (divider != null && divider.isMinimized() && !divider.isHomeStackResizable()) {// Undocking from the minimized state is not supportedreturn false;} else {EventBus.getDefault().send(new UndockingTaskEvent());if (metricsUndockAction != -1) {mMetricsLogger.action(metricsUndockAction);}}}return true;}

启动分屏

    public void splitPrimaryTask(int taskId, int dragMode, int stackCreateMode,Rect initialBounds) {SystemServicesProxy ssp = Recents.getSystemServices();// Make sure we inform DividerView before we actually start the activity so we can change// the resize mode already.if (ssp.setTaskWindowingModeSplitScreenPrimary(taskId, stackCreateMode, initialBounds)) {EventBus.getDefault().send(new DockedTopTaskEvent(dragMode, initialBounds));}}

在窗口控制中, 将窗口属性改为 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY

功能实现在 ActivityManagerService 中.

3.杂项记录

//TODO
// 未完, 仅记录了一个大体框架, 后续再充实.

Android SystemUI系统介绍相关推荐

  1. Android SystemUI之下拉菜单,通知栏,快捷面板(三)

    Android  SystemUI系列: 1.Android  SystemUI之启动流程(一) 2.Android SystemUI之StatusBar,状态栏(二) 3.Android Syste ...

  2. 电视android已停止运行是什么意思,com.android.systemui已停止是什么意思 怎么解决...

    com.android.systemui已停止是什么意思 怎么解决 来源:www.18183.com作者:皮卡时间:2016-01-20 我们如果需要解决手机使用中出现com.android.syst ...

  3. Android SystemUI 架构详解

    Android SystemUI 架构详解 本文描述Android系统中一个核心应用SystemUI,详细赘述SystemUI中几大模块功能的实现过程.由于作者水平有限,如发现本文中错误的地方,欢迎指 ...

  4. Android SystemUI 信号栏后添加信号图标

    SystemUI自定义的状态栏信号view frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\SignalClu ...

  5. Android SystemUI之NavigationBar,导航栏(四)

    Android  SystemUI系列: 1.Android  SystemUI之启动流程(一) 2.Android SystemUI之StatusBar,状态栏(二) 3.Android Syste ...

  6. android SystemUI 流程分析

    android4 SystemUI 流程分析 什么是SystemUI? 对于Phone来说SystemUI指的是:StatusBar(状态栏).NavigationBar(导航栏).而对于Tablet ...

  7. Android SystemUI相关定制(一)

    一.顶部状态栏 1.隐藏右上角状态栏导航电量图标 SystemUI/src/com/android/systemui/BatteryMeterView.java 重写setVisibility()方法 ...

  8. 第1章Android的系统介绍

    第1章Android的系统介绍 1.1 系统介绍    Android 是Google开发的基于Linux平台的.开源的.智能手机操作系统.Android包括操作系统.中间件和应用程序,由于源代码开放 ...

  9. Android SystemUI的客制化 (2)

    本章主要探讨SYstemUI如何将从源码中拷贝出来,放在Android Studio中编译 上前我们了解了Android 源码如何下载,以及对SystemUI有个大概的了解,这期我们主要讲解上期抛出来 ...

最新文章

  1. Spring全局异常处理的三种方式
  2. 史上最严重的忘拿钥匙事件 | 今日最佳
  3. 比较zImage和uImage的区别
  4. [转]CMake 生成makefile 步骤
  5. 新书推荐:可爱的Python
  6. rust 使用fltk 的小问题
  7. 配电室智能辅助控制系统
  8. SAS中library是 excel引擎,LIBNAME STATEMENT
  9. 边缘计算与深度学习——初读研究生的迷茫
  10. 【C++学习汇总】【黑马程序员】
  11. Python中的模块2
  12. wcdma系统随机接入过程的流程图_一种随机接入方法与流程
  13. linux Netfilter在网络层的实现详细分析(iptables)
  14. App跨平台开发方案与抉择
  15. 巴比特 | 元宇宙每日必读:“国产VR之光”PICO正式出海,首发避战Meta,业内人士认为,此为缓兵之计,两者终有一战...
  16. 安搭Share:中国最神奇的城市:500年出一个天子
  17. Javaweb企业员工信息管理系统
  18. JAVA中J2SE和J2EE和 J2me关系
  19. java gc 时间_Java GC日志查看,GC日志时间分析
  20. 基于snmp协议监控华为s5720交换机

热门文章

  1. qq手机邮箱服务器设置方法,手机qq邮箱imap设置图文教程
  2. Android字符设备驱动开发基于高通msm8916【原创 】
  3. 外贸7日通-外贸流程详解 第六天
  4. cosine_annealing
  5. 微信公众号修改业务域名、JS接口安全域名、网页授权域名
  6. html制作晚安,晚安计划.html
  7. 历时三年“鸽王”Filecoin主网上线,分布式存储市场将迎来最强劲敌?
  8. 符合应急监管要求的危化品企业双重预防数字化系统
  9. 网站前端开发必会基础知识有哪些?
  10. Fibonacci数列练习题