Android中的mkdirs()方法创建了一个零字节文件而不是文件夹

7
在我的安卓应用中,我正在尝试在SD卡上创建以下文件夹:
/mnt/sdcard/OSGiComponents/admin/felix-cache/

这里是代码:

File cacheDir = 
    new File( Environment.getExternalStorageDirectory().getAbsolutePath() + 
              "/OSGiComponents/admin/felix-cache/" );
// Create the folder
cacheDir.mkdirs();
// Check if it exists
if ( ! cacheDir.exists() ) {
    Log.e ( "Debug" , "Cache directory cannot be created" );
}

我在android清单文件的manifest标签下拥有WRITE_STORAGE_PERMISSION。我可以在SD卡上创建其他文件夹和文件而没有问题。 该应用程序在以下手机上运行良好: 1. Nexus S(已root)运行Gingerbread(2.3) 2. Nexus S(未root)运行Jelly Bean(4.1.2) 3. HTC Desire(已root)运行Froyo(2.2) 4. HTC Desire(未root)运行Froyo(2.2)
然而,在运行Ice Cream Sandwich(4.0.4)的三星Galaxy Nexus手机上,目录被创建为零大小的文件,在Astro中可以看到。exists()调用返回false。
如您从文件夹名称中所见,我正在使用Apache Felix。 Felix如果不存在则会自动创建缓存目录。在Galaxy Nexus上,它总是抱怨无法创建缓存目录。 Astro显示一个0字节的文件而不是文件夹。这就是为什么我决定尝试在初始化Felix之前自己创建缓存文件夹的原因。
所以,我自己创建了缓存文件夹。应用程序第一次正常工作,我可以在Astro中看到文件夹。如果我关闭应用程序,然后在Astro中删除文件夹,然后重新启动应用程序,即使我的代码神秘地不能创建缓存目录,Astro也会显示0字节的文件。
在Astro中无法删除0字节的文件。但是,当我重启手机时,文件夹就神奇地出现了并且可以正常工作。
我使用FileInstall来监视OSGiComponents / install文件夹。当我将bundle jar放入该文件夹中时,除Galaxy Nexus(应用程序第一次工作时)外,所有手机都能检测到它并安装成功。有关无法监视目录的FileInstall的日志/错误。
我已在2台Galaxy Nexus手机上进行了测试,发现相同的问题。
我怀疑这是一个权限问题,但不确定是什么,并且为什么exists()返回false时会创建0字节的文件。在代码的其他地方我没有创建此文件。
任何关于可能存在的问题的建议?
谢谢:)
更新:我认为我已经确定了问题,请查看我发布的答案。

公共静态字符串 FOLDER=环境.getExternalStorageDirectory().getPath()+ "/文件夹/"; File screensPath = new File(FOLDER); screensPath.mkdirs(); 这个可能会起作用。 - itsrajesh4uguys
谢谢,但不幸的是仍然存在同样的问题:( 我想我已经找到了问题所在,我在下面发布了一个答案。 - Kartik Sankaran
5个回答

2
我找到了一个解决这个问题的方法。每当我删除文件/目录时,我不是直接使用delete(),而是将文件/文件夹重命名,然后再删除它。这种奇怪的解决方法似乎可以解决这个问题。
我是通过查看此问题的答案得到这个想法的-Open failed EBUSY device or Resource busy 然而,我不确定为什么这样做有效,或者最初导致问题的原因是什么。
如果有人正在使用 Galaxy Nexus 上的 Felix 并遇到相同的问题,请按照下面所示更改 Felix 源代码:
org.apache.felix.framework.util.SecureAction.java:
public boolean deleteFile(File target)
{
    if (System.getSecurityManager() != null)
    {
        try
        {
            Actions actions = (Actions) m_actions.get();
            actions.set(Actions.DELETE_FILE_ACTION, target);
            return ((Boolean) AccessController.doPrivileged(actions, m_acc))
                .booleanValue();
        }
        catch (PrivilegedActionException ex)
        {
            throw (RuntimeException) ex.getException();
        }
    }
    else
    {
        // Solution: Rename before deleting
        // https://dev59.com/lWgu5IYBdhLWcg3wJT5I

        File to = new File(target.getAbsolutePath() + System.currentTimeMillis());
        boolean renameStatus = target.renameTo(to);
        boolean deleteStatus = to.delete();
        boolean returnStatus = ( renameStatus && deleteStatus );

        // Debug SecureAction
        //boolean returnStatus = target.delete();
        Log.e ( "SecureAction" , "Deleting " + target + " delete(): " + returnStatus );
        return returnStatus;
    }
}

2
请使用代替。
   File cacheDir = new File( Environment.getExternalStorageDirectory().getAbsolutePath() + 
          "/OSGiComponents/admin/felix-cache/" );
      cacheDir.mkdirs();

为了

  File cacheDir = 
new File( Environment.getExternalStorageDirectory().getAbsolutePath() + 
          "/OSGiComponents/admin/felix-cache" );
     cacheDir.mkdir();

1
谢谢,但是我尝试去掉最后一个斜杠并将mkdirs()更改为mkdir(),或者任何这些更改的组合,但仍然遇到相同的问题。如果我的理解正确,由于此目录树最初在手机上不存在,因此必须使用mkdirs()而不是mkdir()。此外,Felix也在内部使用mkdirs()。 - Kartik Sankaran
请删除getAbsolutePath()并检查一次。 - raju
我仍然遇到同样的问题。 - Kartik Sankaran
哪里是Wally?它是尾随的/ - Mike Casan Ballester

1
String root = Environment.getExternalStorageDirectory()
            .getAbsolutePath()+"/OSGiComponents/admin/felix-cache";
    File myDir = new File(root);

    String fname = "Image Name as you want";
    File file = new File(myDir, fname);
    if (file.exists())
        file.delete();
    try {
        FileOutputStream out = new FileOutputStream(file);
        bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
        out.flush();
        out.close();

    } catch (Exception e) {
        e.printStackTrace();
    }

@Yogesh - 谢谢,但我正在尝试创建一个目录,而不是文件。 - Kartik Sankaran

1

我建议您使用adb连接设备,并在目录中使用命令ls -l,以检查操作系统对此0大小文件(权限等)的报告。这可能最终会解决问题。

如果您无法找到可行的解决方案,也许您可以使用exec()来直接执行mkdir来实现一个解决方法。

您可以使用下面的代码:

public static boolean execCmd(String command, ArrayList<String> results){
    Process process;
    try {
        process = Runtime.getRuntime().exec(new String [] {"sh", "-c", command});
    } catch (IOException e) {
        e.printStackTrace();
        return false;
    } 

    int result;
    try {
        result = process.waitFor();
    } catch (InterruptedException e1) {
        e1.printStackTrace();
        return false;
    }

    if(result != 0){ //error executing command
        Log.d("execCmd", "result code : " + result);
        String line; 
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); 
        try {
            while ((line = bufferedReader.readLine()) != null){
                if(results != null) results.add(line);
                Log.d("execCmd", "Error: " + line);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return false;
    }

    //Command execution is OK
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream())); 

    String line; 
    try {
        while ((line = bufferedReader.readLine()) != null){
            if(results != null) results.add(line);
            Log.d("execCmd", line);
        }
    } catch (IOException e) {
        e.printStackTrace();
        return false;
    }
    return true;
}

使用它:

boolean res= execCmd("mkdir "+ Environment.getExternalStorageDirectory().getAbsolutePath() + "/OSGiComponents", results);
if(!res) return error;   

res= execCmd("mkdir "+ Environment.getExternalStorageDirectory().getAbsolutePath() + "/OSGiComponents/admin", results);
if(!res) return error;   

res= execCmd("mkdir "+ Environment.getExternalStorageDirectory().getAbsolutePath() + "/OSGiComponents/admin/felix-cache", results);
if(!res) return error;   

敬礼。


感谢您详细的回复。我尝试了您的解决方法,但不幸的是它仍然出现相同的问题 :( - Kartik Sankaran
今天我进行了一些调试,并确定了导致问题的文件操作序列。我已经在这个问题上发布了一个问题 - [相关问题](http://stackoverflow.com/questions/14001730/android-felix-bug-particular-sequence-of-operations-producing-zero-byte-file-i) - Kartik Sankaran

0

虽然这不是对你问题的直接回答,但下面提到的信息可能会对你有所帮助:

在某些设备上,Environment.getExternalStorageDirectory().getAbsolutePath() 并不一定反映实际的外部SD卡路径。有时它代表内部存储。

例如,在Galaxy Note 2中:

Log.i(TAG,Environment.getExternalStorageDirectory().getAbsolutePath()); 

将会打印

12-19 17:35:11.366: E/sample.Examples(10420): /storage/sdcard0

同时实际的外部SD卡应该是:

/storage/extSdCard

以下是几篇关于此问题的帖子,可能会对您有所帮助:

构建一个实用程序,每次获取外部可移动存储路径

检查SD卡是否存在,布尔值始终为真

如何在三星和所有其他设备上获取正确的外部存储?

Android如何使用Environment.getExternalStorageDirectory()


谢谢提供的信息,我会查看您提供的链接,看看它们是否能够解决问题。 - Kartik Sankaran
我查看了链接,但在我的手机上,/mnt/sdcard是正确的,并且指向“外部”内部闪存。此外,只要缓存被创建并且可以写入,它的创建位置并不重要。 - Kartik Sankaran

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