Android SystemUI系统介绍
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();
@Override
public 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 NoMan
getRanking(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 bottom
final 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 headsUpManager
return mHeadsUpManager.compare(a, b);
} else if (aMedia != bMedia) {
// Upsort current media notification.
return aMedia ? -1 : 1;
} else if (aSystemMax != bSystemMax) {
// Upsort PRIORITY_MAX system notifications
return 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 支持
外部 Recents
IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
mOverviewProxyService.getProxy().onOverviewToggle();
内部Recents
mImpl.toggleRecents(growTarget);
Recents 本身是一个独立的 Activity, 与 SystemUI 本身关联性并不是很大, 这里以 SystemUI 内部的 recents 为例进行分析.
从SystemUI中启动 Recents
//toggleRecents
protected void startRecentsActivityAndDismissKeyguardIfNeeded(
final ActivityManager.RunningTaskInfo runningTask, final boolean isHomeStackVisible,
final boolean animate, final int growTarget) {
// Preload only if device for current user is unlocked
final StatusBar statusBar = getStatusBar();
if (statusBar != null && statusBar.isKeyguardShowing()) {
statusBar.executeRunnableDismissingKeyguard(() -> {
// Flush trustmanager before checking device locked per user when preloading
mTrustManager.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__() { }
@Implementation
public final Tile getQsTile() {
return mTile;
}
@Implementation
public 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 supported
return 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
// 未完, 仅记录了一个大体框架, 后续再充实.
本文地址:https://blog.csdn.net/mzsyxiao/article/details/107033541
上一篇: 基于Ajax的聊天机器人
下一篇: AJAX实现指定部分页面刷新效果
推荐阅读
-
android教程之把自己的应用加入到系统分享中
-
Android创建服务之started service详细介绍
-
logcat命令使用方法和查看android系统日志缓冲区内容的方法
-
Android中的Looper对象详细介绍
-
Android增量升级的方法和原理详细介绍
-
android开发教程之系统资源的使用方法 android资源文件
-
Android 图片存入系统相册更新显示实例详解
-
mac苹果系统怎么安装win10?mac上装win10的两种方法介绍
-
WinXP系统boot.ini怎么修改?WinXP系统下Boot.ini设置方法介绍
-
Win7系统遇到werfault.exe应用程序错误的解决方法介绍