SystemUI深度裁剪
前言
SystemUI AOSP包含了很多组件,
Status bars,Navigation bars,Notification,Lockscreen,Quick settings,Overview(recent task switcher),VolumeUI,PowerUI 等等,但是我们在某些Andriod设备上不会用到手机上面这么多服务,有可能只会用到Systembar(StatusBar,NavigationBar),例如车载,点餐机等等,为了让我们SystemUI更轻量,性能更优,初始化完成时间更优,那么我们最好对它进程深度裁剪.
一、裁剪思路是什么?
拿车机来说吧,一般没有powerUI,分屏,overview,截屏这些功能。很多车载项目只有状态栏,导航栏,wallpaper,通知中心,VolumeUI。那我们只保留项目中需要的服务。
二、行动吧
代码如下(示例):
<string-array name="config_systemUIServiceComponents" translatable="false">
<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.biometrics.BiometricDialogImpl</item>
<item>com.android.systemui.SliceBroadcastRelayHandler</item>
<item>com.android.systemui.SizeCompatModeActivityController</item>
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
<item>com.android.systemui.theme.ThemeOverlayController</item>
</string-array>
SystemUI核心组件都在这里声明的,在SystemUIService启动的,那么是不是在这里把组件注释了就能达到裁剪效果了呢。如果这么简单的话,那我就没必要写这边文章了。因为SystemUI各个组件耦合太多,如果简单的注释掉,运行会报很多错误让你一脸懵逼。这个时候有人就会说了,那我在组件代码里面再去裁剪掉,那么你真的太棒了。SystemUI组件的逻辑很多也比较复杂,这条路就算是大佬来,估计也得做很多逻辑梳理删除的工作,也要不停的调试,显然这是个笨方法。
如果对SystemUI比较了解的,其实知道所有的视图都是Window,所有的功能都是服务。既然这样那么为什么不能全都自己定义呢,一些属性逻辑参考AOSP就可以了,对的就是这个思路。
1:屏蔽掉不用的服务,只保留CommandQueue$CommandQueueStart,SystemBars
<string-array name="config_systemUIServiceComponents" translatable="false">
<!--<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.biometrics.BiometricDialogImpl</item>
<item>com.android.systemui.SliceBroadcastRelayHandler</item>
<item>com.android.systemui.SizeCompatModeActivityController</item>
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
<item>com.android.systemui.theme.ThemeOverlayController</item>-->
</string-array>
2:AndroidManifest.xml 屏蔽掉服务注册,不然要运行时异常.
<!-- Broadcast receiver that gets the broadcast at boot time and starts
up everything else.
TODO: Should have an android:permission attribute
-->
<service
android:name="SystemUIService"
android:exported="true" />
<!--// <!– On user switch, this service is started to ensure that the associated SystemUI
process for the current user is started. See the resource
"config_systemUIServiceComponentsPerUser".
–>
<service android:name="SystemUISecondaryUserService"
android:exported="false"
android:permission="com.android.systemui.permission.SELF" />
<!– started from PhoneWindowManager
TODO: Should have an android:permission attribute –>
<service android:name=".screenshot.TakeScreenshotService"
android:process=":screenshot"
android:exported="false" />
<!– Called from PhoneWindowManager –>
<receiver android:name=".screenshot.ScreenshotServiceErrorReceiver"
android:process=":screenshot"
android:exported="false">
<intent-filter>
<action android:name="com.android.systemui.screenshot.SHOW_ERROR" />
</intent-filter>
</receiver>
<activity android:name=".screenrecord.ScreenRecordDialog"
android:theme="@style/ScreenRecord" />
<service android:name=".screenrecord.RecordingService" />
<receiver android:name=".SysuiRestartReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.android.systemui.action.RESTART" />
<data android:scheme="package" />
</intent-filter>
</receiver>-->
<service
android:name=".ImageWallpaper"
android:exported="true"
android:permission="android.permission.BIND_WALLPAPER" />
<!-- <activity android:name=".tuner.TunerActivity"
android:enabled="false"
android:icon="@drawable/tuner"
android:theme="@style/TunerSettings"
android:label="@string/system_ui_tuner"
android:process=":tuner"
android:exported="true">
<intent-filter>
<action android:name="com.android.settings.action.EXTRA_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.category"
android:value="com.android.settings.category.ia.system" />
<meta-data android:name="com.android.settings.summary"
android:resource="@string/summary_empty"/>
</activity>
<activity-alias android:name=".DemoMode"
android:targetActivity=".tuner.TunerActivity"
android:icon="@drawable/tuner"
android:theme="@style/TunerSettings"
android:label="@string/demo_mode"
android:process=":tuner"
android:exported="true">
<intent-filter>
<action android:name="com.android.settings.action.DEMO_MODE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity-alias>
<activity
android:name=".stackdivider.ForcedResizableInfoActivity"
android:theme="@style/ForcedResizableTheme"
android:excludeFromRecents="true"
android:stateNotNeeded="true"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:exported="false">
</activity>
<!– Springboard for launching the share and edit activity. This needs to be in the main
system ui process since we need to notify the status bar to dismiss the keyguard –>
<receiver android:name=".screenshot.GlobalScreenshot$ActionProxyReceiver"
android:exported="false" />
<!– Callback for dismissing screenshot notification after a share target is picked –>
<receiver android:name=".screenshot.GlobalScreenshot$TargetChosenReceiver"
android:exported="false" />
<!– Callback for deleting screenshot notification –>
<receiver android:name=".screenshot.GlobalScreenshot$DeleteScreenshotReceiver"
android:exported="false" />
<!– Callback for invoking a smart action from the screenshot notification. –>
<receiver android:name=".screenshot.GlobalScreenshot$SmartActionsReceiver"
android:exported="false"/>
<!– started from UsbDeviceSettingsManager –>
<activity android:name=".usb.UsbConfirmActivity"
android:exported="true"
android:permission="android.permission.MANAGE_USB"
android:theme="@style/Theme.SystemUI.Dialog.Alert"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true">
</activity>
<!– started from UsbDeviceSettingsManager –>
<activity android:name=".usb.UsbPermissionActivity"
android:exported="true"
android:permission="android.permission.MANAGE_USB"
android:theme="@style/Theme.SystemUI.Dialog.Alert"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true">
</activity>
<!– started from UsbDeviceSettingsManager –>
<activity android:name=".usb.UsbResolverActivity"
android:exported="true"
android:permission="android.permission.MANAGE_USB"
android:theme="@style/Theme.SystemUI.Dialog.Alert"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true">
</activity>
<!– started from UsbDeviceSettingsManager –>
<activity android:name=".usb.UsbAccessoryUriActivity"
android:exported="true"
android:permission="android.permission.MANAGE_USB"
android:theme="@style/Theme.SystemUI.Dialog.Alert"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true">
</activity>
<!– started from UsbPortManager –>
<activity android:name=".usb.UsbContaminantActivity"
android:exported="true"
android:permission="android.permission.MANAGE_USB"
android:theme="@style/Theme.SystemUI.Dialog.Alert"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true">
</activity>
<!– started from AdbDebuggingManager –>
<activity android:name=".usb.UsbDebuggingActivity"
android:permission="android.permission.MANAGE_DEBUGGING"
android:theme="@style/Theme.SystemUI.Dialog.Alert"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true">
</activity>
<activity-alias
android:name=".UsbDebuggingActivityAlias"
android:permission="android.permission.DUMP"
android:targetActivity=".usb.UsbDebuggingActivity"
android:exported="true">
</activity-alias>
<activity android:name=".usb.UsbDebuggingSecondaryUserActivity"
android:theme="@style/Theme.SystemUI.Dialog.Alert"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true">
</activity>
<!– started from NetworkPolicyManagerService –>
<activity
android:name=".net.NetworkOverLimitActivity"
android:exported="true"
android:permission="android.permission.MANAGE_NETWORK_POLICY"
android:theme="@android:style/Theme.DeviceDefault.Light.Panel"
android:finishOnCloseSystemDialogs="true"
android:launchMode="singleTop"
android:taskAffinity="com.android.systemui.net"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:excludeFromRecents="true" />
<!– started from MediaProjectionManager –>
<activity
android:name=".media.MediaProjectionPermissionActivity"
android:exported="true"
android:theme="@style/Theme.SystemUI.MediaProjectionAlertDialog"
android:finishOnCloseSystemDialogs="true"
android:launchMode="singleTop"
android:excludeFromRecents="true"
android:visibleToInstantApps="true"/>
<!– started from PipUI –>
<activity
android:name=".pip.tv.PipMenuActivity"
android:permission="com.android.systemui.permission.SELF"
android:exported="false"
android:theme="@style/PipTheme"
android:launchMode="singleTop"
android:taskAffinity=""
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|locale|layoutDirection"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
androidprv:alwaysFocusable="true"
android:excludeFromRecents="true" />
<activity
android:name=".pip.phone.PipMenuActivity"
android:permission="com.android.systemui.permission.SELF"
android:theme="@style/PipPhoneOverlayControlTheme"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:excludeFromRecents="true"
android:exported="false"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
android:stateNotNeeded="true"
android:taskAffinity=""
android:launchMode="singleTop"
androidprv:alwaysFocusable="true" />
<!– started from SliceProvider –>
<activity android:name=".SlicePermissionActivity"
android:theme="@style/Theme.SystemUI.Dialog.Alert"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="com.android.intent.action.REQUEST_SLICE_PERMISSION" />
</intent-filter>
</activity>
<!– platform logo easter egg activity –>
<activity
android:name=".DessertCase"
android:exported="true"
android:label="@string/dessert_case"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
android:launchMode="singleInstance"
android:screenOrientation="locked"
android:process=":sweetsweetdesserts"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".egg.MLandActivity"
android:theme="@android:style/Theme.Material.NoActionBar"
android:exported="true"
android:icon="@drawable/icon"
android:label="@string/mland"
android:launchMode="singleInstance"
android:screenOrientation="locked"
android:process=":sweetsweetdesserts"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<!– a gallery of delicious treats –>
<service
android:name=".DessertCaseDream"
android:exported="true"
android:label="@string/dessert_case"
android:permission="android.permission.BIND_DREAM_SERVICE"
android:enabled="false"
android:process=":sweetsweetdesserts"
>
<intent-filter>
<action android:name="android.service.dreams.DreamService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
<service
android:name=".keyguard.KeyguardService"
android:exported="true"
android:enabled="@bool/config_enableKeyguardService" />
<activity android:name=".keyguard.WorkLockActivity"
android:label="@string/accessibility_desc_work_lock"
android:permission="android.permission.MANAGE_USERS"
android:exported="false"
android:excludeFromRecents="true"
android:stateNotNeeded="true"
android:resumeWhilePausing="true"
android:theme="@android:style/Theme.Black.NoTitleBar">
<intent-filter>
<action android:name="android.app.action.CONFIRM_DEVICE_CREDENTIAL_WITH_USER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".Somnambulator"
android:label="@string/start_dreams"
android:icon="@mipmap/ic_launcher_dreams"
android:theme="@android:style/Theme.Wallpaper.NoTitleBar"
android:exported="true"
android:excludeFromRecents="true"
>
<!–
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
–>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.DESK_DOCK" />
</intent-filter>
</activity>
<activity
android:name=".settings.BrightnessDialog"
android:label="@string/quick_settings_brightness_dialog_title"
android:theme="@*android:style/Theme.DeviceDefault.QuickSettings.Dialog"
android:finishOnCloseSystemDialogs="true"
android:launchMode="singleInstance"
android:excludeFromRecents="true"
android:exported="true">
<intent-filter>
<action android:name="com.android.intent.action.SHOW_BRIGHTNESS_DIALOG" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".ForegroundServicesDialog"
android:process=":fgservices"
android:excludeFromRecents="true"
android:launchMode="singleTop"
android:theme="@*android:style/Theme.DeviceDefault.Settings.Dialog">
<intent-filter android:priority="1">
<action android:name="android.settings.FOREGROUND_SERVICES_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".chooser.ChooserActivity"
android:theme="@*android:style/Theme.NoDisplay"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true"
android:documentLaunchMode="never"
android:relinquishTaskIdentity="true"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
android:process=":ui"
android:visibleToInstantApps="true">
<intent-filter>
<action android:name="android.intent.action.CHOOSER" />
<category android:name="android.intent.category.VOICE" />
</intent-filter>
</activity>
<!– Doze with notifications, run in main sysui process for every user –>
<service
android:name=".doze.DozeService"
android:exported="true"
android:singleUser="true"
android:permission="android.permission.BIND_DREAM_SERVICE" />
<receiver
android:name=".tuner.TunerService$ClearReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.android.systemui.action.CLEAR_TUNER" />
</intent-filter>
</receiver>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.android.systemui.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/fileprovider" />
</provider>
<provider android:name=".keyguard.KeyguardSliceProvider"
android:authorities="com.android.systemui.keyguard"
android:grantUriPermissions="true"
android:exported="true">
</provider>
<!– Provides list and realistic previews of clock faces for the picker app. –>
<provider
android:name="com.android.keyguard.clock.ClockOptionsProvider"
android:authorities="com.android.keyguard.clock"
android:enabled="false"
android:exported="false"
android:grantUriPermissions="true">
</provider>
<receiver
android:name=".statusbar.KeyboardShortcutsReceiver">
<intent-filter>
<action android:name="com.android.intent.action.DISMISS_KEYBOARD_SHORTCUTS" />
<action android:name="com.android.intent.action.SHOW_KEYBOARD_SHORTCUTS" />
</intent-filter>
</receiver>-->
<service
android:name="com.android.XX.notification.XXNotificationListener"
android:exported="true"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
tools:ignore="Instantiatable">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService"></action>
</intent-filter>
</service>
</application>
3:然后自定义StatusBar
com.android.XX.CustomStatusBar
public class XXXStatusBar extends SystemUI {
private WindowManager mWinddowManager;
@Inject
InjectionInflationController mInjectionInflater;
private XXXStatusBarWindowView mStatusBarView;
private XXXNotificationPanelView mPanelView;
private WindowManager.LayoutParams mStatusBarParams;
private ViewGroup mNavigationBarView;
private WindowManager.LayoutParams mNavigationBarParams;
private int mStatusBarHeight;
@Override
public void start() {
putComponent(XXXStatusBar.class, this);
mWinddowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
addStatusBarToWindows();
addNavigationBarToWindows();
startNotificationService();
}
}
private void addStatusBarToWindows() {
mStatusBarHeight = getStatusBarHeight();
mStatusBarParams = new WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, mStatusBarHeight,
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);
mStatusBarParams.token = new Binder();
mStatusBarParams.gravity = Gravity.TOP;
mStatusBarParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mStatusBarParams.setTitle("StatusBar");
mStatusBarParams.packageName = mContext.getPackageName();
mStatusBarParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
//mInjectionInflater.injectable(LayoutInflater.from(mContext)).inflate(R.layout.XXX_status_bar, null);
//Log.i("gary","--mStatusBarView--:"+mInjectionInflater.injectable(LayoutInflater.from(mContext)).inflate(R.layout.XXX_status_bar, null));
mStatusBarView = (XXXStatusBarWindowView) View.inflate(mContext, R.layout.XXX_status_bar, null);
mPanelView = mStatusBarView.findViewById(R.id.dropdown_panel);
mStatusBarView.setPanel(mPanelView);
mPanelView.setBar(this);
mStatusBarView.setBar(this);
mWinddowManager.addView(mStatusBarView, mStatusBarParams);
}
private void addNavigationBarToWindows() {
mNavigationBarParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_SLIPPERY,
PixelFormat.TRANSLUCENT);
mNavigationBarParams.token = new Binder();
mNavigationBarParams.setTitle("NavigationBar" + mContext.getDisplayId());
mNavigationBarParams.accessibilityTitle = mContext.getString(R.string.nav_bar);
mNavigationBarParams.windowAnimations = 0;
mNavigationBarParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
mNavigationBarView = (ViewGroup) View.inflate(mContext, R.layout.XXX_navigation_bar, null);
mWinddowManager.addView(mNavigationBarView, mNavigationBarParams);
});
}
这样自定义的状态栏,导航栏就定义出来了,具体的一些自定义功能就可以参考原生的功能实现,其实百度搜系统api支持大都可以是实现了。
2.自定义下拉面板,VolumeBar,通知中心.
1:自定义下拉面板:可以添加一个window,然后开始默认layout到负一屏的位置,用户滑动状态栏时,根据手势移动这个widow就可以了。
2:自定义VolumeBar:可以参考原生的实现方式监听volumechanged然后弹Window。
3:通知中心:自定义一个Service集成系统的NotificationListenerService 在onNotificationPosted和onNotificationRemoved处理通知。
总结
屏蔽掉原生Service,然后参考原生Service实现逻辑,实现自定义,这样自己更好控制,也能达到深度裁剪,性能优化的效果,这样会让你的SystemUI很轻量。