【问题标题】:Why am I getting being unable to save the bitmap as a picture that I generate from a Fragment in Android?为什么我无法将位图保存为从 Android 中的 Fragment 生成的图片?
【发布时间】:2019-11-19 19:15:12
【问题描述】:

我有一个功能,可以在我的活动中从我的片段中拍照。 以下是我在片段中请求权限的方式:

class WritingArFragment: ArFragment() {
    override fun getAdditionalPermissions(): Array<String?> {
        val additionalPermissions = super.getAdditionalPermissions()
        val permissionLength = additionalPermissions?.size ?: 0
        val permissions = arrayOfNulls<String>(permissionLength + 1)
        permissions[0] = Manifest.permission.WRITE_EXTERNAL_STORAGE
        if (permissionLength > 0) {
            System.arraycopy(additionalPermissions!!, 0, permissions, 1, additionalPermissions.size)
        }
        return permissions
    }
}

然后片段显示相机正在查看的内容以在 AR 环境中添加 3d 模型。 这是我保存图片的方法:

private fun generateFilename(): String {
    val date = SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(Date())
    return Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES).toString() + File.separator + "Sceneform/" + date + "_screenshot.jpg"
}

@Throws(IOException::class)
private fun saveBitmapToDisk(bitmap: Bitmap, filename: String) {
val out = File(filename)
if (!out.parentFile.exists()) {
    out.parentFile.mkdirs()
}
try {
    FileOutputStream(filename).use { outputStream ->
        ByteArrayOutputStream().use { outputData ->
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputData)
            outputData.writeTo(outputStream)
            outputStream.flush()
            outputStream.close()
        }
    }
} catch (ex: IOException) {
    throw IOException("Failed to save bitmap to disk", ex)
}

}

私人乐趣 takePicture() { val 文件名 = generateFilename() val view = arFragment.arSceneView

// Create a bitmap the size of the scene view.
val bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
        Bitmap.Config.ARGB_8888)

// Create a handler thread to offload the processing of the image.
val handlerThread = HandlerThread("PixelCopier")
handlerThread.start()
// Make the request to copy.
PixelCopy.request(view, bitmap, { copyResult ->
    if (copyResult == PixelCopy.SUCCESS) {
        try {
            saveBitmapToDisk(bitmap, filename)
            val snackbar = Snackbar.make(findViewById(android.R.id.content),
                    "Photo saved", Snackbar.LENGTH_LONG)
            snackbar.setAction("Open in Photos") { v ->
                val photoFile = File(filename)

                val photoURI = FileProvider.getUriForFile(this.getApplication(),
                        "$packageName.example.secondar.name.provider",
                        photoFile)
                val intent = Intent(Intent.ACTION_VIEW, photoURI)
                intent.setDataAndType(photoURI, "image/*")
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                startActivity(intent)
            }
            snackbar.show()
        } catch (e: IOException) {
            e.printStackTrace()
            val toast = Toast.makeText(this.getApplication(), e.toString(),
                    Toast.LENGTH_LONG)
            toast.show()
        }
    } else {
        val toast = Toast.makeText(this.getApplication(),
                "Failed to copyPixels: $copyResult", Toast.LENGTH_LONG)
        toast.show()
    }
    handlerThread.quitSafely()
}, Handler(handlerThread.looper))

}

最后这是我的清单:

    <uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.camera.ar" android:required="true"/>

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:usesCleartextTraffic="true"
    android:theme="@style/AppTheme">
    <meta-data android:name="com.google.ar.core" android:value="required" />
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.example.secondar.name.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/paths"/>
    </provider>
</application>

我不断进入我的日志

2019-11-19 14:05:04.503 5383-5553/com.example.secondar W/System.err: java.io.IOException: Failed to save bitmap to disk
2019-11-19 14:05:04.503 5383-5553/com.example.secondar W/System.err:     at com.example.secondar.MainActivity.saveBitmapToDisk(MainActivity.kt:238)
2019-11-19 14:05:04.504 5383-5553/com.example.secondar W/System.err:     at com.example.secondar.MainActivity.access$saveBitmapToDisk(MainActivity.kt:45)
2019-11-19 14:05:04.504 5383-5553/com.example.secondar W/System.err:     at com.example.secondar.MainActivity$takePicture$1.onPixelCopyFinished(MainActivity.kt:258)
2019-11-19 14:05:04.504 5383-5553/com.example.secondar W/System.err:     at android.view.PixelCopy$1.run(PixelCopy.java:191)
2019-11-19 14:05:04.504 5383-5553/com.example.secondar W/System.err:     at android.os.Handler.handleCallback(Handler.java:883)
2019-11-19 14:05:04.504 5383-5553/com.example.secondar W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:100)
2019-11-19 14:05:04.504 5383-5553/com.example.secondar W/System.err:     at android.os.Looper.loop(Looper.java:214)
2019-11-19 14:05:04.504 5383-5553/com.example.secondar W/System.err:     at android.os.HandlerThread.run(HandlerThread.java:67)
2019-11-19 14:05:04.504 5383-5553/com.example.secondar W/System.err: Caused by: java.io.FileNotFoundException: /storage/emulated/0/Pictures/Sceneform/20191119140504_screenshot.jpg: open failed: EACCES (Permission denied)
2019-11-19 14:05:04.505 5383-5553/com.example.secondar W/System.err:     at libcore.io.IoBridge.open(IoBridge.java:496)
2019-11-19 14:05:04.505 5383-5553/com.example.secondar W/System.err:     at java.io.FileOutputStream.<init>(FileOutputStream.java:235)
2019-11-19 14:05:04.505 5383-5553/com.example.secondar W/System.err:     at java.io.FileOutputStream.<init>(FileOutputStream.java:125)
2019-11-19 14:05:04.505 5383-5553/com.example.secondar W/System.err:     at com.example.secondar.MainActivity.saveBitmapToDisk(MainActivity.kt:229)
2019-11-19 14:05:04.505 5383-5553/com.example.secondar W/System.err:    ... 7 more
2019-11-19 14:05:04.505 5383-5553/com.example.secondar W/System.err: Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied)

我错过了什么? 我正在使用 Android 29、Java 1.8、Kotlin 和 AndroidX

提前致谢

问候

【问题讨论】:

  • 我没有看到你实际请求权限。
  • 我在 WritingArFragment 中,当 Fragment 加载时它会要求权限,我什至看到对话框要求权限
  • 这是我尝试使用的路径和文件名:/storage/emulated/0/Pictures/Sceneform/20191119132950_screenshot.jpg

标签: android kotlin androidx arcore sceneform


【解决方案1】:

我在尝试从我的 AVD 29 Pixel 上的外部存储读取数据时遇到了同样的问题,我已经请求并获得了权限。

查看Uriel Frankel提供的此答案:

Google 在 Android Q 上有一项新功能:用于外部存储的过滤视图。对此的快速解决方法是将此代码添加到 AndroidManifest.xml 文件中

<manifest ... >
    <!-- This attribute is "false" by default on apps targeting Android Q. -->
    <application android:requestLegacyExternalStorage="true" ... >
     ...
    </application>
</manifest>

Exception 'open failed: EACCES (Permission denied)' on Android

更新: 正如 cmets 中所讨论的,此处是指向有关即将发生的更改的相关文档的链接。

Developers Documentation: Manage scoped external storage access

在来自Nikos Hidalgo 的这个 SO-answer 中找到。

java.io.FileNotFoundException: /sdcard/testwrite.txt: open failed: EACCES (Permission denied)

【讨论】:

  • 这行得通,但是当谷歌为任何以 Android Q 为目标并试图保存位图的应用程序删除此属性时会发生什么?有没有更好的解决方案
  • 你是对的,下一个版本中会出现变化。 Nikos Hidalgo 提供的答案具有相同的临时修复程序,但也链接到相关文档以了解即将发生的事情 Documentation: Manage scoped external storage access SO-Answer
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多