这个算是一个非常通用并且常见的API了,但是其实这个方法在删除文件的时候其实是有一些限制的。如下,我们可以看见这个API返回值代表着删除是否成功:

public boolean delete() {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkDelete(path);
    }
    if (isInvalid()) {
        return false;
    }
    return fs.delete(this);
}

继续追溯,我们能看到这里的fs的值:

/**
     * The FileSystem object representing the platform's local file system.
     */
    private static final FileSystem fs = DefaultFileSystem.getFileSystem();

哈哈,继续,继续,看看这个到底是什么?

class DefaultFileSystem {

    /**
     * Return the FileSystem object for Unix-based platform.
     */
    public static FileSystem getFileSystem() {
        return new UnixFileSystem();
    }
}

可以看到返回的是Java所虚拟化的UnixFileSystem类,即JVM所模拟的一个Unix-like文件系统类。于是我们暂时得到了,实际上我们调用File#delete(),会执行到UnixFileSystem#delete(File)。继续到UnixFileSystem.java中查询delete()的实现。可以看到其间接调用了native层的delete0(File)方法。OK,继续追踪!

// Android-changed: Added thread policy check
public boolean delete(File f) {
    // Keep canonicalization caches in sync after file deletion
    // and renaming operations. Could be more clever than this
    // (i.e., only remove/update affected entries) but probably
    // not worth it since these entries expire after 30 seconds
    // anyway.
    cache.clear();
    javaHomePrefixCache.clear();
    BlockGuard.getThreadPolicy().onWriteToDisk();
    return delete0(f);
}
private native boolean delete0(File f);

找到UnixFileSystem_md.c类,其中就定义了这个native delete0方法:

JNIEXPORT jboolean JNICALL
Java_java_io_UnixFileSystem_delete0(JNIEnv *env, jobject this,
                                    jobject file)
{
    jboolean rv = JNI_FALSE;

    WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
        //调用unix remove接口以删除具体的文档
        if (remove(path) == 0) {
            rv = JNI_TRUE;
        }
    } END_PLATFORM_STRING(env, path);
    return rv;
}

可以看到该方法返回的就是unix的remove方法的结果,根据APUE Page.94所言,

当调用Unix-like系统remove() API时,应该同时满足如下两个条件:

1. 对于该文件的链接数为0,我们应用内所创建的临时文件肯定是不会有硬链接的
2. 不存在进程打开了该文件
实际在进行文件的删除操作时,只有当链接计数达到0时,该文件的内容才可被删除。另一个条件也会阻止删除文件的内容–只要有进程打开了该文件,其内容也不能删除。关闭一个文件时,内核首先检查打开该文件的进程个数;如果这个技术达到0,内核再去检查其链接计数:如果计数也是0,那么就删除该文件的内容。

我们就能知道最开始所说的限制是什么了,作为android开发,应用运行的时候一般不会去创建硬链接,所以第一个限制一般不会出问题。但是第二个条件,如果在调用File#close()时,有流正在准备或者正在继续输入输出操作,那么File#delete()的调用就会失败,返回false。

下面是一个demo,进行了一次实验,开启了一个额外的输出流,如果在delete的时候仍存在对该文件的使用,delete操作会失败!

import java.io.*;
import java.net.URL;

public class Test {

    private static void downloadUsingStream1(String urlStr, String file) throws IOException {
        URL url = new URL(urlStr);
        final File newFile = new File(file);
        InputStream bis = url.openStream();
        FileOutputStream fis = new FileOutputStream(newFile);
        //重点关注这里!!!!!我额外开启了一个输出流,但是在调用delete的时候没有释放
        FileOutputStream fos = new FileOutputStream(newFile);
        byte[] buffer = new byte[1024];
        int count = 0;
        int writeCount = 0;
        try {
            while ((count = bis.read(buffer, 0, 1024)) != -1) {
                fis.write(buffer, 0, count);
                if (++writeCount >= 60) {
                    throw new IOException("123");
                }
            }
        } catch (IOException e) {
            fis.close();
            bis.close();
            boolean isDeleteSuccessful = newFile.delete();
            System.out.println("" + isDeleteSuccessful);
        }
        fis.close();
        bis.close();
    }

    public static void main(String[] args) {
        String url = "file:" + File.separator + "D:" + File.separator + "game.jpg";
        try {
            downloadUsingStream1(url, "./game1.jpg");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

至于为什么仅仅开启一个输出流就能达成这个目的呢?因为实际输出流在创建的时候就已经开启了文件访问标记。

java 中deletebtn作用 java中file.delete_java 中deletebtn作用