3

I am creating an app that needs to read data from a file. I was initially reading it from the assets folder using a BufferedReader and an InputStreamReader but I was running into memory issues (see Android: File Reading - OutOfMemory Issue). One suggestion was to copy the data from the assets folder to the internal storage (not the SD card) and then access it via RandomAccessFile. So I looked up how to copy files from the assets to internal storage and I found 2 sources:

https://groups.google.com/forum/?fromgroups=#!topic/android-developers/RpXiMYV48Ww

http://developergoodies.blogspot.com/2012/11/copy-android-asset-to-internal-storage.html

I decided to use the code from the second one and modified it for my file. So it looks like this:

public void copyFile() {
    //Open your file in assets
    Context context = getApplicationContext();
    String destinationFile = context.getFilesDir().getPath() + File.separator + "text.txt";

    if (!new File(destinationFile).exists()) {
        try {
            copyFromAssetsToStorage(context, "text.txt", destinationFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }   
}

private void copyStream(InputStream input, OutputStream output) throws IOException {
    byte[] buffer = new byte[1024];
    int length = Input.read(buffer);
    while (length > 0) {
        output.write(buffer, 0, length);
        length = input.read(buffer);
    }
}

private void copyFromAssetsToStorage(Context context, String sourceFile, String destinationFile) throws IOException {
    InputStream inputStream = context.getAssets().open(sourceFile);
    OutputStream outputStream = new FileOutputStream(destinationFile);
    copyStream(inputStream , outputStream );
    outputStream.flush();
    outputStream.close();
    inputStream.close();
}

I am assuming that this copies the file into the app's data directory. I have not been able to test it because I would like to be able to access the file using RandomAccessFile. However, I have never done either one of these two (copying the file from assets and RandomAccessFile) so I am stuck. The work on this app has come to a standstill because this is the only thing that is preventing me from completing it.

Can anyone provide me with corrections, suggestions, and correct implementations of how to access the data using RandomAccessFile? (The data is a list of strings 4-15 characters in length on each line.)

EDIT*

private File createCacheFile(Context context, String filename){
File cacheFile = new File(context.getCacheDir(), filename);

    if (cacheFile.exists()) {
        return cacheFile ;
    }

    InputStream inputStream = null;
    FileOutputStream fileOutputStream = null;

    try {

        inputStream = context.getAssets().open(filename);
        fileOutputStream = new FileOutputStream(cacheFile);

        int bufferSize = 1024;
        byte[] buffer = new byte[bufferSize];
        int length = -1;
        while ( (length = inputStream.read(buffer)) > 0) {
            fileOutputStream.write(buffer,0,length);
        }

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    finally {
        try {
            fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    return cacheFile;
}
Community
  • 1
  • 1
lord_sneed
  • 794
  • 3
  • 12
  • 25

1 Answers1

1

1- Copy the file from assets to the cache directory

This code just for illustration, you have to do appropriate exception handling and close resources

private File createCacheFile(Context context, String filename){
  File cacheFile = new File(context.getCacheDir(), filename);

  if (cacheFile.exists()) {
      return cacheFile ;
  }


  InputStream inputStream = context.getAssets().open(filename);
  FileOutputStream fileOutputStream = new FileOutputStream(cacheFile);

  int bufferSize = 1024;
  byte[] buffer = new byte[bufferSize];
  int length = -1;
  while ( (length = inputStream.read(buffer)) > 0) {
     fileOutputStream.write(buffer,0,length);
  }

  fileOutputStream.close();
  inputStream.close();

  return cacheFile;
}

2- Open the file using RandomAccessFile

File cacheFile = createCacheFile(context, "text.txt");
RandomAccessFile randomAccessFile = new RandomAccessFile(cacheFile, "r");

// Process the file

randomAccessFile.close();    

On a side note, you should follow Java naming conventions, e.g. your method and variable name should start with small letter such as copyFromAssetsToStorage and destinationFile

Edit:

You should make a separate try/catch for each close() operation, so if one fails the other still get executed and check that they are not null

finally {
    try {
       if(fileOutputStream!=null){
          fileOutputStream.close();            
       }
    } catch (IOException e) {
        e.printStackTrace();
    }

    try {
      if(inputStream!=null){
       inputStream.close();      
      }      
    } catch (IOException e) {
        e.printStackTrace();
    }
}  
iTech
  • 18,192
  • 4
  • 57
  • 80
  • Thanks for your reply. I want to make sure I get this right. Did I correctly implement the try/catch (see edit)? There are no errors but that does not necessarily mean that it is correct. Also, do I have to do any sort of "clean up" such as clearing the cache after every use? – lord_sneed Feb 11 '13 at 01:42
  • You should define `fileOutputStream` and `inputStream` outside the `try/catch` block and close them in a `final` block after checking that they are not null (you will need another `try/catch` for each `close()` operation) – iTech Feb 11 '13 at 01:44
  • Wow...uh...ok. I am assuming this should all be on a different thread as well? Currently I have an async thread set up, so would I just have the step 2 code in the doInBackground method of the async class? – lord_sneed Feb 11 '13 at 01:47
  • Also, it complains when I attempt to the put the try after the fileOutputStream/inputStream...says it should be surrounded with a try/catch. – lord_sneed Feb 11 '13 at 01:49
  • I apologize for all of the questions. I am just trying to understand things. – lord_sneed Feb 11 '13 at 15:58
  • You can put both the code for `createCacheFile` and `RandomAccessFile ` reading in `doInBackground()`. As I mentioned in my previous comment, `close()` operation throw an exception, so you have to enclose `fileOutputStream.close();` and `fileOutputStream.close();` in a **separate** `try/catch` block so if one throw an exception still the second `close()` operation will be invoked – iTech Feb 11 '13 at 16:46
  • Almost, you have to make a separate `try/catch` block for each `close`, see my updated answer – iTech Feb 12 '13 at 00:31
  • Ahh ok...I am assuming a similar set-up will have to be done for the RandomAccessFile? – lord_sneed Feb 12 '13 at 00:39
  • Thanks for your help it is working beautifully (and no memory issues!). One last question, let's say that I update the text file and release that update to the users. Will the update take care of removing the cache file? Otherwise, users will be reading from the cache (which is now outdated), so they would have to uninstall and reinstall the app. – lord_sneed Feb 14 '13 at 19:42