PSA: AssetFileDescriptor and FileDescriptor & Android raw resources

In an app I’m making I have the need to copy a file from my app to the local filesystem.  Not a complicated task but I hit snag and it doesn’t appear to be that well documented so I thought I’d share.

First of all I got an AssetFileDescriptor for my raw resource using getResources() and openRawResourceFd().  No problems so far!

Next up I saw that I could get a standard Java FileDescriptor which was handy because I would need that soon.  With that in place I created two simple methods in a util class.  Some stuff has been snipped out for brevity.

// BROKEN - DO NOT COPY!
public static void copyRaw(
  AssetFileDescriptor fd, File destinationFile
) throws IOException {
  FileChannel sourceChannel = 
    new FileInputStream(fd.getFileDescriptor()).getChannel();

  copyNIO(sourceChannel, destinationFile);
}

public static void copyNIO(
  FileChannel source, 
  File destination) throws IOException  {

  // Error checking...

  FileChannel destinationChannel = null;

  try {
    destinationChannel = 
      new FileOutputStream(destination).getChannel();

    source.transferTo(0, source.size(), destinationChannel);
  } finally {
    // Tidy up...
  }
}

The first issue that I had is a well-known one.  Android compresses resources so in order to give it a hint to not compress the file we have to give it a file extension of an already compressed format like “.mp3” or “.png”.  I can live with that.

The second issue is much harder to understand.  When I investigated what was copied to the local system I discovered that it wasn’t just the resource I had provided, but a dump of the every resource that I had in my project!

Perhaps this is working as designed but it doesn’t appear logical to me. What seems to be happening is that the AssetFileDescriptor does describe the actual file but the FileDescriptor you get from that describes the entire blob in which your specific file is contained.  Armed with this information we can make simple adjustments to the code to get it to behave more logically (at least to me).

public static void copyRaw(
  AssetFileDescriptor fd, File destinationFile
) throws IOException {
  FileChannel sourceChannel = 
    new FileInputStream(fd.getFileDescriptor()).getChannel();

  copyNIO(
    sourceChannel, destinationFile, 
    fd.getStartOffset(), fd.getLength()
  );
}

public static void copyNIO(
  FileChannel source, File destination,
  long startOffset, long length
) throws IOException  {

  FileChannel destinationChannel = null;

  try {
    destinationChannel = 
      new FileOutputStream(destination).getChannel();

    source.transferTo(startOffset, length, destinationChannel);
  } finally {
    // Tidy up...
  }
}

The difference here is to get the startOffset and length from the AssetFileDescriptor and then use them in the copy operation with the standard FileDescriptor.  Actually getting the file to then copy into internal memory is a task for another blog post.

One thought on “PSA: AssetFileDescriptor and FileDescriptor & Android raw resources

  1. Works like a charm! I wanted to copy a png from my res/drawable to the sdcard, so users can see it, edit it, rename it, and I can import the edit back into my game. This should get me started.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s