This article is an "in-depth understanding of Android Volume III" in-depth understanding of the status bar reading notes in the chapter.
SystemUI frameworks location in the source code / base / packages / SystemUI in.
First, acquaintance SystemUI
Mainly about the status and navigation bars, all running in the SystemUIService
1, SystemUIService boot process
In ServerThread responsible for starting system services, call
ActivityManagerService.self().systemReady(new Runnable() {
public void run() {
...
if (!headless) startSystemUi(contextF);
Then start SystemUIService
static final void startSystemUi(Context context) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
//Slog.d(TAG, "Starting service: " + intent);
context.startServiceAsUser(intent, UserHandle.OWNER);
}
/**
* The class names of the stuff to start.
*/
final Object[] SERVICES = new Object[] {
0, // system bar or status bar, filled in below.
com.android.systemui.power.PowerUI.class,
com.android.systemui.media.RingtonePlayer.class,
com.android.systemui.settings.SettingsUI.class,
};
In SystemUI, the separation scheme is implemented by PhoneStatusBar, achieved by integration of the layout scheme is TabletStatusBar. Essential function of both is the same, i.e., to provide virtual keys, notification information, and differ only in the different layout, and the this custom behavior derived from it.
Change in the screen is within 720 dp, using separate layout scheme, the judgment condition in PhoneWindowManager.setInitialDisplaySize () in.
Second, create a status bar
1, the creation process
private void addStatusBarWindow() {
// Put up the view
// 状态栏的高度,默认是 25dp, 位置在 frameworks/base/core/res/res/values/dimen.xml 中
// R.dimen.status_bar_height
final int height = getStatusBarHeight();
// Now that the status bar window encompasses the sliding panel and its
// translucent backdrop, the entire thing is made TRANSLUCENT and is
// hardware-accelerated.
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
height,
WindowManager.LayoutParams.TYPE_STATUS_BAR, // 窗口类型
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE // 状态栏不接受按键事件
// 状态栏接受导致设备唤醒的事件
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
// 允许状态栏支持触摸事件序列的拆分
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT); // 状态栏的 Surface 像素格式为支持透明度
// 启动硬件加速
lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
lp.gravity = getStatusBarGravity();
lp.setTitle("StatusBar");
lp.packageName = mContext.getPackageName();
// 创建 StatusBar 和 导航栏
makeStatusBarView();
// 添加到窗口,创建过程完成
mWindowManager.addView(mStatusBarWindow, lp);
}
2. Layout
3. 重要变量
三、状态栏显示的通知
四、SystemUI 的体系结构
1. SystemUIService
一个普通的 Android Service ,它以一个容器的角色运行于 SystemUI 进程中。
2. CommandQueue
运行在 SystemUI 进程中;
继承 IStatusBar.Stub, 是 IStatusBar 的 Bn 段,是 IStatusBarService 与 BaseStatusBar 进行沟通的桥梁;
3. IStatusBarService;
运行在 system_server 进程中;
即系统服务 StatusBarManagerService 是状态栏、导航栏向外界提供服务的前端接口;它接受操作状态栏、导航栏的请求 并转给 BaseStatusBar;
4. StatusBarManagerService
运行在 system_server 进程中;
是 IStatusBarService 的实现者, 在 ServerThread.run() 方法中启动;
它是 SystemUI 中状态栏和导航栏在 system_server 中的代理。所有对状态栏或者导航栏来有需求的对象都可以通过获取 StatusBarManagerService 的实例或 Bp 端达到其目的。只不过使用者必须拥有能够完成操作的相应权限;
它保存了状态栏/导航栏所需的信息副本,用于在 SystemUI 意外退出之后的恢复;
5.IStatusBar
运行在 SystemUI 进程中;
是 SystemUI 中 CommandQueue 联系 StatusBarManagerSerevice 与 BaseStatusBar 的桥梁;
6. SystemUI also includes achieved ImageWallpaper, RecentPanel TakeScreenshotService and other functions. They are Service, Activity and other standard Android application components, independent of each other.
Fifth, the notification display process information
Let's build an example of a display notifications
private void sendNotification(){
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Intent intent = new Intent(this, Main2Activity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
Notification notification = new Notification.Builder(this)
.setAutoCancel(true)
.setTicker("测试新通知")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("新通知")
.setContentText("测试测试测出测试")
.setWhen(System.currentTimeMillis())
.setContentIntent(pendingIntent)
.build();
manager.notify(11232, notification);
}
Show results
Notification display information beginning from NotificationManager.notify (int id, Notification notification)
1, in NotificationManagerService.enqueueNotificationWithTag () in
Call checkCallerIsSystemOrSameApp (...) security checks
checkCallerIsSystemOrSameApp () gets the UID notify the submitter of the information and get in distribution and PackageManager have to specify the package UID name of the application for comparison. If not, then the malicious software attempts to tamper with other applications sent by way of fraudulent use of the name of the package notification information, it will throw an exception, prohibit such behavior.
Restrictions Each application can only be submitted up to 50 notifications, preventing malicious software or notification by a large number of registered lead to system failures
// Limit the number of notifications that any given package except the android
// package can enqueue. Prevents DOS attacks and deals with leaks.
if (!isSystemNotification) {
..
if (count >= MAX_PACKAGE_NOTIFICATIONS) {
..
return;
}
}
}
}
}
. According to rate the importance of information, priority fields, if the score is too low, this notification message will be ignored;
pkg, tag, id in NotificationManagerService in together constitute a unique identification notification.
2. PhoneStatusBar.addNotification () in
<span style="font-size:14px;">public void addNotification(IBinder key, StatusBarNotification notification) {
if (DEBUG) Slog.d(TAG, "addNotification score=" + notification.getScore());
// 1. 将通知内容添加到控件树中, 在 BaseStatusBar 中创建 view
StatusBarIconView iconView = addNotificationViews(key, notification);
if (iconView == null) return;
boolean immersive = false;
try {
immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
if (DEBUG) {
Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
}
} catch (RemoteException ex) {
}
// 2. 为新的通知启动 fullScreenIntent 或进行 ticker
if (notification.getNotification().fullScreenIntent != null) {
// Stop screensaver if the notification has a full-screen intent.
// (like an incoming phone call)
awakenDreams();
// not immersive & a full-screen alert should be shown
if (DEBUG) Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
try {
notification.getNotification().fullScreenIntent.send();
} catch (PendingIntent.CanceledException e) {
}
} else {
// usual case: status bar visible & not immersive
// show the ticker if there isn't an intruder too
if (mCurrentlyIntrudingNotification == null) {
tick(null, notification, true);
}
}
// 3. 更新周边控件
// Recalculate the position of the sliding windows and the titles.
setAreThereNotifications();
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
}</span>
3. BaseStatusBar.addNotificationViews (...) in
protected StatusBarIconView addNotificationViews(IBinder key,
StatusBarNotification notification) {
if (DEBUG) {
Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
}
// Construct the icon.
// 1. 创建一个 StatusBarIconView ,用于显示通知的图标;
final StatusBarIconView iconView = new StatusBarIconView(mContext,
notification.getPackageName() + "/0x" + Integer.toHexString(notification.getId()),
notification.getNotification());
iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
// 2. 创建 StatusBarIcon 实例, 用于保存在 Notification 实例中与通知图标相关的信息;
final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
notification.getUser(),
notification.getNotification().icon,
notification.getNotification().iconLevel,
notification.getNotification().number,
notification.getNotification().tickerText);
if (!iconView.set(ic)) {
handleNotificationError(key, notification, "Couldn't create icon: " + ic);
return null;
}
// Construct the expanded view.
// 3. 创建 NotificaitonData.Entry 实例
NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
// 4. 创建通知在下拉卷帘中的控件树,并将其添加到 mPile 中
if (!inflateViews(entry, mPile)) {
handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
+ notification);
return null;
}
// Add the expanded view and icon.
// 5. 保存 entry 到 mNotificaitonData 中
int pos = mNotificationData.add(entry);
if (DEBUG) {
Slog.d(TAG, "addNotificationViews: added at " + pos);
}
// 6. 更新通知在下拉卷帘中的展开状态;
updateExpansionStates();
// 7. 更新状态栏中所有通知图标的显示状态。
updateNotificationIcons();
return iconView;
}
inflateViews (...) method described in the configuration of the pull-down shutter control tree, reference P445.
通知的详细信息由 R.layout.status_bar_notification_row 显示在 mPile 中。
六、系统状态图标的显示
系统状态图标区的意图由一个字符串描述,意图列表在 frameworks/base/core/res/res/values/config.xml 中 config_statusBarIcons 定义。
显示过程
1. 在 StatusBarManagerService.setIcon() 中
调用 enforceStatusBar() 方法,设置者必须拥有签名级系统权限 android.permission.STATUS_BAR 才能设置系统状 态图标。
2. 大多数系统图标通过 PhoneStatusBarPolicy 类设置
PhoneStatusBarPolicy 在 PhoneStatusBar.start() 方法中创建,通过监听一些广播,随后调用相应的方法,最后调用 StatusBarManager.setIcon 进行修改图标。
监听的广播
// listen for broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_ALARM_CHANGED);
filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
至此,关于导航栏的内容基本结束了。