Android 如何让用户访问我的应用程序的内部存储目录?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26579869/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 11:54:00  来源:igfitidea点击:

How can I let users access the internal storage directory of my app?

androidfile-permissionsfileoutputstream

提问by arlomedia

My app stores files in its internal storage directory (/Android/data/com.mycompany.myapp/files, as returned by getFilesDir()), and I would like to allow users to access those files directly from a file management app on their mobile device or the Android File Transfer desktop appplication.

我的应用程序将文件存储在其内部存储目录(/Android/data/com.mycompany.myapp/files,由 getFilesDir() 返回)中,我希望允许用户直接从其上的文件管理应用程序访问这些文件移动设备或 Android 文件传输桌面应用程序。

The Storage Options developer guidesays:

存储选项开发人员指南说:

By default, files saved to the internal storage are private to your application and other applications cannot access them (nor can the user).

默认情况下,保存到内部存储的文件对您的应用程序是私有的,其他应用程序无法访问它们(用户也不能)。

"By default" implies that I can change the default permissions to allow users to access these files, but I can't find any documentation of how to do that. Currently the com.mycompany.myapp directory is hidden when I browse the /Android/data directory with a file management app.

“默认”意味着我可以更改默认权限以允许用户访问这些文件,但我找不到任何有关如何执行此操作的文档。当前,当我使用文件管理应用程序浏览 /Android/data 目录时, com.mycompany.myapp 目录是隐藏的。

I'm currently saving file data like this:

我目前正在保存这样的文件数据:

String fileName = "myfile.jpg";
String filePath = getFilesDir() + File.separator + fileName;
FileOutputStream fileStream = new FileOutputStream(filePath);
fileData.writeTo(fileStream); // fileData is a ByteArrayOutputStream
fileStream.close();

I tried setting the permissions of the individual files to world-readable, but this didn't make the directory visible:

我尝试将单个文件的权限设置为世界可读,但这并没有使目录可见:

FileOutputStream fileStream = this.app.openFileOutput(fileName, Context.MODE_WORLD_READABLE);

I also checked the documentation for the AndroidManifest file and didn't see anything there. Does anyone know how I can do this?

我还检查了 AndroidManifest 文件的文档,但没有看到任何内容。有谁知道我怎么能做到这一点?

回答by arlomedia

I took a closer look at the result of getFilesDir()vs getExternalFilesDir()and found that getFilesDir()returns /data/data/[packagename]/fileswhile getExternalFilesDir()returns /Android/data/[packagename]/files. I thought the app files I was browsing in /Android/datawere the internal storage directories, but now I see that those are actually the external storage directories.

我仔细查看了getFilesDir()vs的结果,getExternalFilesDir()发现getFilesDir()return /data/data/[packagename]/fileswhile getExternalFilesDir()returns /Android/data/[packagename]/files。我以为我浏览的应用程序文件是/Android/data内部存储目录,但现在我看到那些实际上是外部存储目录。

If the internal storage directories are indeed never available to regular users, I wish the documentation said that instead of saying they are not available "by default." To me at least, saying "by default" implies that the default behavior can be changed.

如果内部存储目录确实对普通用户永远不可用,我希望文档说的是,而不是说它们“默认情况下”不可用。至少对我来说,说“默认”意味着可以更改默认行为。

Anyway, I tested and confirmed that if I delete my app, files saved to the getExternalFilesDir()are deleted automatically. So that meets my need for a storage location that is clearly connected with the app (uses an app-specific directory name and is deleted with the app) but is accessible to users for occasional manual file management.

无论如何,我测试并确认如果我删除我的应用程序,保存到 的文件getExternalFilesDir()会自动删除。因此,这满足了我对与应用程序明确连接的存储位置的需求(使用应用程序特定的目录名称并随应用程序删除)但用户可以访问以进行偶尔的手动文件管理。

Here's a comparison that might be helpful to someone else reading this:

这是一个比较,可能对其他人有帮助:

  • getFilesDir()- creates an app-specific directory; hidden from users; deleted with the app
  • getExternalFilesDir()- creates an app-specific directory; accessible to users; deleted with the app
  • getExternalStoragePublicDirectory()- uses a shared directory (e.g., Music); accessible to users; remains when the app is deleted
  • getFilesDir()- 创建一个特定于应用程序的目录;对用户隐藏;用应用程序删除
  • getExternalFilesDir()- 创建一个特定于应用程序的目录;用户可访问;用应用程序删除
  • getExternalStoragePublicDirectory()- 使用共享目录(例如,音乐);用户可访问;删除应用程序时仍然存在

回答by Leo

I think you are getting confused by the developers documentation, I can't blame you, it's not the best documentation I've ever read. Anyway, Android provides two ways of saving files to the file system:

我认为您对开发人员文档感到困惑,我不能责怪您,这不是我读过的最好的文档。无论如何,Android 提供了两种将文件保存到文件系统的方法:

  1. Using the Internal Storage
  2. Using the External Storage
  1. 使用内部存储
  2. 使用外部存储

The Internal Storageis ALWAYS available and is ONLY accessible to your app, no other app in the system can access the files your app saved to this partition.

内部存储始终可用,是您的应用程序才能访问,在系统中可以访问你的应用程序保存在该分区中的文件,没有其他应用程序。

Now, I think what you need is the External Storagebecause it is "world-readable" and can be accessed by anyone and any any apps. The downside is that it may not be available since the user could mount it on a USB device - which can be removed by the user at anytime.

现在,我认为您需要的是外部存储,因为它是“世界可读的”,并且可以被任何人和任何应用程序访问。缺点是它可能不可用,因为用户可以将它安装在 USB 设备上 - 用户可以随时删除它。

You shouldn't use neither MODE_WORLD_WRITEABLEnor MODE_WORLD_READABLEas per the documentation states, because it is very dangerous. Furthermore, these constants have been deprecated since API Level 17

你不应该使用既不MODE_WORLD_WRITEABLE也不MODE_WORLD_READABLE按文件状态,因为这是非常危险的。此外,这些常量自 API 级别 17 起已被弃用

Recommendation

推荐

If you need to make your files public, then save them to the External Storage. You will need to declare a permission in your manifest file to avoid your app from crashing everytime it access the External Storage...

如果您需要公开您的文件,请将它们保存到外部存储。您需要在清单文件中声明权限,以避免您的应用每次访问外部存储时崩溃...

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

because the External Storagemight not be available you will need to determine the state of it before you perform any operation, otherwise your app will crash...

因为外部存储可能不可用,您需要在执行任何操作之前确定它的状态,否则您的应用程序将崩溃...

public enum StorageState{
  NOT_AVAILABLE, WRITEABLE, READ_ONLY
}

public StorageState getExternalStorageState() {
    StorageState result = StorageState.NOT_AVAILABLE;
    String state = Environment.getExternalStorageState();

    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return StorageState.WRITEABLE;
    }
    else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
        return StorageState.READ_ONLY;
    }

    return result;
}

There is more information on the documentation and things that you should be aware of. For example, you can give your app ownership of these files so that when your app is uninstalled the system can automatically delete these files. For more info please refer to the documentation on the Android Developers Site

有更多关于文档的信息和你应该注意的事情。例如,您可以将这些文件的所有权授予您的应用,以便在卸载您的应用时系统可以自动删除这些文件。有关更多信息,请参阅Android 开发者网站上的文档

回答by Nabat Farsi

add this to manifest

将此添加到清单

then add this in your activity:

然后将其添加到您的活动中:

private static final String RequieredPermission = Manifest.permission.READ_EXTERNAL_STORAGE;

then call HandlePermission()where your require to check for permission, usually in Oncreate method of an activity. These functions are then required:

然后调用HandlePermission()您需要检查权限的地方,通常在活动的 Oncreate 方法中。然后需要这些功能:

@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        Log.d("amir", "onRequestPermissionsResult: called");
        switch (requestCode) {
            case REQUEST_PERMISSION_READING_STATE:
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(MainActivity.this, "Permission Granted!", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(MainActivity.this, "Permission Denied!", Toast.LENGTH_SHORT).show();
                }
        }

    }

    private void HandlePermission() {
        int permissionCheck = ContextCompat.checkSelfPermission(
                this, RequieredPermission);
        if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    RequieredPermission)) {
                showExplanationaboutPermission("Permission Needed", "Rationale", RequieredPermission, REQUEST_PERMISSION_READING_STATE);
            } else {
                requestPermission(RequieredPermission, REQUEST_PERMISSION_READING_STATE);
            }
        } else {
            Toast.makeText(MainActivity.this, "Permission (already) Granted!", Toast.LENGTH_SHORT).show();
        }
    }

    private void showExplanationaboutPermission(String title,
                                                String message,
                                                final String permission,
                                                final int permissionRequestCode) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(title)
                .setMessage(message)
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        requestPermission(permission, permissionRequestCode);
                    }
                });
        builder.create().show();
    }

    private void requestPermission(String permissionName, int permissionRequestCode) {
        ActivityCompat.requestPermissions(this,
                new String[]{permissionName}, permissionRequestCode);
    }