android11文件存储,Andorid11(30)/Android10(29)分区存储-应用间共享文件

android11文件存储,Andorid11(30)/Android10(29)分区存储-应用间共享文件_第1张图片

在Android11/Andorid10分区存储中,两个应用之间如何共享文件呢?比如说我的应用生成了一个jpeg图片,想分享到微信,该怎么搞?

有三种方案:

生成到公共目录下,通过File接口分享(微信支持)

生成到公共目录下,通过MediaStore接口进行分享(微信不支持)

生成到私有目录下,通过FileProvider进行分享(微信支持)

本篇代码比较多,比起废话,Show code更靠谱。

生成到公共目录下,File接口分享

对于Android10的分区存储不支持此方案,而Android11的分区存储是支持的。

源应用把内容写入到外部公共目录下,例子为asset下的文件复制到公共目录Pictures/下。

//通过File的方式分享

private fun fileShare(): String {

val file = File(

Environment.getExternalStoragePublicDirectory(

Environment.DIRECTORY_PICTURES

),

"friends_file.jpg"

)

if (!file.parentFile.exists()) {

file.mkdirs()

}

val fileOutputStream = FileOutputStream(file)

val inputStream = this.assets.open("friends.jpg")

val byteArray = ByteArray(1024)

try {

fileOutputStream.use { outputStream ->

inputStream.use { inputStream ->

while (true) {

val readLen = inputStream.read(byteArray)

if (readLen == -1) {

break

}

outputStream.write(byteArray, 0, readLen)

}

}

}

} catch (e: Throwable) {

Log.e("wfeii", "fileShare:$e")

}

return file.path

}

复制代码

目标应用获取外部公共目录文件,并读取内容到自己的外部存储中。

private fun dealFilePath(intent: Intent?) {

if (intent == null) {

return

}

val filePath = intent.getStringExtra(FILE_PATH)

if (filePath == null) {

return

}

val fileInputStream = FileInputStream(filePath)

val file = File(getExternalFilesDir(""), "mediaStore_file.jpg")

if (!file.parentFile.exists()) {

file.parentFile.createNewFile()

}

val fileOutputStream = FileOutputStream(file)

val byteArray = ByteArray(1024)

try {

fileInputStream.use { fileInputStream ->

fileOutputStream.use { fileOutputStream ->

while (true) {

val readLen = fileInputStream.read(byteArray)

if (readLen == -1) {

break

}

fileOutputStream.write(byteArray, 0, readLen)

}

}

}

} catch (t: Throwable) {

Log.e("wfeii", "dealFilePath:$t")

}

Toast.makeText(this, "复制成功", Toast.LENGTH_LONG).show()

}

复制代码

注意:目标应用需要有READ_EXTERNAL_STORAGE权限。

生成到公共目录下,通过MediaStore接口分享

源应用把内容写入到外部公共目录下,例子为asset下的文件写入到Image的公共目录下。

//通过MediaStore存储到公共目录再分享

private fun mediaStore(): String? {

val resolver = applicationContext.contentResolver

val contentValues = ContentValues().apply {

put(MediaStore.Images.Media.DISPLAY_NAME, "friends.jpg")

}

val uri = resolver

.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)

if (uri != null) {

val outputStream = resolver.openOutputStream(uri)

if (outputStream == null) {

return null

}

val inputStream = this.assets.open("friends.jpg")

val byteArray = ByteArray(1024)

try {

inputStream.use { input ->

outputStream.use { output ->

while (true) {

val readLen = input.read(byteArray)

if (readLen == -1) {

break

}

outputStream.write(byteArray, 0, readLen)

}

}

}

} catch (e: Throwable) {

Log.e("wfeii", "mediaStore e:$e")

}

}

Log.e("wfeii", "mediaStore:$uri")

return uri?.toString()

}

复制代码

目标应用获取外部公共目录文件,并读取内容到自己的外部存储中。

private fun dealMediaStoreUrl(intent: Intent?) {

if (intent == null) {

return

}

val mediaStoreUrl = intent.getStringExtra(MEDIA_STORE)

if (mediaStoreUrl == null) {

return

}

val readOnlyMode = "r"

val fileDescriptor =

contentResolver.openFileDescriptor(

Uri.parse(mediaStoreUrl),

readOnlyMode

)?.fileDescriptor

if (fileDescriptor == null) {

return

}

val fileInputStream: InputStream = FileInputStream(fileDescriptor)

val file = File(getExternalFilesDir(""), "mediaStore.jpg")

val fileOutputStream = FileOutputStream(file)

val byteArray = ByteArray(1024)

try {

fileInputStream.use { fileDescriptor ->

fileOutputStream.use { fileOutputStream ->

while (true) {

val readLen = fileInputStream.read(byteArray)

if (readLen == -1) {

break

}

fileOutputStream.write(byteArray, 0, readLen)

}

}

}

} catch (e: Throwable) {

Log.e("wfeii", "dealMediaStoreUrl:$e")

}

Toast.makeText(this, "复制成功", Toast.LENGTH_LONG).show()

}

复制代码

注意:目标应用需要有READ_EXTERNAL_STORAGE权限。

生成到私有目录下,通过FileProvider分享

目标应用在manifest中声明FileProvider

android:name="androidx.core.content.FileProvider"

android:authorities="com.kuaima.sharefileforandroid11.fileProvider"

android:exported="false"

android:grantUriPermissions="true">

android:name="android.support.FILE_PROVIDER_PATHS"

android:resource="@xml/file_provider_paths" />

复制代码

使用androidx时,android:name声明为"androidx.core.content.FileProvider"。未使用androidx时,android:name声明为"android.support.v4.content.FileProvider"。

android:authorities声明为域名为${yourApplicationId}+FileProvider。

android:exported="false" 不能修改为true,为true时就会抛出SecurityException("Provider must not be exported")。

android:grantUriPermissions="true" 不能修改为false,为false时会抛出SecurityException("Provider must grant uri permissions")。

meta-data中android:name声明为"android.support.FILE_PROVIDER_PATHS"。

meta-data中android:resource用于声明那些目录可以通过FileProvider访问。

定义FileProvider可以访问的目录。在file_provider_paths.xml中我们定义如下:

name="my_images"

path="images/" />

复制代码

external-files-path指定共享的目录

name表示共享的url的path

path指定的是共享目录下那个文件夹

下表是url,路径直接的对应关系:

android11文件存储,Andorid11(30)/Android10(29)分区存储-应用间共享文件_第2张图片

根据File获取url并写入内容

private fun fileProviderShare(): String? {

val imagePath = File(this.getExternalFilesDir(null), "images")!!

val file = File(imagePath, "friends_file_provider.jpg")

if (!file.parentFile.exists()) {

file.parentFile.mkdirs()

}

//更具获取Url

val contentUri: Uri =

FileProvider.getUriForFile(this, "com.kuaima.sharefileforandroid11.fileProvider", file)

val fileOutputStream = contentResolver.openOutputStream(contentUri)

if (fileOutputStream == null) {

return null

}

val inputStream = this.assets.open("friends.jpg")

val byteArray = ByteArray(1024)

try {

fileOutputStream.use { outputStream ->

inputStream.use { inputStream ->

while (true) {

val readLen = inputStream.read(byteArray)

if (readLen == -1) {

break

}

outputStream.write(byteArray, 0, readLen)

}

}

}

} catch (e: Throwable) {

Log.e("wfeii", "fileProviderShare e:$e")

}

Log.e("wfeii", "fileProviderShare:$contentUri")

return contentUri.toString()

}

复制代码

授权临时权限

grantUriPermission(

"com.kuaima.destinationapp",

Uri.parse(fileProviderPath),

Intent.FLAG_GRANT_READ_URI_PERMISSION

)

复制代码

目标应用读取文件

private fun dealFileProviderPath(intent: Intent?) {

if (intent == null) {

return

}

val fileProviderUri = intent.getStringExtra(FILE_PROVIDER_URI)

if (fileProviderUri == null) {

return

}

Log.e("wfeii", "dealFileProviderPath fileProviderUri:$fileProviderUri")

val readOnlyMode = "r"

val fileDescriptor =

contentResolver.openFileDescriptor(

Uri.parse(fileProviderUri), readOnlyMode

)?.fileDescriptor

if (fileDescriptor == null) {

return

}

val inputStream = FileInputStream(fileDescriptor)

val file = File(getExternalFilesDir(""), "file_provider_path.jpg")

val fileOutputStream = FileOutputStream(file)

val byteArray = ByteArray(1024)

try {

inputStream.use { fileDescriptor ->

fileOutputStream.use { fileOutputStream ->

while (true) {

val readLen = inputStream.read(byteArray)

if (readLen == -1) {

break

}

fileOutputStream.write(byteArray, 0, readLen)

}

}

}

} catch (t: Throwable) {

Log.e("wfeii", "t")

}

Toast.makeText(this, "复制成功", Toast.LENGTH_LONG).show()

}

复制代码

在分区存储中,这些就是应用间共享文件的常用的三种方案。

参考文档

限于个人水平,有错误请指出,大家共同学习进步!

扫码关注公众号,查看更多内容。

你可能感兴趣的:(android11文件存储)