1. 项目概述与核心需求解析最近在基于瑞芯微RK3566/RK3568平台开发Android 11的定制设备时遇到了一个非常典型的需求如何默认屏蔽掉系统自带的导航栏和状态栏。这听起来像是一个简单的UI隐藏问题但实际动手时你会发现它涉及到Android系统框架、Overlay机制、系统属性、甚至内核驱动的协同工作。无论是做商显广告机、自助服务终端还是工业控制平板这个需求都极其普遍。用户希望设备一开机就进入一个纯净的、全屏的应用界面不受系统UI元素的干扰。我接手这个任务时第一反应是去修改SystemUI的源码或者找个adb shell命令临时隐藏。但前者编译工作量大后者重启就失效都不是理想的“默认”方案。经过一番折腾和源码梳理我总结出了一套在RK3566/RK3568 Android 11上稳定、可靠且可批量部署的默认屏蔽方案。这套方案的核心思路不是去“删除”或“禁用”SystemUI而是通过系统级的配置告诉Android“在这个设备上我们不希望显示导航栏和状态栏”。这样做的好处是SystemUI服务本身依然在运行保证了系统稳定性只是其UI组件被设置为不可见并且这个设置从系统启动之初就生效。简单来说我们的目标是在不重新编译整个Android源码的前提下通过修改系统镜像中的特定配置文件实现导航栏和状态栏的永久性隐藏。这需要你具备基本的Android系统知识会使用adb工具并且对设备的boot.img或system.img有一定的操作经验。如果你是设备制造商或系统集成商这套方法可以直接整合到你的量产固件制作流程中。2. 方案选型与底层原理剖析面对隐藏导航栏和状态栏的需求通常有几种技术路径我们需要根据RK3566/RK3568 Android 11的特性进行选择。2.1 常见方案对比与优劣分析应用层全屏标志FLAG_FULLSCREEN原理在应用的Activity中设置WindowManager.LayoutParams.FLAG_FULLSCREEN。分析这是最应用层的方法只对当前Activity有效。当弹出对话框、启动新Activity或按Home键回到桌面时状态栏和导航栏就会重新出现。完全无法满足“设备全局默认隐藏”的需求首先被排除。SystemUI组件禁用adb命令原理通过adb shell执行settings put global policy_control immersive.full*或pm disable-user --user 0 com.android.systemui。分析immersive模式是Android提供的沉浸式API但它是一种“策略”可以被应用或用户行为改变且命令方式非持久化。而直接禁用SystemUI服务是极其危险的操作会导致整个系统UI包括锁屏、通知、近期任务完全崩溃设备可能无法正常使用绝对不可取。修改Framework-Res资源原理反编译framework-res.apk修改bool.xml中config_showNavigationBar和config_showStatusBar的值为false然后重新打包。分析这是一个比较底层的修改理论上可以全局生效。但操作复杂需要处理签名问题并且不同Android版本、不同厂商的framework-res结构可能有差异兼容性和维护成本高。Overlay机制本次采用的核心方案原理Android提供了一种名为“Overlay”的机制允许在不修改原始APK或资源包的情况下用一个新的资源包去覆盖系统中的特定资源。我们可以创建一个Overlay APK专门用于覆盖掉与导航栏、状态栏显示相关的系统资源。分析这是Android官方支持的标准方法安全、灵活、可逆。Overlay包可以像普通应用一样安装、启用、禁用。对于RK3566/RK3568这类有明确BSP板级支持包的硬件平台将其集成到系统镜像中使其在开机时自动启用是实现“默认屏蔽”最优雅的方式。2.2 为什么选择Overlay方案对于RK3566/RK3568这样的嵌入式设备开发方案的选择必须考虑以下几点稳定性不能影响系统核心服务的正常运行。持久性修改需要能经受住重启的考验。可维护性当Android版本升级或需要调整显示策略时修改过程应清晰、可控。平台兼容性方案应在Rockchip的Android 11 BSP上被验证可行。Overlay方案完美契合了这些要求。它工作在SystemUI的上层通过资源替换的方式“欺骗”系统让其认为导航栏和状态栏的高度为0或者不应该被绘制。Rockchip的BSP通常也预留了集成自定义Overlay的接口使得整个集成过程有迹可循。2.3 核心原理资源覆盖与系统属性我们的Overlay将主要覆盖两个关键资源导航栏显示开关对应config_showNavigationBar布尔值。导航栏高度对应dimen资源中的navigation_bar_height和navigation_bar_height_landscape横竖屏高度。将其设置为0dp是实现隐藏的关键。状态栏高度对应dimen资源中的status_bar_height。同样设置为0dp。同时为了确保万无一失我们还需要设置一个关键的系统属性System Propertyqemu.hw.mainkeys。这个属性历史悠久最初用于模拟器但被很多真机系统沿用。当它被设置为1时系统会认为这是一个没有硬件导航键的设备从而强制软件导航栏不显示。这是一个非常重要的“双保险”。因此我们的最终方案是创建一个系统级Overlay APK覆盖相关显示资源并在系统启动的早期阶段设置qemu.hw.mainkeys1属性。3. 实操步骤一创建系统Overlay资源包这是整个过程中最需要细心的一步。我们将手动创建一个Android Studio项目结构的Overlay APK。3.1 项目结构与文件准备首先在你的工作目录下创建一个名为HideSystemBarOverlay的文件夹并建立如下目录结构HideSystemBarOverlay/ ├── AndroidManifest.xml ├── Android.mk (或 Android.bp取决于编译系统) └── res/ └── values/ ├── bools.xml └── dimens.xml我们使用传统的Android.mk因为它更通用在Rockchip的SDK中兼容性好。3.2 编写AndroidManifest.xml这个文件定义了Overlay包的基本信息。关键点在于android:targetPackage和android:priority。?xml version1.0 encodingutf-8? manifest xmlns:androidhttp://schemas.android.com/apk/res/android packagecom.custom.overlay.hidesystembar android:versionCode1 android:versionName1.0 overlay android:targetPackageandroid android:targetNameAndroidSystemUI android:priority9999 android:isStatictrue / application android:labelHide System Bar Overlay android:hasCodefalse android:allowBackupfalse /application /manifestpackageOverlay包的名字可以自定义。overlay标签这是核心。android:targetPackageandroid我们的目标是覆盖android包即系统框架的资源因为导航栏/状态栏的显示定义在framework-res中。android:priority9999设置一个非常高的优先级确保我们的Overlay能覆盖其他可能存在的Overlay。android:isStatictrue将其声明为静态Overlay这类Overlay在系统构建时就被确定并且默认启用无法通过普通应用安装方式更改非常适合集成到固件中。3.3 编写资源覆盖文件res/values/bools.xml用于覆盖显示开关。?xml version1.0 encodingutf-8? resources !-- 屏蔽导航栏 -- bool nameconfig_showNavigationBarfalse/bool !-- 通常状态栏没有简单的bool开关主要通过高度控制但这里可以一并设置 -- !-- 有些系统版本可能有 config_showStatusBar如有则添加 -- !-- bool nameconfig_showStatusBarfalse/bool -- /resourcesres/values/dimens.xml用于将高度设置为0这是隐藏的物理实现。?xml version1.0 encodingutf-8? resources !-- 设置导航栏高度为0 -- dimen namenavigation_bar_height0dp/dimen dimen namenavigation_bar_height_landscape0dp/dimen dimen namenavigation_bar_width0dp/dimen !-- 竖屏时的导航栏宽度也设为0 -- !-- 设置状态栏高度为0 -- dimen namestatus_bar_height0dp/dimen dimen namestatus_bar_height_portrait0dp/dimen dimen namestatus_bar_height_landscape0dp/dimen !-- 可选关闭导航栏的边距确保全屏 -- dimen namenavigation_bar_gesture_height0dp/dimen /resources3.4 编写编译脚本Android.mk这个文件告诉Android编译系统如何构建我们的Overlay。LOCAL_PATH : $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE : HideSystemBarOverlay LOCAL_MODULE_TAGS : optional LOCAL_SRC_FILES : $(call all-subdir-java-files) LOCAL_RESOURCE_DIR : $(LOCAL_PATH)/res LOCAL_PACKAGE_NAME : HideSystemBarOverlay LOCAL_SDK_VERSION : current LOCAL_CERTIFICATE : platform # 使用平台签名这是系统应用权限 LOCAL_PRIVILEGED_MODULE : true LOCAL_MODULE_CLASS : APPS LOCAL_OVERRIDES_PACKAGES : LOCAL_UNINSTALLABLE_MODULE : true # 标记为不可卸载 LOCAL_STATIC_ANDROID_LIBRARIES : include $(BUILD_PACKAGE)关键点LOCAL_CERTIFICATE : platform使用系统平台签名这是Overlay能覆盖android包资源的前提。LOCAL_UNINSTALLABLE_MODULE : true防止这个包被意外卸载。LOCAL_MODULE和LOCAL_PACKAGE_NAME定义了生成APK的名字。3.5 编译生成Overlay APK你需要将整个HideSystemBarOverlay目录放到你的RK3566/RK3568 Android 11源码树下通常放在vendor/rockchip/common/overlays/或packages/overlays/目录下具体路径请参考你的SDK文档。然后在源码根目录执行source build/envsetup.sh lunch rk3566_r-userdebug (或你的具体产品午餐项) make HideSystemBarOverlay编译成功后你可以在out/target/product/rk3566_r/system/overlay/或out/target/product/rk3566_r/system/product/overlay/目录下找到HideSystemBarOverlay.apk。注意如果你没有完整的源码编译环境Rockchip通常会在SDK中提供预编译的system.img。你可以使用解包工具如erofs或ext4工具将system.img挂载或解压手动将编译好的APK放入system/product/overlay/目录然后重新打包。但这需要你对镜像操作非常熟悉。4. 实操步骤二集成Overlay与设置系统属性仅仅有Overlay APK还不够我们需要确保它被系统识别并启用同时设置那个关键的“双保险”属性。4.1 将Overlay集成到系统镜像对于量产固件我们需要修改设备的makefile通常是device/rockchip/rk3566_r/device.mk或product.mk将我们的Overlay添加到PRODUCT_PACKAGES中。# 在 device.mk 文件中添加 PRODUCT_PACKAGES \ HideSystemBarOverlay同时确保Overlay的路径被包含在系统的Overlay搜索路径中。在Android 11上系统会自动扫描system/product/overlay和vendor/overlay等目录。将APK放在正确位置后由于其isStatictrue和高优先级系统会在启动时自动启用它。4.2 设置系统属性 qemu.hw.mainkeys这是确保导航栏消失的强力手段。我们需要在系统启动的早期阶段设置这个属性。最合适的地方是在设备的init.rc文件通常是device/rockchip/rk3566_r/init.rk3566.rc或类似的init.xxx.rc中。找到on boot或on early-init阶段添加一行# 在 init.xxx.rc 文件中 on early-init # ... 其他初始化命令 ... setprop qemu.hw.mainkeys 1on early-init阶段非常早此时系统服务还未完全启动在这里设置属性可以最大程度地影响SystemUI的初始化逻辑。4.3 验证属性设置除了在init.rc中设置我们还可以在system.prop通常在同一设备目录下中设置作为备份# system.prop qemu.hw.mainkeys1修改完init.rc和system.prop后需要重新编译boot.img因为init.rc打包在boot.img的ramdisk里和system.img。4.4 重新编译固件在源码根目录执行make -j8或者单独编译bootimage和systemimagemake bootimage -j8 make systemimage -j8编译完成后使用Rockchip提供的工具如upgrade_tool将新的boot.img和system.img烧录到RK3566/RK3568设备中。5. 效果验证与问题排查烧录新固件后首次开机是验证成果的时刻。5.1 开机验证步骤观察开机动画与锁屏设备启动后在开机动画和锁屏界面你就应该看不到顶部的状态栏时间、电量图标和底部的导航栏返回、主页、多任务键。进入系统桌面解锁进入桌面。传统的Android桌面Launcher可能会预留状态栏位置但由于高度为0它应该是完全全屏的。导航栏区域应该完全空白无法滑动呼出。使用adb命令确认连接设备adb shell。检查Overlay状态cmd overlay list。你应该在输出列表中看到com.custom.overlay.hidesystembar的状态是[ ]静态Overlay可能不在此列表动态显示或者通过[x]显示已启用。更直接的方法是检查android包的资源映射。检查系统属性getprop qemu.hw.mainkeys。返回值应该是1。检查SystemUI组件状态dumpsys window | grep -i policy或dumpsys window displays。查看输出中关于mSystemUiVisibility、mHasNavigationBar等信息。如果看到mHasNavigationBarfalse则说明设置成功。检查资源值可以通过编写一个简单的测试APP调用getResources().getBoolean(com.android.internal.R.bool.config_showNavigationBar)和getResources().getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height)来验证返回值是否为false和0。5.2 常见问题与排查技巧实录即使按照步骤操作也可能会遇到一些问题。下面是我在调试过程中遇到的一些坑和解决方法问题现象可能原因排查步骤与解决方案状态栏隐藏但导航栏还在1. Overlay未生效优先级低或被覆盖。2.qemu.hw.mainkeys属性未成功设置。3. 设备有物理导航键系统策略不同。1.adb shell cmd overlay list查看Overlay状态。确保我们的Overlay优先级最高。可以临时用adb shell cmd overlay enable com.custom.overlay.hidesystembar测试。2.adb shell getprop qemu.hw.mainkeys确认是否为1。如果不是检查init.rc语法和烧录的boot.img是否正确。3. 检查内核设备树(.dts)中关于gpio-keys的配置看是否定义了物理按键。有些系统检测到物理按键会强制启用软件栏作为补充需要调整策略。开机后先隐藏但几秒后又出现有其他系统服务或应用在启动后修改了窗口策略或系统属性。1. 使用adb logcat | grep -E “(NavigationBar横竖屏切换时导航栏区域出现黑条或布局错乱dimens.xml中只设置了竖屏高度横屏高度覆盖不完整或某些特殊尺寸资源未覆盖。1. 确保dimens.xml中同时设置了navigation_bar_height和navigation_bar_height_landscape。2. 检查navigation_bar_width竖屏时的宽度是否也设为0dp。3. 可以尝试在Overlay中覆盖更多相关的dimen资源如navigation_bar_frame_height等。使用adb shell dumpsys window查看当前窗口的frame信息分析黑条是哪个部分。SystemUI崩溃或系统不稳定Overlay覆盖了错误的资源或者资源类型不匹配。1.这是最危险的情况。立即通过adb shell cmd overlay disable禁用你的Overlay包。2. 仔细检查bools.xml和dimens.xml中的资源名是否与目标framework-res.apk中的完全一致。可以使用aapt dump resources framework-res.apk命令来查看原版资源名和类型。3. 确保Overlay只覆盖你需要修改的资源不要添加任何代码(hasCodefalse)。烧录后无法开机卡在Logo界面1.init.rc语法错误导致初始化失败。2. Overlay APK签名错误或集成位置不对导致系统验证失败。1. 这是严重的变砖风险。务必在修改init.rc前备份原文件并使用androidboot.selinuxpermissive内核参数启动查看adb logcat输出定位init阶段的错误。2. 确保Overlay APK使用了正确的platform签名并且放在了正确的系统只读分区如system/product/overlay。错误的路径可能导致系统无法挂载。实操心得调试系统级修改adb logcat和dumpsys是你最好的朋友。尤其是logcat在系统启动阶段就连接上设备过滤SystemUI、WindowManager、OverlayManagerService等标签的日志能清晰地看到资源加载、属性生效、UI组件初始化的全过程绝大多数问题都能从日志中找到线索。5.3 高级技巧应对特殊场景临时恢复系统栏出于调试目的你可能需要临时查看状态栏。可以通过adb shell settings put global policy_control null命令来临时清除沉浸策略如果系统使用了的话但更根本的是adb shell setprop qemu.hw.mainkeys 0然后重启SystemUIadb shell pkill -f com.android.systemui。注意这需要你的系统SystemUI有响应kill信号并重启的能力。针对特定应用不隐藏如果你希望设备上某个特定的应用比如系统设置仍然显示状态栏Overlay方案是全局的无法做到。这需要在应用层面处理或者使用更复杂的policy_control策略例如immersive.fullapps,-com.android.settings但这需要修改SystemUI或WindowManager的代码超出了本文默认屏蔽的范围。与手势导航的兼容在Android 10及以上版本手势导航逐渐成为主流。隐藏导航栏后手势操作如底部上滑返回桌面可能会失效。你需要确认你的设备是否启用了手势导航settings get secure swipe_up_to_switch_apps_enabled并测试手势是否仍然有效。有时需要额外覆盖config_navBarInteractionMode等资源。在RK平台上也可能需要配合内核和InputReader的配置来确保触摸事件能正确传递。经过以上步骤你应该能在RK3566/RK3568的Android 11设备上实现导航栏和状态栏的完美默认隐藏。这套方法的核心在于理解Android的资源覆盖机制和系统属性通过静态Overlay和init.rc属性设置这两个“组合拳”从系统底层实现干净、稳定的全局隐藏效果非常适合嵌入式设备的量产定制。