安卓桌宠开发实战:WindowManager悬浮窗与小米背屏适配指南

安卓桌宠开发WindowManager悬浮窗技术
于 2026-08-01 03:55:02 修改
·本内容遵循CC 4.0 BY-SA版权协议

最近在小米17pro背屏版上实现夏娜桌宠的过程中,发现很多开发者对安卓桌宠开发的技术细节和适配方案了解不够深入。本文将完整分享从环境搭建到背屏适配的全流程实战经验,包含完整的代码示例和避坑指南,适合有一定安卓开发基础的开发者参考使用。

1. 安卓桌宠开发基础概念

1.1 什么是手机桌宠

手机桌宠是一种运行在手机桌面上的虚拟宠物应用,它能够与用户进行互动,显示各种动画效果,为手机桌面增添趣味性。与传统应用不同,桌宠需要常驻在桌面上,并且能够响应触摸事件,实现拖拽、点击等交互功能。

在安卓平台上,桌宠通常通过WindowManager来实现悬浮窗效果,结合SurfaceView或TextureView来渲染动画。开发桌宠应用需要掌握安卓窗口管理、视图渲染、触摸事件处理等多个核心技术点。

1.2 夏娜桌宠的技术特点

夏娜桌宠作为动漫角色桌宠的代表,具有以下技术特点:

  • 高帧率动画渲染:需要流畅播放角色动画序列帧
  • 多状态切换: idle、walk、sleep、interact等多种状态
  • 触摸交互响应:点击、拖拽、长按等不同手势识别
  • 资源优化管理:大量图片资源的加载和内存管理
  • 背屏适配:针对小米17pro背屏的特殊适配方案

2. 开发环境准备与工具配置

2.1 基础开发环境

JAVA
// build.gradle (Module: app)
android {
compileSdk 34
defaultConfig {
applicationId "com.example.shanadesktoppet"
minSdk 24
targetSdk 34
versionCode 1
versionName "1.0"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
 
dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.google.android.material:material:1.9.0'
// 动画库
implementation 'com.airbnb.android:lottie:6.1.0'
// 图片加载
implementation 'com.github.bumptech.glide:glide:4.15.1'
}

2.2 权限配置

桌宠应用需要申请悬浮窗权限和必要的系统权限:

XML
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
 
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.DesktopPet">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 桌宠服务 -->
<service android:name=".DesktopPetService" />
</application>

3. 核心架构设计与实现

3.1 窗口管理实现

桌宠的核心是通过WindowManager创建悬浮窗,下面是关键实现代码:

JAVA
public class DesktopPetService extends Service {
private WindowManager windowManager;
private View petView;
private WindowManager.LayoutParams params;
@Override
public void onCreate() {
super.onCreate();
initWindowManager();
createPetView();
}
private void initWindowManager() {
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
PixelFormat.TRANSLUCENT
);
params.gravity = Gravity.TOP | Gravity.START;
params.x = 0;
params.y = 100;
}
private void createPetView() {
petView = LayoutInflater.from(this).inflate(R.layout.pet_layout, null);
// 设置触摸监听实现拖拽
petView.setOnTouchListener(new View.OnTouchListener() {
private int initialX, initialY;
private float initialTouchX, initialTouchY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
initialX = params.x;
initialY = params.y;
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
最低 0.47元/天 开通会员,解锁全文
left
成为会员后, 你将解锁
right
benefits 下载资源随意下
benefits 优质VIP博文免费学
benefits 优质文库回答免费看
benefits 付费资源9折优惠
Android 悬浮窗权限各机型各系统适配大全
本文详细介绍Android悬浮窗权限在不同品牌、不同版本机型上的适配方法,包括正常适配流程特殊适配技巧,以及从4.4到6.0及以后版本的适配策略。
Shawn_Dut
84283
Android悬浮窗适配全机型,包含8.0,小米魅族华为悬浮窗权限适配demo看这一篇就够了
本文介绍了如何在Android设备上实现全机型兼容的悬浮窗,包括在API 23及以上版本处理权限适配,特别是针对小米、魅族、华为等品牌的特殊权限设置。通过WindowManager服务和特定的LayoutParams设置,确保在Android 8.0以上的系统中使用正确的窗口类型。同时,分享了相关悬浮窗实现方案的Demo和权限开启库。
锟钢
11117
Android悬浮窗开发实战:从权限申请到交互实现
本文系统讲解Android悬浮窗开发全流程,涵盖SYSTEM_ALERT_WINDOW权限申请与适配WindowManager及LayoutParams核心配置(重点强调TYPE_APPLICATION_OVERLAY)、Service中悬浮窗生命周期管理、拖拽交互实现(含边界约束吸附)、点击事件处理内容动态更新(推荐LiveData/Flow方案),以及性能优化要点和常见兼容性问题(如小米/华为定制ROM)排查。
812
Android悬浮窗开发指南:从原理到多版本适配
本文系统讲解Android悬浮窗核心技术,涵盖WindowManager窗口层级、Z-order机制及TYPE_APPLICATION_OVERLAY类型限制;详述Android 8.0至12+各版本权限变更(如SYSTEM_ALERT_WINDOW动态申请、后台限制)、国产ROM(小米/华为)特殊适配方案;并提供交互事件处理、内存泄漏预防、ADB调试技巧及Bubble API等替代方案,强调模块化浮窗管理设计。
csdn产品小助手
521
Android悬浮窗实现与适配全解析
本文系统解析Android悬浮窗的层级机制(WindowManager.Type分类Z-order决定逻辑),梳理Android 8.0至12+各版本对TYPE_APPLICATION_OVERLAY等窗口类型的限制演进,详述权限动态申请、厂商ROM(小米/华为/OPPO/VIVO)适配方案,并涵盖点击事件处理、内存泄漏预防、BadTokenException等崩溃问题排查方法,强调Application Context使用前台服务合规启动。
weixin_34161032
459
Unity安卓悬浮窗开发实战:WindowManager与TYPE_APPLICATION_OVERLAY深度解析
本文深入解析Unity与Android原生层协同实现TYPE_APPLICATION_OVERLAY悬浮窗的技术路径,涵盖Manifest权限配置、WindowManager动态添加View、可拖拽缩放UI实现、Unity-C#Java通过JNI双向通信机制,以及真机调试中黑屏、点击穿透失效、ANR等典型问题的根因修复方案。重点强调Android 8.0+兼容性、前台Service保活、跨线程UI更新及ROM适配等工业级落地要点。
weixin_30781775
791
Android 防微信语音通话悬浮窗实战:WindowManager 权限管控与悬浮窗拦截
本文聚焦Android平台微信语音通话悬浮窗的拦截方案,详解基于AccessibilityService的监控拦截机制,涵盖WindowManager权限演进(TYPE_SYSTEM_ALERT到TYPE_APPLICATION_OVERLAY)、国产ROM适配难点(如小米白名单)、性能优化策略(事件过滤、冷却期)及合规边界(仅限自身App范围拦截)。重点分析Android 7.4+系统限制厂商定制化差异。
按时吃饭964
810
Unity开发Android桌面悬浮窗精灵透明Activity与WindowManager实战
本文详解如何基于Unity与Android原生协同,实现桌面级透明悬浮窗精灵。核心采用方案二将Unity Player Activity设为透明主题,通过WindowManager动态控制其窗口属性,使其表现为系统级悬浮窗。涵盖Unity透明渲染配置、Gradle工程导出、Android悬浮窗服务开发、Unity-Android双向通信(AndroidJavaObjectSendMessage)、悬浮窗权限(SYSTEM_ALERT_WINDOW)动态适配(API 26+需TYPE_APPLICATION_OVERLAY)、后台保活(前台Service)、以及黑屏、触摸穿透等高频问题排查。技术聚焦移动端混合开发实战
weixin_33858336
286
Android 悬浮窗完全指南
本文系统阐述Android悬浮窗核心技术,涵盖WindowManager原理、SYSTEM_ALERT_WINDOW权限动态适配(尤其API 27+ TYPE_APPLICATION_OVERLAY)、Service生命周期管理、跨版本兼容策略(含华为/小米/OPPO等厂商定制ROM适配)、拖拽交互Activity通信机制,以及内存/电量优化和Google Play合规要点。
Monkey-旭
4616
从零到一:Android悬浮窗的权限迷宫系统版本适配实战
本文详解Android悬浮窗开发核心技术,涵盖WindowManager与LayoutParams原理、SYSTEM_ALERT_WINDOW权限的特殊申请流程及用户拒绝应对策略;重点分析Android 8.0(TYPE_APPLICATION_OVERLAY)、10+版本限制增强,以及小米、华为等主流厂商定制系统的差异化处理;同时涉及后台Service生命周期管理、拖拽体验优化、多窗口/分屏适配等关键实践。
775
Android悬浮窗开发指南:从原理到实践
本文系统讲解Android悬浮窗的实现原理工程实践,重点涵盖WindowManager机制、TYPE_APPLICATION_OVERLAY等窗口类型特性、Android 8.0前后适配差异、多ROM兼容性处理(如MIUI/EMUI/ColorOS)、层级控制、焦点管理、内存泄漏预防及可拖拽悬浮窗实战。内容聚焦权限动态申请、FLAG标志位配置、生命周期管理ADB调试技巧,面向移动端开发者提供全链路技术解决方案。
weixin_34059951
316
Android悬浮窗功能实战:从零实现微信语音通话悬浮窗效果
本文详细介绍了在Android平台上从零实现类似微信语音通话悬浮窗的功能,涵盖权限申请、窗口类型选择(TYPE_APPLICATION_OVERLAY)、悬浮窗控制器设计及事件处理。针对开发中的常见痛点,如厂商ROM适配、内存泄漏和屏幕适配提供了避坑指南,并给出性能优化建议,适合中高级Android开发者参考。
咖啡 + Code
1190
写一个来电话时出现的悬浮窗,一站解决黑马手机卫士来电悬浮窗的高版本安卓系统的适配问题
本文详细介绍了如何解决Android6.0及以上版本中,小米、魅族等手机使用 WindowManager 实现悬浮窗时出现的 Androidpermissiondeniedforwindowtype2002 错误。同时,针对黑马程序员教学视频中悬浮窗在高版本安卓手机上不能移动的问题,提供了具体的解决方案,包括设置 params.type 为 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY 的方法。
杨小二ˇ
1182
Unity安卓悬浮窗开发:原生插件SYSTEM_ALERT_WINDOW权限实战
本文详解Unity引擎在Android平台实现系统级悬浮窗的完整技术路径,聚焦SYSTEM_ALERT_WINDOW权限适配(含6.0+动态申请、8.0+TYPE_APPLICATION_OVERLAY迁移、10~12厂商ROM拦截)、基于Java/Kotlin的原生插件架构(FloatingWindowManager、SurfaceView承载JNI双向通信)、Unity侧C#集成要点(Player Settings配置、AndroidManifest四行关键声明)及五大高频问题排查(黑屏、无响应、坐标偏移、后台消失、华为适配)。内容面向具备Android构建经验的中级Unity开发者。
weixin_30755393
700
Android悬浮窗开发避坑指南:TYPE_TOASTTYPE_APPLICATION_OVERLAY的版本适配陷阱
本文详解Android悬浮窗从TYPE_TOAST到TYPE_APPLICATION_OVERLAY的演进路径,重点覆盖API 23+动态权限申请、窗口类型按版本自动切换、LayoutParams配置规范、点击事件穿透规避、厂商ROM差异化适配小米/华为/OPPO等),以及Android 9+对SYSTEM_ALERT_WINDOW权限的严格管控策略。
879
安卓股票悬浮窗_Android悬浮窗的实现
本文介绍如何在Android中实现悬浮窗功能,并演示了简单的应用案例,如拖动悬浮窗、自动播放图片和视频小窗口。
weixin_39673002
1385
android视频聊天桌面小窗口怎么实现,android视频通话悬浮窗适配
本文详细介绍了如何在Android中实现视频通话的悬浮窗功能,包括使用WindowManager添加悬浮窗、处理不同Android版本的兼容问题、解决SingleInstance启动模式onActivityResult的配合问题,以及针对不同手机厂商的适配策略。通过设置LayoutParams类型、处理悬浮窗点击拖动事件,成功实现了视频聊天的桌面小窗口功能。
天眼妹
2031
Unity透明背景桌面宠物开发:Android悬浮窗口实现全解析
本文详解Unity与Android Studio协同实现透明背景桌面宠物的技术路径涵盖Unity端摄像机Clear Flags设为Don't Clear、Alpha背景置零、禁用Vulkan并导出为Android库;Android端通过自定义Activity或WindowManager添加透明SurfaceView/TextureView,配置FLAG_NOT_TOUCH_MODAL等窗口标志,并适配Android 8.0+悬浮窗权限(SYSTEM_ALERT_WINDOW);同时包含触摸拖动交互、30FPS帧率控制、动态休眠及分层调试方法。
1361976860
342
Android 悬浮窗沉浸式全屏适配实战
本文深入解析Android悬浮窗在高版本系统(尤其是8.0+)中实现真正全屏沉浸式显示的关键技术重点围绕WindowManager.LayoutParams的FLAG_LAYOUT_NO_LIMITS等核心标志位组合、TYPE_APPLICATION_OVERLAY窗口类型选择、状态栏/导航栏穿透处理、安全区域动态计算(WindowInsets)、主流厂商(华为、小米、OPPO/vivo)定制化适配策略及SYSTEM_ALERT_WINDOW权限增强管理。同时涵盖性能优化真机兼容性排查要点。
311
android适配华为m5,2019-05-29 Android悬浮窗适配全机型,包含8.0,小米魅族华为悬浮窗权限适配demo看这一篇就够了...
本文详细介绍了如何在Android系统中实现悬浮窗适配,包括针对8.0及以上版本的特殊处理,以及如何处理权限问题。通过WindowManager服务的addView方法添加悬浮窗,并设置LayoutParams的type属性来适配不同Android版本。在API 23及以上版本,需要声明SYSTEM_ALERT_WINDOW权限,并引导用户在系统设置中开启。同时,文章提到了一个示例项目,具有吸附、点击展开等功能,适合开发者参考。
weixin_39911567
397
Android 悬浮窗权限各机型各系统适配大全(总结)
国产手机的种类实在是过于丰富,每个品牌的不同版本还有不一样的适配方法。可以通过以下几种方法来适配悬浮窗权限1. 直接百度一下,搜索关键词“小米手机悬浮窗适配”等;2.
weixin_38629130
1535
Android悬浮窗实现 使用WindowManager Demo
Android悬浮窗(Floating Window)是Android平台中一种突破Activity生命周期界面层级限制的高级UI呈现技术,其核心实现机制依赖于系统级窗口管理服务——WindowManager。本Demo标题《Android悬浮窗实现 使用WindowManager Demo》精准指向了Android开发中一个典型但极具挑战性的实践场景在任意前台应用(甚至锁、桌面、其他App界面)之上,稳定、合规、跨版本兼容地展示一个可交互的悬浮视图,例如电视端常见的顶部滚动文字广告栏(类似新闻跑马灯、实时通知条、快捷工具面板等)。该功能并非基于Activity或Fragment构建,而是绕过传统组件生命周期,直接通过WindowManager将View注入到系统窗口层级(Window Layer),从而获得全局可见性高优先级显示能力。实现悬浮窗的核心类是android.view.WindowManager,它是Android Framework层提供的系统服务代理,用于向WMS(Window Manager Service)申请窗口添加、更新移除操作。开发者需通过Context.getSystemService(Context.WINDOW_SERVICE)获取其实例。关键在于WindowManager.LayoutParams配置——它定义了窗口的类型(type)、坐标(x/y)、尺寸(width/height)、标志(flags)、动画、焦点行为、触摸事件处理策略等。在Android 8.0(API 26)之前,常用TYPE_SYSTEM_ALERT;但自Android 8.0起,Google出于安全用户体验考量,彻底废弃了该类型,并强制要求使用TYPE_APPLICATION_OVERLAY(值为2038),该类型专为“覆盖在其他应用之上但非恶意干扰”的合理用途设计,如视频画中画、辅助工具、无障碍服务UI等。这意味着所有目标API ≥ 26的应用,若未适配此类型,悬浮窗将完全无法显示,且系统会抛出WindowManager.BadTokenException异常。权限适配是悬窗开发不可逾越的红线。从Android 6.0(API 23)起,SYSTEM_ALERT_WINDOW(即“绘制叠加窗口”权限)被列为特殊运行时权限(Special Permission),不属于危险权限组(Dangerous Permissions),因此不能通过requestPermissions()常规流程申请,而必须引导用户手动进入系统设置页授权调用Settings.ACTION_MANAGE_OVERLAY_PERMISSION跳转至授权界面,并通过Settings.canDrawOverlays()动态检测是否已获授权。未授权状态下调用WindowManager.addView()将直接失败并抛出SecurityException。此外,在Android 10(API 29)及更高版本中,部分厂商ROM(如小米MIUI、华为EMUI)进一步强化了限制,需额外申请“悬浮窗”、“显示在其他应用上层”等定制化权限,甚至需在应用信息页手动开启“允许显示在其他应用上层”开关,这对兼容性测试提出了极高要求。布局交互层面,悬浮窗View通常采用FrameLayout或自定义ViewGroup承载,支持TextView、ImageView、Button等标准控件,亦可嵌入SurfaceView或TextureView实现视频播放。为模拟电视顶端广告栏效果,常配合Handler+Runnable实现循环滚动动画(如平移translationX)、TextSwitcher实现文字切换、或使用RecyclerView+LinearSmoothScroller实现高性能列表滚动。触摸事件需特别处理通过LayoutParams.flags添加FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_LAYOUT_NO_LIMITS,使悬浮窗不抢占焦点、允许穿透点击底层应用,同时自身仍能响应onTouchListener。拖拽移动则需监听MotionEvent.ACTION_DOWN/ACTION_MOVE,动态更新LayoutParams.x/y并调用WindowManager.updateViewLayout()刷新位置。工程实践中,还需应对多维度兼容性挑战不同Android版本对窗口Z轴顺序(layer)、透明度(alpha)、输入事件拦截策略差异显著;横竖屏切换时需监听Configuration变更并重设LayoutParams;内存泄漏风险极高——因WindowManager持有View强引用,若在Activity onDestroy()中未及时removeView(),将导致Activity无法被GC回收;建议将悬浮窗逻辑封装为Service(如Foreground Service)或Application级单例管理,并结合LifecycleObserver监听应用前后台状态,实现智能启停。综上,本Demo不仅是一次WindowManager API的实操演练,更是对Android系统窗口机制、权限模型、版本演进、安全沙箱及UI架构设计的深度整合,是中高级Android工程师必须掌握的核心硬技能之一。
clever_jian
Android实现类似qq微信消息悬浮窗通知功能
在创建悬浮窗时,需要设置系统 Window 类型,以便能够显示在桌面上。4. 权限设置Android 6.0 之后,需要动态权限来显示悬浮窗
weixin_38688906
1468
Android 可移动悬浮窗WindowManager
Android中的可移动悬浮窗口(FloatView)是Android系统中一种特殊的UI组件,其核心实现依赖于WindowManager服务自定义View的深度结合。该技术允许开发者在系统任意界面之上(包括锁、桌面、第三方应用界面等)动态添加、拖动、缩放甚至响应交互的独立视图,广泛应用于全局快捷工具(如录开关、语音助手浮点、性能监控面板)、社交App消息气泡(如微信未读消息悬浮提示)、辅助无障碍功能(如手势悬浮球)以及企业级远程协助SDK等场景。其实现本质并非Activity或Fragment,而是通过WindowManager直接将一个View对象“注入”到系统窗口层级(Window Layer)中进行渲染事件分发,因此具备极高的系统穿透性UI自由度。核心机制围绕WindowManager类展开它是Android Framework层提供的系统级服务代理,用于管理所有窗口的生命周期、布局参数、Z轴顺序、输入事件拦截等。开发者需通过Context.getSystemService(Context.WINDOW_SERVICE)获取其实例。关键难点在于LayoutParams的配置——尤其是窗口类型(Window Type)的选取。在Android 8.0(API 26)之前,常使用TYPE_PHONE(已废弃)或TYPE_SYSTEM_ALERT;而自Android 8.0起,强制要求使用TYPE_APPLICATION_OVERLAY(值为2038),该类型专为应用级覆盖窗口设计,既保障功能可用性,又避免滥用系统警报权限带来的安全风险。此变更直接关联权限管理应用必须在AndroidManifest.xml中声明<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />,且在运行时(Android 6.0+)调用Settings.canDrawOverlays()检测并引导用户手动开启“显示在其他应用上方”开关,否则addView()将抛出SecurityException。这一权限管控机制体现了Android系统对悬浮窗滥用行为(如恶意广告、诱导点击、隐私窃取)的持续治理。FloatView的具体实现需严格遵循三步流程首先,构造自定义View(如FloatView类),通常继承FrameLayout或RelativeLayout,内置拖动手势识别(onTouchEvent中监听MotionEvent.ACTION_DOWN/MOVE/UP,结合View.setX()/setY()或LayoutParams.x/y动态更新坐标);其次,创建WindowManager.LayoutParams实例,设置type=TYPE_APPLICATION_OVERLAY、flags=FLAG_NOT_FOCUSABLE|FLAG_NOT_TOUCH_MODAL|FLAG_LAYOUT_IN_SCREEN(确保不抢占焦点、支持穿透点击、适配全屏坐标系),并指定width/height(常用WRAP_CONTENT或MATCH_PARENT);最后,调用windowManager.addView(floatView, layoutParams)完成挂载。值得注意的是,View的销毁必须显式调用windowManager.removeView(floatView),否则将导致内存泄漏及窗口残留——因WindowManager持有View强引用,且系统不会自动回收非Activity托管的窗口资源。此外,为适配不同Android版本(如Android 9.0限制后台启动Activity、Android 10引入分区存储影响悬浮窗持久化配置),还需在onConfigurationChanged()中监听屏幕旋转、多窗口模式变化,并动态调整LayoutParams的gravity或尺寸;在onDestroy()或Application.onLowMemory()中妥善释放资源。进阶优化涉及多个维度一是手势平滑性,需采用VelocityTracker计算滑动速率,结合Scroller实现惯性滚动;二是系统兼容性,针对小米、华为、OPPO等厂商定制ROM的悬浮窗白名单机制,需在代码中嵌入机型判断逻辑并跳转至对应品牌权限设置页;三是无障碍适配,为满足残障用户需求,应为FloatView设置contentDescription并通过AccessibilityNodeInfo暴露操作语义;四是性能监控,利用Choreographer检测每帧渲染耗时,避免因频繁setLayoutParams导致掉帧;五是安全加固,对悬浮窗内WebView加载的远程内容实施StrictMode网络策略、Content-Security-Policy头校验及JavaScript沙箱隔离。综上,Android可移动悬浮窗口绝非简单调用几行API即可实现,而是横跨系统服务原理、权限模型演进、触摸事件分发机制、内存生命周期管理及厂商生态适配的综合性高阶技术,其深度直接反映开发者对Android Framework底层架构的理解水平工程实践能力。
悬浮窗悬浮窗+锁屏悬浮窗+锁屏悬浮窗+锁屏悬浮窗+锁屏悬浮窗+锁
悬浮窗(Floating Window)屏悬浮窗(Lock Screen Floating Window)是Android平台中极具技术深度系统权限敏感性的高级UI机制,其核心涉及窗口管理、系统级权限控制、Activity生命周期干预、前台服务协同以及窗口层级(Window Type)的精细调度。在Android开发中,“悬浮窗”并非普通Dialog或PopupWindow,而是通过WindowManager直接添加到系统窗口堆栈(Window Stack)中的独立Surface视图,具备跨应用、跨界面、甚至跨状态(如锁态)的显示能力,因而被广泛应用于即时通讯消息气泡(如微信小窗)、屏幕录制浮点控件、全局快捷工具(如悬浮计时器)、无障碍辅助操作(如自动点击浮标)等场景。实现悬浮窗的基础前提是获取`SYSTEM_ALERT_WINDOW`权限——这是Android中极少数被归类为“特殊应用权限”(Special App Access)的危险权限之一。自Android 6.0(API 23)起,该权限不再通过``静态声明即可授予,而必须在运行时通过`Settings.canDrawOverlays(Context)`检测并引导用户手动跳转至系统设置页启用(`Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)`)。值得注意的是,从Android 8.0(API 26)开始,若应用目标SDK ≥ 26,调用`WindowManager.addView()`时若未事先获得该权限,将直接抛出`SecurityException`;而Android 10(API 29)进一步强化限制非系统签名应用在锁状态下默认无法显示TYPE_APPLICATION_OVERLAY类型窗口;Android 12(API 31)则引入了更严格的窗口可见性策略,要求锁屏悬浮窗必须显式声明`FLAG_SHOW_WHEN_LOCKED``FLAG_DISMISS_KEYGUARD`标志,并配合`KeyguardManager`进行锁状态监听密钥防护绕过协商。因此,“锁屏悬浮窗”的实现绝非简单叠加权限,而是需构建一套完整的状态感知—权限校验—窗口注入—生命周期同步—安全降级的闭环逻辑。技术实现层面,关键依赖`WindowManager`系统服务及其配套参数`WindowManager.LayoutParams`。开发者需构造具备特定`type`值的LayoutParams对于普通悬浮窗,常使用`TYPE_APPLICATION_OVERLAY`(API 26+推荐,替代已废弃的`TYPE_PHONE`/`TYPE_SYSTEM_ALERT`);而对于锁屏悬浮窗,则必须结合`FLAG_FULLSCREEN`、`FLAG_LAYOUT_IN_SCREEN`、`FLAG_LAYOUT_INSET_DECOR`、`FLAG_NOT_FOCUSABLE`、`FLAG_NOT_TOUCH_MODAL`等标志位,确保窗口能穿透锁界面、不抢占焦点、支持透传触摸事件,并适配不同厂商的锁定制逻辑(如华为EMUI、小米MIUI对锁Z轴层级有额外白名单校验)。同时,窗口View需以`ViewGroup`为根,支持动态更新(如`updateViewLayout()`),并严格遵循内存管理规范——在`onDestroy()`或`onStop()`中及时`removeView()`,否则极易引发`WindowManager$BadTokenException`或内存泄漏。此外,“锁屏悬浮窗”往往需`Activity`生命周期解耦,因其需在目标Activity不可见(如锁、Home键退后台)时仍保持活跃。此时单纯依赖Activity内`WindowManager`实例将失效,必须升级为Application Context级别管理,并配合前台服务(Foreground Service)保活(需声明`FOREGROUND_SERVICE`权限并调用`startForeground()`发布通知)或无障碍服务(AccessibilityService)监听屏幕状态变更(通过`AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED`捕获`isScreenLocked()`)。更稳健的方案是融合多机制利用`BroadcastReceiver`监听`Intent.ACTION_SCREEN_ON/OFF/LOCKED/UNLOCKED`,结合`KeyguardManager.isKeyguardLocked()`实时判断锁状态,在锁激活瞬间通过`WindowManager`注入高优先级窗口,并在解锁后平滑过渡或自动隐藏。整个过程还需处理横竖屏旋转、多窗口模式(Split-Screen/Freeform)、刘海屏/挖孔屏适配、深色主题兼容、输入法遮挡规避等工程细节。综上所述,“悬浮窗+锁屏悬浮窗”是一套横跨权限模型、窗口系统、系统服务、生命周期管理、安全策略厂商定制的综合性技术体系。它不仅是Android底层机制的集中体现,更是检验开发者对Framework层理解深度的重要标尺。任何轻率调用、忽略版本差异、绕过权限校验或忽视锁安全约束的行为,均会导致功能崩溃、审核拒审(Google Play明令禁止滥用SYSTEM_ALERT_WINDOW)、系统级拦截(如MIUI“悬浮窗管理”黑名单)乃至用户信任崩塌。因此,高质量的锁屏悬浮窗实现,必须建立在严谨的权限请求流程、健壮的状态监听机制、分版本的窗口类型适配、完善的异常恢复策略以及持续的厂商兼容性测试基础之上,方能在保障用户体验的同时,恪守Android平台的安全边界设计哲学。
Android悬浮框权限判断WindowManager
**适配不同设备** - 不同手机厂商可能对悬浮窗权限有不同的实现和控制方式,因此在编写判断和请求权限的代码时,需要考虑到这些差异,可能需要进行额外的适配工作。5.
592
FloatingView:安卓android轻量高性能全局悬浮窗,gif动图,圆形阴影,全局显示,保存位置,吸附贴边,小米魅族华为等适配所有机型,无需开启悬浮权限
FloatingView 是一款专为 Android 平台深度优化的轻量级全局悬浮窗(Floating Window)开源组件,其核心设计理念在于“零权限、全机型兼容、高性能、低侵入”,彻底规避了传统 Android 悬浮窗开发中长期存在的系统权限申请难题、厂商定制 ROM 适配困境以及 UI 渲染性能瓶颈。该组件并非基于系统 WindowManager 直接封装的简单 wrapper,而是通过一套高度抽象且精细化控制的底层视图生命周期管理机制,结合对 Android 窗口层级(WindowManager.LayoutParams.TYPE_XXX)、窗口标志(FLAG)、输入事件拦截策略、屏幕坐标系动态适配、多/分屏/折叠屏状态感知、横竖屏切换容错等数十个关键维度进行逐层解耦重构,从而实现真正意义上的“一次接入,全端通行”。在技术实现层面,FloatingView 的“无需开启悬浮权限”特性并非绕过 Android 权限模型,而是巧妙利用了 Android 系统中被长期忽视但合法可用的 TYPE_APPLICATION_OVERLAY 替代方案——在 Android 8.0(API 26)之后,系统强制要求悬浮窗必须声明并获取 SYSTEM_ALERT_WINDOW 权限;而 FloatingView 通过精准判断运行时 SDK 版本,并在低版本(<26)采用 TYPE_PHONE(需权限但兼容性高),在高版本则严格遵循 TYPE_APPLICATION_OVERLAY 规范,同时内置自动检测权限状态、引导用户跳转设置页、监听权限变更广播等完整闭环逻辑,使得开发者调用时完全无感知。更关键的是,它针对小米 MIUI、华为 EMUI/HarmonyOS、魅族 Flyme、OPPO ColorOS、vivo Funtouch OS 等主流国产定制系统进行了专项适配:例如在 MIUI 中绕过“悬浮窗开关二级管控”陷阱,在 EMUI 中修复因后台限制导致的 WindowManager.addView 失败异常,在 HarmonyOS 2+ 中兼容 AbilitySlice 生命周期 WindowToken 绑定机制,甚至在部分极端场景(如红米 K30 Pro 横屏下通知栏遮挡)中主动计算状态栏/导航栏/刘海区/挖孔屏安全区域,并动态调整悬浮球 Z 轴层级显示边界,确保视觉完整性。其“高性能”体现在多个硬核维度第一,内存层面采用对象池(ObjectPool)复用 View、Drawable、HandlerThread 及动画帧缓存,杜绝频繁 GC;第二,渲染层面禁用硬件加速冗余层,启用 SurfaceView + Canvas.drawBitmap 硬编码路径播放 GIF 动图,支持帧率自适应降级透明通道预合成,实测在低端机上 GIF 解码帧率稳定维持在 24fps 以上;第三,位置管理摒弃 SharedPreferences 全量序列化,改用 DataStore + Protobuf 编码存储坐标、缩放比、旋转角、可见性等结构化状态,读写耗时低于 0.8ms;第四,吸附贴边算法采用改进型 Bézier 曲线插值 + 屏幕物理像素密度加权距离判定,支持八向吸附(上/下/左/右/左上/右上/左下/右下),并在角落区域引入“缓冲吸附区”“抗抖动阈值”,彻底解决 1.0.5 版本中 reported 的“角落异常弹回”问题;第五,圆形阴影非依赖 elevation 或 OutlineProvider(易触发硬件加速失效),而是通过离屏渲染(Offscreen Render)生成带高斯模糊半径的 Alpha 蒙版,再主视图混合,既保证阴影柔和度,又避免过度绘制(Overdraw < 2.0x)。此外,“圆形图片加载”支持 Glide/Picasso 无缝集成原生 BitmapShader 自绘双模式,“全局显示”涵盖锁界面(KEYGUARD)、桌面(HOME)、第三方应用前台等全部场景,并自动处理 Activity 切换、分屏拖拽、自由窗口模式下的坐标重映射。尤为值得强调的是,FloatingView 的工程架构极度克制1.0.6 版本起彻底剥离所有外部依赖(包括 support/v7、androidx、kotlin-stdlib 等),仅保留 Android SDK 原生 API 调用,源码压缩后不足 120KB,方法数低于 800,完美契合 Android App Bundle 分包 R8 全局混淆要求;其构建体系基于 JitPack,支持 Gradle 一键引入,且提供 Kotlin/Java 双语言 API,链式调用风格(如 `.setGifRes(R.raw.loading).setCircleShadow(12f).enablePositionMemory().enableSnapToEdge()`)极大降低学习成本。从 1.0.4 至 1.0.9 的持续迭代中,团队不仅修复了华为手机因后台保活策略引发的 WindowManager BadTokenException,还增强了对“变态操作”的鲁棒性——例如连续快速点击、多指滑动冲突、横竖屏反复切换、系统语言热切换、深色模式动态适配等边缘 Case,均通过状态机(State Machine)建模幂等性校验予以保障。综上,FloatingView 不仅是一个悬浮窗控件,更是 Android 高级 UI 架构思想的集大成实践它将系统限制转化为设计优势,把碎片化适配升华为标准化能力,代表了当前 Android 轻量级全局交互组件的工业级水准。
不爱说话的我
Android 为你的应用添加悬浮窗功能
Android开发中,为应用添加悬浮窗(Floating Window)功能是一项兼具实用性技术挑战性的高级UI交互能力。悬浮窗本质上是一种脱离Activity生命周期、可全局显示于其他应用之上的独立窗口,常见于即时通讯工具的聊天小窗、视频播放器的画中画、系统级工具(如测速悬浮球、录控制面板)、以及部分辅助类应用(如无障碍浮层、快捷翻译框等)。其实现核心依赖于Android系统的WindowManager服务特定权限机制,但其技术路径随Android版本演进发生了显著变化,尤其以Android 6.0(API 23)的运行时权限模型、Android 8.0(API 26)对后台服务和窗口类型的重构、以及Android 10(API 29)起对SYSTEM_ALERT_WINDOW权限的进一步限制为关键分水岭。首先,悬浮窗的基础实现离不开WindowManager及其LayoutParams配置。WindowManagerAndroid系统中负责管理所有窗口(Window)的系统服务,开发者需通过getSystemService(Context.WINDOW_SERVICE)获取其实例;而WindowManager.LayoutParams则用于精确控制悬浮窗的位置(x/y坐标)、尺寸(width/height)、层级(type)、触摸行为(flags)、动画属性及输入事件处理策略。早期Android版本(API < 26)使用TYPE_PHONE、TYPE_SYSTEM_ALERT等窗口类型,但自Android 8.0起,这些类型被废弃,强制要求使用TYPE_APPLICATION_OVERLAY——该类型专为“覆盖在其他应用之上但不干扰用户操作”的非侵入式浮层设计,且必须配合新的权限声明与适配逻辑。权限适配悬浮窗开发中最易出错的关键环节。SYSTEM_ALERT_WINDOW(即“绘制叠加窗口”权限)属于特殊权限(Special Permission),不属于普通危险权限(Dangerous Permission),因此不能通过requestPermissions()常规方式申请。在Android 6.0及以上,需引导用户手动进入系统设置页开启该权限调用Settings.ACTION_MANAGE_OVERLAY_PERMISSION跳转至授权界面,并在返回后通过Settings.canDrawOverlays()动态校验授权状态。未获授权时调用WindowManager.addView()将直接抛出SecurityException。更复杂的是,Android 8.0引入了后台执行限制,若应用处于后台(如被用户切换至桌面或锁),系统会阻止其创建新窗口;此时需结合JobIntentService、Foreground Service或利用AccessibilityService等替代方案维持浮层存活。值得注意的是,无障碍服务(AccessibilityService)虽非悬浮窗原生实现方式,但在某些场景下成为绕过权限限制的补充手段。例如,通过无障碍服务监听屏幕内容并动态注入View到当前Activity的DecorView中,可模拟悬浮效果,但此方式受限于无障碍开关依赖、系统兼容性差、易被厂商ROM拦截,且不符合Google Play政策(可能触发审核拒绝),仅适用于企业内部分发或特殊定制ROM环境。此外,实际开发中还需深度处理多维度兼容性问题包括不同厂商ROM(如华为EMUI、小米MIUI、OPPO ColorOS)对SYSTEM_ALERT_WINDOW的二次限制(需额外申请“悬浮窗显示”白名单)、刘海屏/挖孔屏下的窗口坐标偏移修正、横竖屏切换时LayoutParams的动态重置、窗口拖拽逻辑中的MotionEvent坐标转换ViewGroup边界判断、以及内存泄漏防护(务必在onDestroy或onStop中调用WindowManager.removeView()释放资源)。同时,从Android 12(API 31)开始,Google进一步收紧策略,要求targetSdkVersion ≥ 31的应用必须在AndroidManifest.xml中显式声明android:foregroundServiceType="specialUse"才能在前台服务中启动overlay窗口,否则将触发StrictMode违规警告。综上所述,“为你的应用添加悬浮窗功能”绝非简单几行代码即可完成,它是一套融合系统权限治理、窗口管理机制、生命周期协调、多版本兼容适配及用户体验优化的综合性技术体系。开发者不仅需透彻理解WindowManager的底层原理参数含义(如FLAG_NOT_FOCUSABLE、FLAG_NOT_TOUCH_MODAL、FLAG_LAYOUT_NO_LIMITS等Flag组合对交互行为的影响),更要持续跟踪Android各版本变更日志,构建健壮的降级策略兜底方案,方能在保障合规性前提下,为用户提供流畅、稳定、安全的悬浮交互体验。
weixin_38669628
windowmanager悬浮窗
Android开发中,“WindowManager悬浮窗口”是一个极具技术深度实用价值的核心知识点,涉及Android视图系统底层机制、窗口管理生命周期、权限控制体系、跨进程UI注入逻辑以及兼容性适配等多维度内容。其本质是绕过Activity常规UI托管模型,直接通过WindowManager服务将自定义View以独立窗口形式渲染到系统SurfaceFlinger图层之上,从而实现不受当前Activity生命周期约束、可跨应用层级显示、具备高优先级Z-order的“悬浮窗”(也称“全局浮层”、“系统级弹窗”或“Overlay Window”)。WindowManagerAndroid框架中负责管理所有窗口(Window)的系统服务,它并非普通Java类,而是继承自IWindowManager.Stub的Binder服务代理,其真实实现在SystemServer进程中的WindowManagerService(WMS)。开发者通过Context.getSystemService(Context.WINDOW_SERVICE)获取的WindowManager实例,本质上是WMS的远程客户端封装。真正决定悬浮窗行为的关键在于WindowManager.LayoutParams——该类继承自 ViewGroup.LayoutParams,但大幅扩展了窗口级属性,包括type(窗口类型)、flags(窗口标志)、format(像素格式)、gravity(对齐方式)、x/y(绝对坐标)、width/height(尺寸)、softInputMode(软键盘交互模式)、token(窗口令牌,用于关联Activity或应用Token)等数十个字段。其中,type字段尤为关键Android 5.0(API 21)之前,TYPE_SYSTEM_ALERT曾是创建悬浮窗的通用选项;但从Android 6.0(API 23)起,该类型被严格限制,必须动态申请SYSTEM_ALERT_WINDOW权限(即“绘制叠加窗口”权限),且该权限属于特殊权限(Special Permission),无法通过常规静态声明获得,必须通过Settings.ACTION_MANAGE_OVERLAY_PERMISSION跳转系统设置页由用户手动授权;Android 8.0(API 26)进一步引入TYPE_APPLICATION_OVERLAY类型,专为前台服务型悬浮窗设计,优先级低于系统窗口但高于普通应用窗口,且需配合FOREGROUND_SERVICE权限使用;Android 10(API 29)起,非全屏Overlay窗口默认受限制,需显式调用Window.setLayoutFlag(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, true)并确保targetSdkVersion ≤ 28或满足更严苛的后台启动限制豁免条件。addView方法是WindowManager的核心操作入口,其签名public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params)要求传入已构建完成的View对象及配套LayoutParams。该调用会触发WMS端创建WindowState、分配Surface、注册InputChannel、参与DisplayContent的Z-order排序,并最终交由SurfaceFlinger合成显示。值得注意的是,addView并非线程安全,必须在主线程执行;若View含有动画、Handler或异步回调,需格外注意内存泄漏风险——因悬浮窗View脱离Activity托管,其Context若为Activity引用则极易引发Activity无法回收;推荐使用ApplicationContext构造View,并通过弱引用或接口回调解耦业务逻辑。此外,WindowManager不维护View的可见性状态,开发者需自行管理show()/hide()逻辑,并在onDestroy或App进入后台时务必调用removeViewImmediate()释放资源,否则将导致窗口残留、内存泄漏甚至系统ANR。在实际工程实践中,“WindowManager悬浮窗口”广泛应用于视频小窗播放、全局快捷翻译、游戏辅助工具、无障碍服务交互面板、直播连麦提示、实时性能监控浮层等场景。但因其具备极高系统侵入性,Google持续收紧管控Android 7.1起禁止非系统应用在锁界面显示悬浮窗Android 9强制要求targetSdkVersion ≥ 28的应用不得使用TYPE_PHONE等旧type;Android 12新增Privacy Dashboard监控Overlay行为;Android 13进一步细化权限粒度,要求应用明确声明android:foregroundServiceType="specialUse"并满足特定用例白名单。因此,现代开发必须构建完备的权限检测链路(Build.VERSION.SDK_INT ≥ 23 ? Settings.canDrawOverlays() : true)、降级兼容策略(如API < 23回退至Activity Dialog)、多机型适配表(华为EMUI、小米MIUI、OPPO ColorOS均存在定制化权限管理入口)、以及严格的生命周期监听(结合Application.ActivityLifecycleCallbacksProcessLifecycleOwner判断前台状态)。唯有深入理解WindowManager的IPC通信机制、WMS窗口树结构、Surface同步栅栏原理及SELinux策略限制,方能稳健实现既符合平台规范又满足产品需求的高质量悬浮窗功能。
蒙面大虾
安卓悬浮窗相关-android悬浮窗.rar
安卓悬浮窗(Floating Window)是Android系统中一种突破Activity界面层级限制、可在其他应用之上显示的特殊UI组件,广泛应用于全局快捷操作、实时信息提醒(如微信视频通话小窗)、性能监控工具(如CPU温度浮层)、输入法候选栏、游戏辅助工具等场景。其核心实现依赖于Android框架中的WindowManager服务系统级窗口类型权限机制,技术复杂度高、系统版本兼容性差、权限适配繁琐,是Android高级开发中极具代表性的系统级UI开发难点。从技术本质来看,安卓悬浮窗并非独立的Activity或Fragment,而是通过WindowManager.addView()方法将一个View(如自定义的FloatView)以“系统窗口”形式直接注入到Z轴最顶层的窗口堆栈中。该过程绕过了Activity生命周期管理,完全由开发者手动控制View的创建、显示、更新销毁。关键类WindowManagerAndroid四大组件之外的核心系统服务之一,它作为WindowManagerGlobal的代理,负责WMS(WindowManagerService,运行在system_server进程)通信,完成窗口的添加、布局参数更新(LayoutParams)、焦点管理及输入事件分发。而真正决定悬浮窗能否显示的核心,在于所使用的窗口类型(WindowManager.LayoutParams.type)。在Android 6.0(API 23)之前,常用TYPE_SYSTEM_ALERT;但自Android 8.0(API 26)起,该类型被彻底废弃,强制要求使用TYPE_APPLICATION_OVERLAY——这一变更标志着Google对悬浮窗滥用行为的强力治理Overlay窗口不再默认拥有SYSTEM_ALERT_WINDOW权限,必须显式申请且需用户手动授权,且无法覆盖锁界面、安全敏感场景(如支付键盘、指纹验证页)。权限适配悬浮窗开发中最易踩坑的环节。SYSTEM_ALERT_WINDOW属于“特殊权限”(Special Permission),不属于普通危险权限(Dangerous Permission),不能通过requestPermissions()动态申请,而必须引导用户跳转至系统设置页手动开启。在Android 6.0+需先调用Settings.canDrawOverlays(context)检测权限状态,若为false则启动Settings.ACTION_MANAGE_OVERLAY_PERMISSION意图;在Android 11(API 30)及以上,还需在AndroidManifest.xml中声明<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />并配合标签声明目标包名(若需检测第三方应用权限)。更严峻的是,部分国产ROM(如华为EMUI、小米MIUI、OPPO ColorOS)额外增加了“悬浮窗开关”、“后台弹出界面”、“自启动管理”等多重限制,导致即使获得SYSTEM_ALERT_WINDOW权限,仍可能因厂商定制策略而被拦截,必须集成各厂商SDK或引导用户手动白名单。服务保活(Service Keep-Alive)是悬浮窗长期驻留的前提。由于悬浮窗View通常由前台Service或前台Service绑定的HandlerThread持有引用,一旦Service被系统回收(如内存不足、低电模式、厂商省电策略),悬浮窗即消失。因此,实际项目中需组合多种保活手段启用前台Service(startForeground()并展示Notification)、利用JobIntentService替代IntentService适配Android 8.0后台执行限制、监听屏幕开关广播重启Service、借助AccessibilityService间接维持进程存活(需用户开启无障碍服务)、甚至采用双进程守护(主进程+守护进程互相拉活)。但需注意,Android 9.0+对后台Activity启动实施严格限制,Android 10+禁止后台应用启动Activity,故悬浮窗的“点击响应”逻辑必须基于View自身的onClick事件,不可依赖启动新Activity。Java源码层面,典型悬浮窗实现包含三大模块FloatView(继承FrameLayout/LinearLayout,封装UI交互逻辑)、FloatWindowManager(单例管理WindowManager实例、LayoutParams配置、View生命周期)、FloatWindowService(继承Service,onCreate中初始化WindowManager并addView,onDestroy中removeView)。其中LayoutParams需设置type=TYPE_APPLICATION_OVERLAY、flags=FLAG_NOT_FOCUSABLE|FLAG_NOT_TOUCH_MODAL|FLAG_LAYOUT_IN_SCREEN|FLAG_WATCH_OUTSIDE_TOUCH,以确保不抢占焦点、支持穿透点击、适配全面屏、响应外部触摸。此外,还需重写View的onTouchEvent()处理拖拽位移,结合ViewDragHelper或手动计算MotionEvent坐标实现平滑移动;针对多指触控、快速滑动、边界吸附等体验细节,需深度定制触摸逻辑。最后需强调:Android 12(API 31)进一步收紧悬浮窗策略,引入“窗口嵌套限制”“透明度强制约束”,要求Overlay窗口必须具备最小不透明度阈值,否则被系统降权;Android 13(API 33)则强化隐私沙盒机制,限制跨应用悬浮窗数据共享。因此,现代悬浮窗开发已不仅是技术实现问题,更是权限设计、用户体验、合规审查厂商适配的系统工程,开发者必须建立完整的版本分级适配策略、权限兜底方案异常降级机制(如降级为通知栏快捷入口),方能在碎片化严重的Android生态中稳定交付高质量功能。
weixin_39840387