安卓应用无法读取外部存储文件

5
我正在创建一个仅供个人使用的应用程序...它不是那么重要,因此请尊重您的时间:P
我想做的是一个从数据库中读取并播放声音的应用程序。我已经创建了数据库,并且可以从res/raw文件夹播放声音。然而,有成千上万个要播放的文件,一些新的文件将成为应用程序的一部分,其他文件将在未来被删除。因此,每次添加一个简单的文件都会让我下载整个应用程序(大约1 GB)(或者我错了?)。相反,我考虑从SD卡中读取Excel文件并播放声音。但是我不能...据我所知:

除了其特定于包的目录之外,应用程序不允许写入(删除、修改...)外部存储。

我将文件放入/storage/emulated/0/Android/data/my_package_folder/files文件夹中。我已经阅读了很多主题,但没有什么帮助我。
Environment.MEDIA_MOUNTED.equals(extStorageState) //returns true
Environment.MEDIA_MOUNTED_READ_ONLY.equals(extStorageState) //returns false

我尝试了几种方法来创建新的文件对象或使用MediaPlayer播放声音(如我之前所说,播放res/raw文件没有问题),但是file.exists始终返回false或声音无法播放:

String filePath = getExternalFilesDir(null) + "/myfile.mp3"; //returns correct file path
File file = new File(filePath);

或者

File file = new File(getExternalFilesDir(null).getPath(), "myfile.mp3");

或者

MediaPlayer mp;
mp = MediaPlayer.create(this, Uri.parse(getExternalFilesDir(null).getPath() + "/myfile.mp3"));

minSdkVersion被设置为19,因此我不需要在清单中添加那一行:

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

我已经包含了那行代码,但是没有任何变化:
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

我也尝试使用其他文件扩展名来做这件事。此外,这里提供的解决方案并没有帮助。我的手机是三星Galaxy Grand Prime(SM-G530FZ),Android版本为5.0.2。请帮忙,谢谢! :)


你的targetSdk是多少? - Nisarg
目标 SDK 版本 23 - kuznikowski
如果有帮助,请检查我的答案 :) - Nisarg
请查看此链接:https://dev59.com/lJffa4cB1Zd3GeqP2xnf#37253779 - Vickyexpert
5个回答

5
这里是代码 @Nisarg,需要稍微整理一下:
import android.Manifest;
import android.content.pm.PackageManager;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

import java.io.File;

public class MainActivity extends AppCompatActivity implements OnClickListener {

Button readExcelButton;
static String TAG = "ExelLog";
MediaPlayer mpintro;

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M) {
        Toast.makeText(this, "Permission checking", Toast.LENGTH_SHORT).show();
        checkPermission();
    }

    readExcelButton = (Button) findViewById(R.id.readExcel);
    readExcelButton.setOnClickListener(this);

}

public void onClick(View v)
{
    switch (v.getId())
    {
        case R.id.readExcel:
            String filePath = getExternalFilesDir(null) + "/p0000.mp3";
            File file = new File(filePath);
            file.setReadable(true);
            file.setWritable(true);

            if (!isExternalStorageAvailable() || isExternalStorageReadOnly()) {
                Toast.makeText(this, "Ext storage not available or read only", Toast.LENGTH_SHORT).show();
            }
            else {
                Toast.makeText(this, "Ext storage available", Toast.LENGTH_SHORT).show();
            }

            if (file.exists()){
                Toast.makeText(this, "File found", Toast.LENGTH_SHORT).show();
            }
            else {
                Toast.makeText(this, getExternalFilesDir(null) + "/p0000.mp3 - File not found", Toast.LENGTH_LONG).show();
            }

            break;
    }
}

private void checkPermission() {
    if (ContextCompat.checkSelfPermission(this,
            Manifest.permission.WRITE_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
            Manifest.permission.READ_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {//Can add more as per requirement

        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE},
                123);

    } else {

    }
}

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case 123: {

            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED)     {
                //Peform your task here if any
            } else {

                checkPermission();
            }
            return;
        }
    }
}

public static boolean isExternalStorageReadOnly() {
    String extStorageState = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(extStorageState)) {
        return true;
    }
    return false;
}

public static boolean isExternalStorageAvailable() {
    String extStorageState = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(extStorageState)) {
        return true;
    }
    return false;
}

}


2

如果您的targetSdk为23,则必须像这样检查权限,以适配Marshmallow及以上设备。

if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M) {
    checkPermission();
}
else {
}


private void checkPermission() {
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
            Manifest.permission.READ_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {//Can add more as per requirement

            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE},
                    123);

        } else {

        }
}

@Override
public void onRequestPermissionsResult(int requestCode,
                                   String permissions[], int[] grantResults)   
   {
    switch (requestCode) {
        case 123: {


 if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED)     {
   //Peform your task here if any
    } else {

        checkPermission();
    }
        return;
    }
}
}

你是说在那个switch语句中应该用"default"而不是"else",对吗?不,什么都没改;( - kuznikowski
@kuznikowski 检查已编辑的答案,抱歉这是我的错误。 - Nisarg
我是新手,需要问一个愚蠢的问题……第一个 IF 语句必须放在我的 onCreate 方法里面吗?它返回 false ,所以代码的其余部分不会执行。 - kuznikowski
因为内容太长不能放在评论里,我会把它放在回答里 - kuznikowski
@kuznikowski,没关系,有什么问题吗? - Nisarg
显示剩余3条评论

1

您还需要具有READ_EXTERNAL_STORAGE权限;当涉及到外部存储时,仅写入或读取是不够的,因为您需要同时使用两者。

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

您还可以阅读声明了解有关外部数据写入的更多信息。

您无法读取的原因是因为您没有添加该权限。一个好的规则是,如果您只需要读取,则只使用读取权限。但是,如果您需要写入,则需要写入和读取权限。

编辑:

在加载目录时,请尝试以下操作:

file.setReadable(true);
file.setWritable(true);

注意:这里的'value'值是指目录,而不是每个单独的文件(尽管这也可以正常工作)。
从声明中可以看出:
此权限从API 19级开始执行。在API 19级之前,此权限未执行,所有应用程序仍然可以访问外部存储器进行读取。
如果我理解正确,这意味着从API 19及以上版本必须存在该权限,但在API 19之前则不需要。

API 19,我不需要它...但是好吧,我再次检查了一下,添加了读写权限,但没有帮助。 - kuznikowski
你尝试过使用 file.setReadable 和 file.setWritable 吗? - Zoe stands with Ukraine
我在这里找到了一个链接 - https://developer.android.com/reference/android/Manifest.permission.html#WRITE_EXTERNAL_STORAGE - "从API 19级开始,在应用程序特定目录中读写文件时不需要此权限"编辑: 是的,setReadable和setWritable也没有帮助。 - kuznikowski
如果您不需要读取外部文件(来自其他应用程序的公共保存文件),您可以尝试这个链接:http://pastebin.com/fTPT1JKi - Zoe stands with Ukraine
不好意思,我不知道如何使用它。无论如何,谢谢你的尝试 :) - kuznikowski
调用Saver.save方法,传入名称、值(字符串)和上下文参数,然后调用Saver.load方法,你会看到适合所使用变量的建议。与外部存储不同,这种保存方式更加安全。 - Zoe stands with Ukraine

0
 MyMediaPlayer=MediaPlayer.create(this,Uri.parse(String.valueOf(Uri.fromFile(f))));

在这里,MyMediaPlayerMediaPlayer 对象,f 是文件对象。 我们首先将其转换为 URI,然后将 URI 转换为字符串,最后再将字符串转换为 URI。

我之所以这样做,是因为当我们选择路径时,它会出现 /// 三次 /,但实际上只需要两次,这就是为什么我进行了这种转换,帮助我获取所需的文件路径。

朋友,请试一试。


0

Android 9(API Level 28)开始限制哪些应用程序可以使其内部存储数据目录中的文件对其他应用程序可访问。 针对 Android 9 或更高版本的应用程序无法将其数据目录中的文件设置为全局可访问。 如需进一步了解,请查看此 link

如果您的目标是 API 级别 28 或更高级别,则可以在 AndroidManifest.xml 中简单添加一些标记来解决问题。 因此,它应该至少有这些标记:

<?xml version="1.0" encoding="utf-8"?>
<manifest
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   package="com.your.application">

   <!-- These three tags -->
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
   <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
   <uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE"/>

   <!-- Add the tag android:requestLegacyExternalStorage="true" in application -->
   <!-- Other tags has been omitted -->
   <application
      android:requestLegacyExternalStorage="true">
   </application>

</manifest>

现在您可以检查自己是否能够读写:

File root = new File(Environment.getExternalStorageDirectory() + "/My app folder");

Log.d("Storage: can write", String.valueOf(root.canWrite()));
Log.d("Storage: can read", String.valueOf(root.canRead()));

两个日志的结果都应该是 TRUE。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接