1

In the app I am working on right now, part of the functionality is to write data saved on the device to a flash drive connected via a USB-OTG adapter. Specifically, the device is a rooted Motorola Xoom running 4.2.2. I can successfully write files to the drive and read them on my computer. That part works fine. However, when I try to replace existing files with new information, the resulting files come out empty. I even delete the existing files before writing new data. What's weird is that after copying the contents of my internal file to the flash drive, I log the length of the resulting file. It always matches the input file and is always a non-0 number, yet the file still shows up as blank on my computer. Can anyone help with this problem? Relevant code from the AsyncTask that I have doing this work is below.

@Override
protected Void doInBackground(Void... params) {

    File[] files = context.getFilesDir().listFiles();

    for (File file : files) {
        if (file.isFile()) {
            List<String> nameSegments = Arrays.asList(file.getName().split(
                    "_"));

            Log.d("source file", "size: " + file.length());

            String destinationPath = "/storage/usbdisk0/"
                    + nameSegments.get(0) + "/" + nameSegments.get(1) + "/";

            File destinationPathFile = new File(destinationPath);

            if (!destinationPathFile.mkdirs()) {
                destinationPathFile.mkdirs();
            }

            File destinationFile = new File(destinationPathFile,
                    nameSegments.get(2));

            FileReader fr = null;
            FileWriter fw = null;
            try {
                fr = new FileReader(file);
                fw = new FileWriter(destinationFile, false);
                int c = fr.read();
                while (c != -1) {
                    fw.write(c);
                    c = fr.read();
                }
                fw.flush();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    fr.close();
                    fw.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            Log.d("destination file", "size: " + new File(destinationFile.getPath()).length());
        }
    }
    return null;
}

EDIT: Per @Simon's suggestion, I added output.flush() to my code. This does not change the result.

EDIT #2: I did some further testing with this and found something interesting. If I go to Settings->Storage->Unmount USB Storage after writing to the flash drive but before removing it from the OTG adapter, everything works perfectly. However, failing to eject the drive after writing results in the data not being written. What's strange is that the folder structure and file itself are created on the drive, but the file is always empty. One more thing: if I go to a file manager application and open up the file prior to removing the drive, the files all exist as they should. However, even removing the device, plugging it straight back in to the tablet and opening any of the files results in the file looking empty. I can't make heads or tails of this, and this is incredibly frustrating. Can anyone help with this?

EDIT #3: I also changed to using FileReaders and FileWriters just to wee what would happen. I don't care about efficiency at this point, I simply want file writing to work reliably. This change did not affect the issue. Updated code is posted above.

Nathan Walters
  • 4,116
  • 4
  • 23
  • 37
  • Where do you `flush()` the output stream before closing it? – Simon Dec 30 '13 at 19:16
  • It works just fine as a Java app, maybe something related to the file system? – StoopidDonut Dec 30 '13 at 19:59
  • That's actually very possible. I just added some code to create a blank file in the root of the flash drive and then delete it; `delete()` returned true but the file was still present. This could be part of the problem. – Nathan Walters Dec 30 '13 at 20:09
  • Your log statement to print the length of the file is wrong. You are always printing the length of the file path, instead of printing the length of the file contents. Try using File.length() method and see if you get 0 for your contents. Hope this helps – Arunkumar Jan 03 '14 at 04:22
  • Could you specify where exactly that error is? As far as I can tell, I always call length() on File objects. – Nathan Walters Jan 03 '14 at 04:25
  • My bad, I mislooked you log statement. Your log is correct – Arunkumar Jan 03 '14 at 04:28

4 Answers4

0

It sounds like the filesystem is caching your changes, but not actually writing them to the flash drive until you eject it. I don't think there's a way to flush the filesystem cache, so the best solution seems to be just to unmount and then remount the flash drive.

Gavin S. Yancey
  • 1,216
  • 1
  • 13
  • 34
  • That answer doesn't explain how to unmount and mount something; in fact, that is impossible to do programmatically in Android due to security concerns. However I expect your explanation is right, that's what I'm beginning to think now. The question remains, why does it behave this way and how can I get around it? – Nathan Walters Jan 03 '14 at 00:37
  • My bad, I thought it did. – Gavin S. Yancey Jan 03 '14 at 03:58
  • It seems like [this answer](http://stackoverflow.com/a/9861335/1294423) might help – Gavin S. Yancey Jan 03 '14 at 04:15
  • if you [search google for "unmount and remount programatically android"](http://www.google.com/search?q=unmount+and+remount+programatically+android&oq=unmount+and+remount+programatically+android) you get a lot of people asking for the same thing. Something there might help. I'm not an android developer, so I can't really tell. – Gavin S. Yancey Jan 03 '14 at 04:21
  • Thank you for the references, I will look them over tomorrow. – Nathan Walters Jan 03 '14 at 04:27
0

Try using FileReader.ready() method before your FileReader.read() call, and ensure if your FileReader really has some bytes in it.

Arunkumar
  • 3,812
  • 3
  • 22
  • 30
  • I know the FileReader is working properly because, as I mentioned in my question, I can see the contents of the file in a file explorer after writing it. However, that sounds like a good safety check to have; I will add it. – Nathan Walters Jan 03 '14 at 04:28
  • That was how I originally did it; it made no difference. – Nathan Walters Jan 03 '14 at 04:46
0

Try this , Used buffered reader for writing

try
{
  fw = new FileWriter(destinationFile);
  BufferedWriter writer=new BufferedWriter(fw);
  writer.append(yourText); // Append can be changed to write or something if you want to overwrite
  writer.close();
} 
catch (Exception e) {
  throw new RuntimeException(e);
} 
finally {
  if (fw != null) {
try {
  fw.flush();
  fw.close();
} 
 catch (IOException e) {
}
Viswanath Lekshmanan
  • 9,945
  • 1
  • 40
  • 64
0

I found the solution to my problem. It appears that the Android system buffers some files off of the SD card/flash drive, and then writes them to the flash drive upon eject. The following code after my file operations synchronizes the buffer with the filesystem and allows the flash drive to be immediately removed from the device without data loss. It's worth noting that this DOES require root access; it will not work on a non-rooted device.

try {
    Process p = Runtime.getRuntime().exec("su");
    DataOutputStream os = new DataOutputStream(p.getOutputStream());
    os.writeBytes("sync; sync\n");
    os.writeBytes("exit\n");
    os.flush();
} catch (Exception e) {
    e.printStackTrace();
}

Source of my solution: Android 2.1 programatically unmount SDCard

Community
  • 1
  • 1
Nathan Walters
  • 4,116
  • 4
  • 23
  • 37