Android文件存储 #

一、存储概述 #

Android提供了多种数据存储方式,文件存储是其中最基本的一种。根据存储位置,可以分为内部存储和外部存储。

1.1 存储分类 #

类型 说明 特点
内部存储 应用私有目录 不需要权限,卸载时删除
外部存储 共享存储目录 需要权限,卸载可能保留
应用专属外部存储 外部存储中的应用私有目录 不需要权限,卸载时删除

1.2 存储路径 #

text
内部存储:
/data/data/<package_name>/files/
/data/data/<package_name>/cache/

外部存储:
/storage/emulated/0/

应用专属外部存储:
/storage/emulated/0/Android/data/<package_name>/files/
/storage/emulated/0/Android/data/<package_name>/cache/

二、内部存储 #

内部存储是应用私有的存储空间,其他应用无法访问,不需要权限。

2.1 写入文件 #

kotlin
fun writeToFile(fileName: String, content: String) {
    try {
        val file = File(filesDir, fileName)
        file.writeText(content)
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

// 或使用openFileOutput
fun writeToFile2(fileName: String, content: String) {
    try {
        openFileOutput(fileName, Context.MODE_PRIVATE).use { output ->
            output.write(content.toByteArray())
        }
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

2.2 读取文件 #

kotlin
fun readFromFile(fileName: String): String {
    return try {
        val file = File(filesDir, fileName)
        file.readText()
    } catch (e: IOException) {
        e.printStackTrace()
        ""
    }
}

// 或使用openFileInput
fun readFromFile2(fileName: String): String {
    return try {
        openFileInput(fileName).use { input ->
            input.bufferedReader().readText()
        }
    } catch (e: IOException) {
        e.printStackTrace()
        ""
    }
}

2.3 缓存文件 #

kotlin
// 写入缓存
fun writeToCache(fileName: String, content: String) {
    try {
        val file = File(cacheDir, fileName)
        file.writeText(content)
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

// 读取缓存
fun readFromCache(fileName: String): String {
    return try {
        val file = File(cacheDir, fileName)
        file.readText()
    } catch (e: IOException) {
        e.printStackTrace()
        ""
    }
}

// 清除缓存
fun clearCache() {
    cacheDir.deleteRecursively()
}

2.4 文件操作 #

kotlin
// 获取文件列表
val files = fileList()

// 删除文件
deleteFile(fileName)

// 检查文件是否存在
val exists = File(filesDir, fileName).exists()

// 获取文件大小
val size = File(filesDir, fileName).length()

三、外部存储 #

3.1 存储权限 #

xml
<!-- 读取外部存储 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<!-- 写入外部存储(Android 10以下) -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<!-- Android 11+ 管理所有文件 -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

3.2 检查存储状态 #

kotlin
fun isExternalStorageWritable(): Boolean {
    return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
}

fun isExternalStorageReadable(): Boolean {
    val state = Environment.getExternalStorageState()
    return state == Environment.MEDIA_MOUNTED || state == Environment.MEDIA_MOUNTED_READ_ONLY
}

3.3 应用专属外部存储 #

不需要权限,卸载应用时自动删除。

kotlin
// 获取外部文件目录
val externalFilesDir = getExternalFilesDir(null)  // /Android/data/<package>/files/
val externalCacheDir = externalCacheDir  // /Android/data/<package>/cache/

// 获取特定类型目录
val picturesDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
val downloadsDir = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
val documentsDir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
val musicDir = getExternalFilesDir(Environment.DIRECTORY_MUSIC)
val moviesDir = getExternalFilesDir(Environment.DIRECTORY_MOVIES)

// 写入文件
fun writeToExternalFile(fileName: String, content: String) {
    if (!isExternalStorageWritable()) return
    
    val file = File(getExternalFilesDir(null), fileName)
    file.writeText(content)
}

// 读取文件
fun readFromExternalFile(fileName: String): String {
    if (!isExternalStorageReadable()) return ""
    
    val file = File(getExternalFilesDir(null), fileName)
    return file.readText()
}

3.4 公共外部存储 #

需要权限,卸载应用后文件保留。

kotlin
// Android 10及以上使用MediaStore
fun saveImageToGallery(context: Context, bitmap: Bitmap) {
    val contentValues = ContentValues().apply {
        put(MediaStore.Images.Media.DISPLAY_NAME, "image_${System.currentTimeMillis()}.jpg")
        put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
        put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
    }
    
    val uri = context.contentResolver.insert(
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        contentValues
    )
    
    uri?.let {
        context.contentResolver.openOutputStream(it).use { output ->
            bitmap.compress(Bitmap.CompressFormat.JPEG, 90, output)
        }
    }
}

// Android 9及以下
fun saveImageToGalleryLegacy(context: Context, bitmap: Bitmap) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
        val picturesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
        val file = File(picturesDir, "image_${System.currentTimeMillis()}.jpg")
        file.outputStream().use { output ->
            bitmap.compress(Bitmap.CompressFormat.JPEG, 90, output)
        }
        // 通知媒体库更新
        MediaScannerConnection.scanFile(
            context,
            arrayOf(file.absolutePath),
            arrayOf("image/jpeg"),
            null
        )
    }
}

四、动态权限请求 #

4.1 检查和请求权限 #

kotlin
private val requestPermissionLauncher = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { isGranted ->
    if (isGranted) {
        // 权限已授予
        saveFile()
    } else {
        // 权限被拒绝
        Toast.makeText(this, "需要存储权限", Toast.LENGTH_SHORT).show()
    }
}

fun checkAndRequestPermission() {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
            ) == PackageManager.PERMISSION_GRANTED
        ) {
            saveFile()
        } else {
            requestPermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
        }
    } else {
        // Android 10+ 不需要WRITE_EXTERNAL_STORAGE权限
        saveFile()
    }
}

4.2 Android 11+ 管理所有文件 #

kotlin
fun checkManageExternalStoragePermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        if (!Environment.isExternalStorageManager()) {
            val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
            intent.data = Uri.parse("package:$packageName")
            startActivity(intent)
        }
    }
}

五、文件选择器 #

5.1 使用Storage Access Framework #

kotlin
private val openDocumentLauncher = registerForActivityResult(
    ActivityResultContracts.OpenDocument()
) { uri ->
    uri?.let {
        readFileFromUri(it)
    }
}

fun openFile() {
    openDocumentLauncher.launch(arrayOf("text/*", "application/pdf"))
}

private val createDocumentLauncher = registerForActivityResult(
    ActivityResultContracts.CreateDocument("text/plain")
) { uri ->
    uri?.let {
        writeToFile(it, "Hello World")
    }
}

fun createFile() {
    createDocumentLauncher.launch("new_file.txt")
}

5.2 读取选择的文件 #

kotlin
fun readFileFromUri(uri: Uri): String {
    return contentResolver.openInputStream(uri)?.use { input ->
        input.bufferedReader().readText()
    } ?: ""
}

fun writeToFile(uri: Uri, content: String) {
    contentResolver.openOutputStream(uri)?.use { output ->
        output.write(content.toByteArray())
    }
}

六、文件复制与移动 #

kotlin
fun copyFile(source: File, dest: File) {
    source.inputStream().use { input ->
        dest.outputStream().use { output ->
            input.copyTo(output)
        }
    }
}

fun moveFile(source: File, dest: File): Boolean {
    return if (source.renameTo(dest)) {
        true
    } else {
        copyFile(source, dest)
        source.delete()
    }
}

七、最佳实践 #

7.1 使用协程处理文件操作 #

kotlin
suspend fun readFileAsync(fileName: String): String = withContext(Dispatchers.IO) {
    val file = File(filesDir, fileName)
    file.readText()
}

suspend fun writeFileAsync(fileName: String, content: String) = withContext(Dispatchers.IO) {
    val file = File(filesDir, fileName)
    file.writeText(content)
}

7.2 使用FileProvider分享文件 #

xml
<!-- AndroidManifest.xml -->
<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>
xml
<!-- res/xml/file_paths.xml -->
<paths>
    <files-path name="files" path="." />
    <cache-path name="cache" path="." />
    <external-files-path name="external_files" path="." />
</paths>
kotlin
fun shareFile(file: File) {
    val uri = FileProvider.getUriForFile(
        this,
        "${packageName}.fileprovider",
        file
    )
    
    val intent = Intent(Intent.ACTION_SEND).apply {
        type = "image/*"
        putExtra(Intent.EXTRA_STREAM, uri)
        addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    }
    
    startActivity(Intent.createChooser(intent, "分享文件"))
}

7.3 定期清理缓存 #

kotlin
fun clearOldCache() {
    val cacheDir = cacheDir
    val currentTime = System.currentTimeMillis()
    val oneWeek = 7 * 24 * 60 * 60 * 1000L
    
    cacheDir.listFiles()?.forEach { file ->
        if (currentTime - file.lastModified() > oneWeek) {
            file.delete()
        }
    }
}

八、总结 #

本章详细介绍了Android文件存储:

  1. 存储分类和路径
  2. 内部存储的使用
  3. 外部存储的使用
  4. 动态权限请求
  5. 文件选择器
  6. 文件操作最佳实践

合理使用文件存储可以满足各种数据持久化需求,但要注意权限管理和存储空间的合理使用。

最后更新:2026-03-26