首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >以编程方式截图Android 13

以编程方式截图Android 13
EN

Stack Overflow用户
提问于 2022-10-18 03:11:43
回答 1查看 99关注 0票数 0

我有下面的代码,它在Android 12和更低的版本上运行得非常好,但出于一个奇怪的原因,Android 13大约95%的时间都会使用“黑色”屏幕截图。

代码语言:javascript
复制
fun setVirtualDisplay() {
    mImageReader = ImageReader.newInstance(
        deviceScreenUtils.getWidth(),
        deviceScreenUtils.getHeight(),
        PixelFormat.RGBA_8888,
        2
    )

    mImageReader?.let {
        val flags =
            DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY or DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC

        try {
            mVirtualDisplay = mMediaProjection?.createVirtualDisplay(
                "screen-mirror", deviceScreenUtils.getWidth(), deviceScreenUtils.getHeight(),
                deviceScreenUtils.getDensity(), flags, it.surface, null, null
            )
        } catch (e: Throwable) {
            Log.i(TAG, "Media Projection not longer available...")
            mMediaProjectionIntent = null
            mImageReader = null
        }
    }
}

fun takeScreenshot() {
    Log.i(TAG, "Taking screenshot...")
    val handler = Handler(Looper.getMainLooper())
    mImageReader?.setOnImageAvailableListener({ imageReader ->
        val image = imageReader.acquireLatestImage()
        Log.i(TAG, "Acquiring image...")
        if (image != null) {
            val planes = image.planes
            val buffer = planes[0].buffer
            val pixelStride = planes[0].pixelStride
            val rowStride = planes[0].rowStride
            val rowPadding: Int = rowStride - pixelStride * deviceScreenUtils.getWidthPixels()
            val bitmap = Bitmap.createBitmap(
                deviceScreenUtils.getWidth() + (rowPadding.toFloat() / pixelStride.toFloat()).toInt(),
                deviceScreenUtils.getHeight(),
                Bitmap.Config.ARGB_8888
            )
            bitmap.copyPixelsFromBuffer(buffer)
            image.close()

            fileUtil.saveImage(bitmap)?.let {
                lastScreenshot.onNext(it)
            }
        }

        imageReader.setOnImageAvailableListener(null, handler)
        releaseBuffer()
    }, handler)
}

这个特定的功能有什么变化吗?或者,我的代码中有什么问题,而且它一直在神奇地处理以前的版本?

EN

回答 1

Stack Overflow用户

发布于 2022-11-24 02:16:05

在我的测试和观察中,当在Android13上使用ImageReader和媒体投影时,有两个问题

首先,setOnImageAvailableListener的回调结果有时会返回带空像素的缓冲区。因此,我们只需等待下一个图像,直到得到一个非空的位图。

代码语言:javascript
复制
imageReader.setOnImageAvailableListener({ imageReader ->
    val image = imageReader.acquireLatestImage()
    // ... get buffer here
    val bitmap = Bitmap.createBitmap(screenWidth + rowPadding / pixelStride, screenHeight, Bitmap.Config.ARGB_8888)
    bitmap.copyPixelsFromBuffer(buffer)

    // IMPORTANT!
    val isEmptyBitmap = bitmap.isEmptyBitmap()
    if (isEmptyBitmap) {
        // don't stop the listener and let the imageReader continue to run so that we can get next round of image buffer
    } else {
        // save bitmap to file
    }

    }, Handler(Looper.getMainLooper()))

isEmptyBitmap()只是位图的一个扩展:

代码语言:javascript
复制
fun Bitmap.isEmptyBitmap(): Boolean {
    val emptyBitmap = Bitmap.createBitmap(width, height, config)
    return this.sameAs(emptyBitmap)
}

第二个问题是,OnImageAvailableListener有时甚至不回调!在本例中,我设置了一个等待结果的超时,当超时时,从同一个VirtualDisplay实例重新创建MediaProjection对象,它就可以工作了。

我使用的是kotlin协同器,因此代码片段看起来可能如下:

代码语言:javascript
复制
retry(10) {
    val imageVirtualDisplay = createVirtualDisplay(...)
    try {
        withTimeout(100) {
            // awaitImageAvailable will suspend here to get the non-empty bitmap
            val bitmap = imageReader.awaitImageAvailable(screenWidth, screenHeight)
            // save bitmap to file
        }
    } finally {
        Timber.d("screenshot imageVirtualDisplay release")
        imageVirtualDisplay?.release()
    }
}

awaitImageAvailable的实现

代码语言:javascript
复制
suspend fun ImageReader.awaitImageAvailable(screenWidth: Int, screenHeight: Int): Bitmap =
suspendCancellableCoroutine { cont ->
    setOnImageAvailableListener({ imageReader ->

        val image = imageReader.acquireLatestImage() ?: throw Error("get screen image result failed")

        val planes = image.planes
        val buffer = planes[0].buffer

        val pixelStride = planes[0].pixelStride
        val rowStride = planes[0].rowStride
        val rowPadding: Int = rowStride - pixelStride * screenWidth
        var bitmap =
            Bitmap.createBitmap(screenWidth + rowPadding / pixelStride, screenHeight, Bitmap.Config.ARGB_8888)
        bitmap.copyPixelsFromBuffer(buffer)
        bitmap = Bitmap.createBitmap(bitmap, 0, 0, screenWidth, screenHeight)
        image.close()

        if (!bitmap.isEmptyBitmap()) {
            Timber.d("image not empty")
            setOnImageAvailableListener(null, null)
            cont.resume(bitmap)
        } else {
            Timber.w("image empty")
        }
    }, Handler(Looper.getMainLooper()))

    cont.invokeOnCancellation { setOnImageAvailableListener(null, null) }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74105274

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档