Android 窗口镜像(mirror)常规的应用场景有投影、录屏等,本文介绍窗口镜像的使用方法和实现机制
mirror相关的方法有些是hide的,所以使用前需要评估当前app是否有权限
1. 投屏使用方法
官方文档:developer.android.google.cn/media/grow/…
投影接口:MediaProjection
标准用法:
- 通过用户授权获取一个一次性的
MediaProjection
kotlin 代码解读复制代码val mediaProjectionManager = getSystemService(MediaProjectionManager::class.java)
var mediaProjection : MediaProjection
val startMediaProjection = registerForActivityResult(
StartActivityForResult()
) { result ->
if (result.resultCode == RESULT_OK) {
mediaProjection = mediaProjectionManager
.getMediaProjection(result.resultCode, result.data!!)
}
}
startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent())
- 启动一个前台服务
xml 代码解读复制代码<manifest ...>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<application ...>
<service
android:name=".MyMediaProjectionService"
android:foregroundServiceType="mediaProjection"
android:exported="false">
service>
application>
manifest>
- 创建一个
VirtualDisplay
接收投影数据
kotlin 代码解读复制代码virtualDisplay = mediaProjection.createVirtualDisplay(
"ScreenCapture",
width,
height,
screenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
surface,
null, null)
其中的
Surface
可以来自SurfaceView
(实时显示)、ImageReader
(截图)、MediaRecorder
(录屏)或SurfaceTexture
(实时显示、编辑),根据具体使用场景决定
2. 核心实现
核心实现涉及到一个类SurfaceControl
,SurfaceControl
代表一个绘制图层,是 SurfaceFlinger
中 Layer
在Java层的一种封装,其中也包含了 Layer
的各种属性和操作接口。
一般的窗口,如Activity
、Dialog
以及通过WindowManager.addView()
添加的窗口至少包含一个SurfaceControl
,这样窗口才能被绘制出来。
核心方法
java 代码解读复制代码// android.view.SurfaceControl
public static SurfaceControl mirrorSurface(SurfaceControl mirrorOf)
投屏或者镜像的核心实现是:通过SurfaceControl
中的hide方法mirrorSurface()
,把数据源SurfaceControl
“复制”(mirror)一份,放(reparent)到目标SurfaceControl
中。
两个操作对应到SurfaceFlinger
中:
- mirror操作是建一个新的
Layer
,并记录mirror源Layer
的信息,到合成时直接使用源Layer
的内容 - reparent操作是将两个
Layer
设置从属关系,比如位置、透明度等属性从属Layer
会受到主Layer
的影响和限制
3. 扩展应用
使用mirror功能,需要源/从属SurfaceControl
,也需要目标/主SurfaceControl
,所以需要知道怎么获取SurfaceControl
。
3.1 获取SurfaceControl
对于Window来说,可以通过View
中的方法AttachedSurfaceControl getRootSurfaceControl()
获取到接口AttachedSurfaceControl
,通过其中方法buildReparentTransaction(SurfaceControl child)
绑定mirror内容。这个接口中使用的是Window的根SurfaceControl
。
另外也可以通过在布局中添加SurfaceView
,然后使用SurfaceControl getSurfaceControl()
获取SurfaceView
对应的SurfaceControl
。这也侧面说明SurfaceView
与窗口中其他部分使用了不同的Layer
进行合成。
获取到SurfaceControl
就可以进行后续的操作。比如可以把一个SurfaceView
的内容投影到另一个SurfaceView
里,投影时可以添加各种常规的转换,比如缩放、旋转、位移和透明度等。
这些转换和reparent都属于SurfaceControl.Transaction
的方法,对SurfaceControl
的操作都需要通过SurfaceControl.Transaction
进行,系统的窗口动画很多都是通过这个类实现的。
3.2 壁纸预览
AOSP里mirror的一个使用场景是壁纸预览,有两个方式可以获取壁纸:
- 任意壁纸:
SurfaceControl IWallpaperEngine.mirrorSurfaceControl()
,使用示例可以参考packages/apps/WallpaperPicker2/src/com/android/wallpaper/util/WallpaperConnection.java
,简单来说是需要绑定一个WallpaperService
,然后通过回调中的IWallpaperEngine
获取壁纸内容的SurfaceControl
- 当前壁纸: 通过
SurfaceControl WindowManagerGlobal.getInstance().mirrorWallpaperSurface(0 /*display id*/)
获取某个显示屏上的系统壁纸SurfaceControl
获取到壁纸内容SurfaceControl
后,可以通过3.1中的方式把壁纸经过转换显示到窗口或者SurfaceView
中。
如果只是希望用壁纸来做窗口背景,可以给窗口加上
FLAG_SHOW_WALLPAPER
,或者主题里添加showWallpaper
。上面列出的方式适合更复杂的使用场景。
3.3 放大镜
AOSP里另一个使用场景是无障碍中的放大镜功能,其中涉及到对SurfaceControl
进行转换
- 文档参考:source.android.google.cn/docs/core/d…
- 代码参考:
frameworks/base/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
评论记录:
回复评论: