FileDocCategorySizeDatePackage
AtomicFile.javaAPI DocAndroid 5.1 API8930Thu Mar 12 22:22:10 GMT 2015android.util

AtomicFile

public class AtomicFile extends Object
Helper class for performing atomic operations on a file by creating a backup file until a write has successfully completed. If you need this on older versions of the platform you can use {@link android.support.v4.util.AtomicFile} in the v4 support library.

Atomic file guarantees file integrity by ensuring that a file has been completely written and sync'd to disk before removing its backup. As long as the backup file exists, the original file is considered to be invalid (left over from a previous attempt to write the file).

Atomic file does not confer any file locking semantics. Do not use this class when the file may be accessed or modified concurrently by multiple threads or processes. The caller is responsible for ensuring appropriate mutual exclusion invariants whenever it accesses the file.

Fields Summary
private final File
mBaseName
private final File
mBackupName
Constructors Summary
public AtomicFile(File baseName)
Create a new AtomicFile for a file located at the given File path. The secondary backup file will be the same file path with ".bak" appended.

        mBaseName = baseName;
        mBackupName = new File(baseName.getPath() + ".bak");
    
Methods Summary
public voiddelete()
Delete the atomic file. This deletes both the base and backup files.

        mBaseName.delete();
        mBackupName.delete();
    
public voidfailWrite(java.io.FileOutputStream str)
Call when you have failed for some reason at writing to the stream returned by {@link #startWrite()}. This will close the current write stream, and roll back to the previous state of the file.

        if (str != null) {
            FileUtils.sync(str);
            try {
                str.close();
                mBaseName.delete();
                mBackupName.renameTo(mBaseName);
            } catch (IOException e) {
                Log.w("AtomicFile", "failWrite: Got exception:", e);
            }
        }
    
public voidfinishWrite(java.io.FileOutputStream str)
Call when you have successfully finished writing to the stream returned by {@link #startWrite()}. This will close, sync, and commit the new data. The next attempt to read the atomic file will return the new file stream.

        if (str != null) {
            FileUtils.sync(str);
            try {
                str.close();
                mBackupName.delete();
            } catch (IOException e) {
                Log.w("AtomicFile", "finishWrite: Got exception:", e);
            }
        }
    
public java.io.FilegetBaseFile()
Return the path to the base file. You should not generally use this, as the data at that path may not be valid.

        return mBaseName;
    
public longgetLastModifiedTime()
Gets the last modified time of the atomic file. {@hide}

return
last modified time in milliseconds since epoch.
throws
IOException

        if (mBackupName.exists()) {
            return mBackupName.lastModified();
        }
        return mBaseName.lastModified();
    
public java.io.FileOutputStreamopenAppend()

hide
deprecated
This is not safe.

        try {
            return new FileOutputStream(mBaseName, true);
        } catch (FileNotFoundException e) {
            throw new IOException("Couldn't append " + mBaseName);
        }
    
public java.io.FileInputStreamopenRead()
Open the atomic file for reading. If there previously was an incomplete write, this will roll back to the last good data before opening for read. You should call close() on the FileInputStream when you are done reading from it.

Note that if another thread is currently performing a write, this will incorrectly consider it to be in the state of a bad write and roll back, causing the new data currently being written to be dropped. You must do your own threading protection for access to AtomicFile.

        if (mBackupName.exists()) {
            mBaseName.delete();
            mBackupName.renameTo(mBaseName);
        }
        return new FileInputStream(mBaseName);
    
public byte[]readFully()
A convenience for {@link #openRead()} that also reads all of the file contents into a byte array which is returned.

        FileInputStream stream = openRead();
        try {
            int pos = 0;
            int avail = stream.available();
            byte[] data = new byte[avail];
            while (true) {
                int amt = stream.read(data, pos, data.length-pos);
                //Log.i("foo", "Read " + amt + " bytes at " + pos
                //        + " of avail " + data.length);
                if (amt <= 0) {
                    //Log.i("foo", "**** FINISHED READING: pos=" + pos
                    //        + " len=" + data.length);
                    return data;
                }
                pos += amt;
                avail = stream.available();
                if (avail > data.length-pos) {
                    byte[] newData = new byte[pos+avail];
                    System.arraycopy(data, 0, newData, 0, pos);
                    data = newData;
                }
            }
        } finally {
            stream.close();
        }
    
public java.io.FileOutputStreamstartWrite()
Start a new write operation on the file. This returns a FileOutputStream to which you can write the new file data. The existing file is replaced with the new data. You must not directly close the given FileOutputStream; instead call either {@link #finishWrite(FileOutputStream)} or {@link #failWrite(FileOutputStream)}.

Note that if another thread is currently performing a write, this will simply replace whatever that thread is writing with the new file being written by this thread, and when the other thread finishes the write the new write operation will no longer be safe (or will be lost). You must do your own threading protection for access to AtomicFile.

        // Rename the current file so it may be used as a backup during the next read
        if (mBaseName.exists()) {
            if (!mBackupName.exists()) {
                if (!mBaseName.renameTo(mBackupName)) {
                    Log.w("AtomicFile", "Couldn't rename file " + mBaseName
                            + " to backup file " + mBackupName);
                }
            } else {
                mBaseName.delete();
            }
        }
        FileOutputStream str = null;
        try {
            str = new FileOutputStream(mBaseName);
        } catch (FileNotFoundException e) {
            File parent = mBaseName.getParentFile();
            if (!parent.mkdir()) {
                throw new IOException("Couldn't create directory " + mBaseName);
            }
            FileUtils.setPermissions(
                parent.getPath(),
                FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
                -1, -1);
            try {
                str = new FileOutputStream(mBaseName);
            } catch (FileNotFoundException e2) {
                throw new IOException("Couldn't create " + mBaseName);
            }
        }
        return str;
    
public voidtruncate()

hide
deprecated
This is not safe.

        try {
            FileOutputStream fos = new FileOutputStream(mBaseName);
            FileUtils.sync(fos);
            fos.close();
        } catch (FileNotFoundException e) {
            throw new IOException("Couldn't append " + mBaseName);
        } catch (IOException e) {
        }