将文件保存在 Android - 初学者(内部/外部存储)

Saving Files in Android - For Beginners (Internal / External Storage)

我是 android 开发的新手,我需要初学者的帮助...

我正在创建一个应该有 2 个目录的应用程序,一个数据库目录和一个图像目录,就像 Whatsapp 一样。

我想在浏览 phone 时在文件管理器中看到这些目录,就像您看到其他应用程序文件夹一样。 我尝试了在这里找到的所有内容,以通过代码在内部存储和外部存储中创建它。这些文件夹似乎已创建,但是当我浏览 phone 的文件管理器时,我找不到带有我的应用程序名称的文件夹以及其中的数据库和图像文件夹... 我究竟做错了什么?我是否需要在 android 工作室中创建这些文件夹作为添加文件夹?还是我需要从代码创建它?

能否请您告诉我完成此任务需要执行的操作? 我正在使用 android studio 3.1.3。 感谢各位帮手! :)

术语“内部存储”和“外部存储”一开始可能会让人混淆,因为Google意图与我们从日常语言使用中所期望和了解的不同:"External" 并不一定意味着 "SD Card"。 This guy made a great article about the terminology confusion

根据您的意图,您可能希望使用 外部存储 概念。 Documentation 中对差异进行了很好的解释,但我将在此处简要介绍一下。

最后我会给你一个例子,但首先让我们了解一下基础知识:

内部存储

  • 只有您的应用程序可以访问文件
  • 卸载您的应用程序时会删除文件
  • 文件始终可用(意味着它们永远不会保存在可移动存储器中)

外部存储

  • 文件可被其他应用程序完全读取(在您的情况下包括文件管理器应用程序的任何变体)
  • 卸载您的应用程序时不一定会删除文件 - 稍后解释
  • 不保证文件可用性(可以被其他应用程序/可移动内存删除)

既然我们知道您需要外部存储,那么在开始之前需要完成几件事:

  • 需要权限 (read/write) 在您的 Manifest.xml 文件中,具体取决于您的需要:
    <manifest ...>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
    </manifest>

每个权限都是独立的,这意味着您不需要同时拥有这两个权限,例如,如果您只想读取文件而不是写入文件

  • 验证 storage is available - 这是在运行时完成的,并且在文档中有很好的解释。我们需要确保存储已安装到设备中/它的状态不会以某种方式导致 read/write 请求失败。

示例时间!

在给定的方法中,我们将在根目录中保存一个文本文件。

感谢this article

public void writeFileExternalStorage() {

    //Text of the Document
    String textToWrite = "bla bla bla";

    //Checking the availability state of the External Storage.
    String state = Environment.getExternalStorageState();
    if (!Environment.MEDIA_MOUNTED.equals(state)) {

        //If it isn't mounted - we can't write into it.
        return;
    }

    //Create a new file that points to the root directory, with the given name:
    File file = new File(getExternalFilesDir(null), filenameExternal);

    //This point and below is responsible for the write operation
    FileOutputStream outputStream = null;
    try {
        file.createNewFile();
        //second argument of FileOutputStream constructor indicates whether
        //to append or create new file if one exists
        outputStream = new FileOutputStream(file, true);

        outputStream.write(textToWrite.getBytes());
        outputStream.flush();
        outputStream.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

我想专门回答你的一些问题:

do I need to create those folders in the android studio as adding folders? or do I need to create it from code?

绝对不是通过 Android Studio。这些是您的项目文件夹,其中包含您的代码。方法上面已经说了

I couldn't find the folder with my app name and inside it my Databases and Images folders... What am I doing wrong?

可能如您之前提到的那样将您的文件保存为内部存储文件/将它们保存为项目文件夹 - 但那些不会(也不应该)显示。


有用的知识

有两种类型的目录:public和私有。

私人

  • 媒体商店无法访问
  • 卸载应用程序时删除文件
  • 通过getExternalFilesDir(...)方法检索

示例: WhatsApp 目录(在我的 phone 中)位于根级别。调用它是:getExternalFilesDir("WhatsApp/...")

Public(Downloads/Movies/Images 个图书馆)

  • 文件由 MediaStore
  • 扫描
  • 通过Environment.getExternalStoragePublicDirectory(...)方法检索

示例: 获取 Documents 文件夹如下所示:Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)

主要答案确实帮助了我,如果没有那个很好的答案,我就无法做到这一点。

但是我在获取存储目录时遇到了一些困难(因为 API 随着时间的推移发生了变化)并且我使用的是 Kotlin,所以我想我只是分享我使用的最终代码。

我的也是 returns 字符串消息(文件的绝对路径是成功的,否则是错误消息)。

我还添加了一些日志语句,这样您就可以看到发生了什么。

fun writeFileExternalStorage() :String {

        var fileData : String = "test this"
        
        Log.i("FirstFrag", "in writeFileExternalStorage")
        //Checking the availability state of the External Storage.
        val state = Environment.getExternalStorageState()
        if (Environment.MEDIA_MOUNTED != state) {

            Log.i("FirstFrag", "Couldn't get file system.")
            return "Couldn't write file."
        }

        var file =File(requireContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "test.txt")

        Log.i("FirstFrag", file.absolutePath)
        //This point and below is responsible for the write operation
        var outputStream: FileOutputStream? = null
        try {
            Log.i("FirstFrag", "creating new file")
            file.createNewFile()
            Log.i("FirstFrag", "file created")
            //second argument of FileOutputStream constructor indicates whether
            //to append or create new file if one exists
            outputStream = FileOutputStream(file, true)
            outputStream?.write(fileData.toByteArray())
            outputStream?.flush()
            outputStream?.close()
            Log.i("FirstFrag", "wrote file!")
            return file.absolutePath
        } catch (e: Exception) {
            Log.i("FirstFrag",e.message.toString())
            return e.message.toString() + "\nPlease check that you have a valid filename."
        }
        finally {
            isRetrievingFile = false;
        }
    }