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

Android 如何让用户访问我的应用程序的内部存储目录?,android,file-permissions,fileoutputstream,Android,File Permissions,Fileoutputstream,我的应用程序将文件存储在其内部存储目录中(/Android/data/com.mycompany.myapp/files,由getFilesDir()返回),我希望允许用户直接从移动设备上的文件管理应用程序或Android file Transfer desktop应用程序访问这些文件 报告说: 默认情况下,保存到内部存储器的文件是应用程序的专用文件,其他应用程序无法访问它们(用户也不能) “默认情况下”意味着我可以更改默认权限以允许用户访问这些文件,但我找不到任何关于如何操作的文档。当前,当我

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

报告说:

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

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

我当前保存的文件数据如下:

String fileName = "myfile.jpg";
String filePath = getFilesDir() + File.separator + fileName;
FileOutputStream fileStream = new FileOutputStream(filePath);
fileData.writeTo(fileStream); // fileData is a ByteArrayOutputStream
fileStream.close();
我尝试将各个文件的权限设置为世界可读,但这并没有使目录可见:

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

我还检查了AndroidManifest文件的文档,没有看到任何内容。有人知道我是如何做到这一点的吗?

我想你被开发人员文档弄糊涂了,我不能怪你,这不是我读过的最好的文档。无论如何,Android提供了两种将文件保存到文件系统的方法:

  • 使用内部存储器
  • 使用外部存储器
  • 内部存储始终可用,并且仅可供您的应用访问,系统中的其他应用无法访问您的应用保存到此分区的文件

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

    您不应该按照文档说明使用或,因为这非常危险。此外,自API级别17以来,这些常量已被弃用

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

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

    有更多关于文档的信息以及您应该注意的事项。例如,您可以授予应用程序对这些文件的所有权,以便在卸载应用程序时,系统可以自动删除这些文件。有关更多信息,请参阅我仔细查看了
    getFilesDir()
    getExternalFilesDir()
    的结果,发现
    getFilesDir()
    返回
    /data/data/[packagename]/files
    getExternalFilesDir()
    返回
    /Android/data/[packagename]/files
    。我以为我在
    /Android/data
    中浏览的应用程序文件是内部存储目录,但现在我看到这些实际上是外部存储目录

    如果内部存储目录确实不可供普通用户使用,我希望文档中不要说它们“默认”不可用。至少对我来说,说“默认”意味着默认行为可以更改

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

    下面是一个比较,可能对其他人阅读本文有所帮助:

    • getFilesDir()-创建特定于应用程序的目录;对用户隐藏;已随应用程序删除
    • getExternalFilesDir()-创建特定于应用程序的目录;用户可访问;已随应用程序删除
    • getExternalStoragePublicDirectory()-使用共享目录(如音乐);用户可访问;删除应用程序时保留
    将此添加到清单中

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

    private static final String RequieredPermission = Manifest.permission.READ_EXTERNAL_STORAGE;
    
    然后在需要检查权限的地方调用
    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);
        }
    

    内部/外部名称确实令人困惑(“设备内置内存上的外部”存储与可移动存储卡上的“辅助外部”存储),但我想我明白了。内部存储在/Android/data/com.mycompany.myapp,对吗?似乎其他应用程序正在让用户看到这一点。例如,我安装了Kindle应用程序,可以从ES File Explorer应用程序或Mac上的Android File Transfer浏览/Android/data/com.amazon.Kindle/files目录。@arlomedia mate,我想你会更加困惑。您在文件夹中查看的Kindle文件不是私有文件,而是“世界可读的”。此外,如果您以MTP方式连接Windows机器,您甚至不需要使用Android File Transfer之类的工具来读取这些文件。所以,基本上,你不会真正理解它,否则你就不会发表评论……MODE_PRIVATE的意思就是只对拥有它的应用程序可见。继续浏览你的应用程序的内部存储,你会发现其中很多都是empty@arlomedia你到底还不明白什么?我的应用程序正在将其文件保存在data/com.mycompany.myapp/files中,对于浏览文件系统的普通(非根目录)用户来说,这似乎不存在。其他一些应用程序的目录确实会出现,用户可以浏览它们的文件。我如何才能让我的应用程序以这种方式工作?我只是尝试保存到getExternalFilesDir()位置,而不是getFilesDir()位置,这创建了一个可浏览的data/com.mycompany.myapp/files目录。看起来这些目录就是外部目录
    @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);
        }