【Android开发|Android10及以上访问公有目录】公有目录指的是系统根目录下的Download、DCIM、Documents、Screenshots、Music等文件夹。
本文说的访问是指:列举出某一公有目录下的所有文件、删除某个文件、保存文件到某个公有目录等意思。
Android10以下按原来的File(path)方式,本文不表。
Android10及以上可以使用MediaStore
访问公有目录。如果我们在公有目录下只操作自己应用生成的文件,是不需要申请文件读写权限的。另外,公有目录下的文件在APP卸载后不会被删除,也能被其他应用访问。(其他应用只能read,不能write)
下面是关于Android10及以上存储权限的说明:(官方说明:https://developer.android.com/training/data-storage?hl=zh-cn)
-
WRITE_EXTERNAL_STORAGE
在Android11已过时,不要再去申请这个。
- Android10及以上是新版的权限
MANAGE_EXTERNAL_STORAGE
。但是如果你要在Google Play上架,听人谣传审核是比较严格的:如果你的APP不是文件管理器之类的应用,那么一般不给过。代码里Android Stuido也提示说Most apps are not allowed to use MANAGE_EXTERNAL_STORAGE
。
-
READ_EXTERNAL_STORAGE
读取权限。如果你需要读取别的应用保存在公有目录下的文件,则需要动态申请这个权限;如果读取的是自己应用保存的文件,即便是公有目录,也不需要申请这个权限。
私有目录文件复制到公有目录
/**
* 复制私有目录的文件到公有Download目录
* @param context 上下文
* @param oldPath 私有目录的文件路径
* @param targetDirName 公有目录下的目标文件夹名字。比如传test,则会复制到Download/test目录下。另外如果Download目录下test文件夹不存在,会自动创建。
* @return 公有目录的uri,为空则代表复制失败
*/
@RequiresApi(Build.VERSION_CODES.Q)
fun copyFileToDownloadDir(context: Context,oldPath: String,targetDirName:String): Uri? {try {val oldFile = File(oldPath)
//设置目标文件的信息
val values = ContentValues()
values.put(MediaStore.Images.Media.DESCRIPTION, "This is a file.")
values.put(MediaStore.Files.FileColumns.DISPLAY_NAME, oldFile.name)
values.put(MediaStore.Files.FileColumns.TITLE, oldFile.name)
values.put(MediaStore.Files.FileColumns.MIME_TYPE, getMimeType(oldPath))
val relativePath = Environment.DIRECTORY_DOWNLOADS + File.separator + targetDirName
values.put(MediaStore.Images.Media.RELATIVE_PATH, relativePath)
val downloadUri = MediaStore.Downloads.EXTERNAL_CONTENT_URI
val resolver = context.contentResolver
val insertUri = resolver.insert(downloadUri, values)
if (insertUri != null) {val fos = resolver.openOutputStream(insertUri)
if (fos != null) {val fis = FileInputStream(oldFile)
fis.copyTo(fos)
fis.close()
return insertUri
}
}
} catch (e: Exception) {e.printStackTrace()
}
return null
}fun getMimeType(path: String?): String {var mime = "*/*"
path ?: return mime
val mmr = MediaMetadataRetriever()
try {mmr.setDataSource(path)
mime = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_MIMETYPE) ?: mime
} catch (e: Exception) {e.printStackTrace()
} finally {mmr.release()
}
return mime
}
查询公有目录下的文件
/**
* 获取公有Download目录下的文件
* @param dirName Download目录下的下一级文件夹的名字
* @return 文件的uri集合
*/
@RequiresApi(Build.VERSION_CODES.Q)
fun listFiles(context: Context, dirName: String): List {val resultList = ArrayList()
try {val resolver = context.contentResolver
val downloadUri = MediaStore.Downloads.EXTERNAL_CONTENT_URI
val resultCursor = resolver?.query(
downloadUri,
null,
MediaStore.Files.FileColumns.BUCKET_DISPLAY_NAME + "=?",
arrayOf(dirName),
null
)
if (resultCursor != null) {val fileIdIndex = resultCursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)
//val fileNameIndex = resultCursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME)
while (resultCursor.moveToNext()) {val fileId = resultCursor.getLong(fileIdIndex)
//文件名
//val fileName = resultCursor.getString(fileNameIndex)
val pathUri = downloadUri.buildUpon().appendPath("$fileId").build()
resultList.add(pathUri)
}
resultCursor.close()
}
} catch (e: Exception) {e.printStackTrace()
}
return resultList
}
删除公有目录下的文件
/**
* 删除公有目录的文件。(自己应用创建的文件才有权限删除)
*/
@RequiresApi(Build.VERSION_CODES.Q)
fun deleteFile(context: Context, fileUri: Uri) {try {context.contentResolver?.delete(fileUri, null, null)
} catch (e: Exception) {e.printStackTrace()
}
}
公有目录文件复制到私有目录
/**
* 公有目录文件复制到私有目录
* @param fileUri 公有目录文件的uri
* @param privatePath 私有目录的路径
*/
fun copyToPrivateDir(context: Context,fileUri:Uri,privatePath:String){try {val fis =FileInputStream(context.contentResolver.openFileDescriptor(fileUri,"r")?.fileDescriptor)
fis.copyTo(FileOutputStream(privatePath))
fis.close()
}catch (e:Exception){e.printStackTrace()
}
}
上面用到的api,导包如下:
import android.content.ContentValues
import android.content.Context
import android.media.MediaMetadataRetriever
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import androidx.annotation.RequiresApi
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.lang.Exception
推荐阅读
- Android|Jetpack 系列之替换代替ButterKnife的全新视图工具 ViewBinding的使用详解
- 详细实例教程!集成华为虚假用户检测,防范虚假恶意流量
- Android|44岁万达女高管跳楼(摧毁一个中年人有多容易!)
- android|本来打算年前离职,但是昨天的一次 Android 面试让我心好累……我迷茫了
- android|android ffmpeg rtmp,Android利用ffmpeg产生推流
- 什么是原创(独立完成就是原创吗?)
- android|壁纸服务的启动过程
- 最全的 Android 之 Dialog
- Android|简直离谱!字节面了5轮,4轮都让我分析 Android Framework 层的源码