为啥 getExternalFilesDirs() 在某些设备上不起作用?

Posted

技术标签:

【中文标题】为啥 getExternalFilesDirs() 在某些设备上不起作用?【英文标题】:Why getExternalFilesDirs() doesn't work on some devices?为什么 getExternalFilesDirs() 在某些设备上不起作用? 【发布时间】:2016-01-25 19:38:03 【问题描述】:

我的应用在 android 5.0 上运行。我使用方法getExternalFilesDirs() 来检查外部SD 卡是否可用。如果返回超过 1 个File,则表示存在外置 SD 卡。

但在某些设备上(例如 Elephone G2),方法 getExternalFilesDirs() 仅返回一个主存储目录。我确定该设备有外部 SD 卡 (/storage/sdcard1/)。

谁能给我答案?

【问题讨论】:

据推测,Elephone 的制造商决定不允许开发人员访问可移动媒体。或者,也许他们在配置 ROM 时搞砸了。 @CommonsWare 应该是答案。我真的不明白为什么getExternalFilesDirs() 不起作用。它适用于几乎所有设备。 您的 AndroidManifest.xml 文件中有 android.permission.WRITE_EXTERNAL_STORAGE 吗? @Shark 当然可以。正如我所说,它几乎可以在设备上运行。 @Sunshinetpu 看起来你偶然发现了一个半途而废的供应商 ROM:/ 【参考方案1】:

为了让 getExternalFilesDirs 返回 sdcard 的路径,OEM 必须在设备特定的 init.rc 文件中设置 SECONDARY_STORAGE 环境变量,如下所述: https://source.android.com/devices/storage/config-example.html

在这里查看getExternalFilesDirs的来源: http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/app/ContextImpl.java#1039

该值是从Environment.buildExternalStorageAppFilesDirs 获得的。在此处查看该来源: http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/os/Environment.java#206

该值取决于mExternalDirsForApp,而后者又通过读取 SECONDARY_STORAGE 变量的内容来填充: http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/os/Environment.java#136

如您所见,如果没有设置 SECONDARY_STORAGE 变量,则不会返回 sdcard 路径。 您可以通过转到adb shell 并查看echo $SECONDARY_STORAGE 的输出来交叉检查这一点

【讨论】:

谢谢。那么如果没有设置 SECONDARY_STORAGE 变量,我们就无法访问外部 sd 卡? 如果未设置该环境变量,则无法使用 getExternalFilesDirs 写入 sdcard,因为它不会返回 sdcard 路径。但是,您可以通过 SAF 询问用户写入 sdcard 的权限。看看这个链接:***.com/questions/26744842/… 实际上,如果设备不支持$SECONDARY_STORAGE,您只需请求WRITE_EXTERNAL_STORAGE权限即可。无需其他修改,我对此进行了测试。【参考方案2】:

在我使用此代码的项目中,我没有任何问题。

getExternalFilesDirs 方法返回长度为 2 的数组。

Dirs[0] ==> Internal Sorage Dirs[1] ==> External Storage

 File[] Dirs = ContextCompat.getExternalFilesDirs(MyApp.GetContext(), null);

【讨论】:

您有 WRITE_EXTERNAL_STORAGE 权限吗?你能用你的方法创建文件吗? OP 专门要求一种方法不起作用...您在答案中使用... @MHossein 我会测试你的代码。你能解释一下你为什么使用ContextCompat吗?我使用Context 的方法getExternalFilesDirs(String fileName)。它适用于几乎所有设备。 需要注意的是,MHossein 使用的是ContextCompat.getExternalFilesDirs 而不是Context. getExternalFilesDirs。 Src link @MHossein 为什么文件路径必须包含“sdcard”?【参考方案3】:

一些联想设备也存在这个问题。

我的解决方案是这样的。

String EXTERNAL_SD_PATH1;
String EXTERNAL_SD_PATH2;

public boolean hasExternalSDCard()

    try
    
        String state = Environment.getExternalStorageState();
        if(Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state))
        return true;
    
    catch (Throwable e)
    

    return false;


        @SuppressLint("SdCardPath")
        protected synchronized void _prepareStorage()
        
            EXTERNAL_SD_PATH1 = null;
            EXTERNAL_SD_PATH2 = null;
            if (hasExternalSDCard())
            
                try
                
                    if(VERSION_SDK_INT > 18)
                    
                        Context context = getContext();
                        File[]  sds = getExternalFilesDirs("");

                        if(sds == null)
                            return;

                        if(sds.length >= 2)
                        
                            EXTERNAL_SD_PATH1 = TextWorker.getSubStringBeforeLastMark(sds[1].getAbsolutePath(),"/Android/");
                            if(sds.length > 2)
                                EXTERNAL_SD_PATH2 = TextWorker.getSubStringBeforeLastMark(sds[2].getAbsolutePath(),"/Android/");
                        
                        else
                        
                            String internal = sds[0].getAbsolutePath();
                            internal = TextWorker.getSubStringBeforeLastMark(internal,"/Android/");
                            int len = internal.length();
                            int num = Integer.valueOf(internal.substring(len - 1));

                            String ex1 = internal.substring(0, len-1) + (num+1);
                            File sd1 = new File(ex1);
                            if(sd1.exists())
                                EXTERNAL_SD_PATH1 = sd1.getAbsolutePath();

                            String ex2 = internal.substring(0, len-1) + (num+2);
                            File sd2 = new File(ex2);
                            if(sd2.exists())
                                EXTERNAL_SD_PATH2 = sd2.getAbsolutePath();
                        
                    

                    else
                    
                        File sd = Environment.getExternalStorageDirectory();
                        String path = sd.getAbsolutePath();
                        if (sd.exists() && (path.contains("/mnt/") || path.contains("/storage") || path.contains("/sdcard")) && (!path.contains("emulate")))
                        
                            EXTERNAL_SD_PATH1 = path;
                        
                    
                
                catch (Throwable e)
                
            

        


        public static String getSubStringBeforeLastMark(String str,String mark)
     
        int l = str.lastIndexOf(mark);
        if(l == -1 || l == 0)
            return "";

        return str.substring(0, l);
    

【讨论】:

以上是关于为啥 getExternalFilesDirs() 在某些设备上不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

在 API 23 及更高版本中插入 SD 卡时,getExternalFilesDirs() 未在 ApplicationContext 中更新

FileProvider 和辅助外部存储

Android 5(HTC) EACCES(权限被拒绝)

Flutter开发之——文件系统目录pathprovider,997页手淘Android面试真题解析火爆全网

你应该同步运行方法吗?为啥或者为啥不?

为啥使用 glTranslatef?为啥不直接更改渲染坐标?