Synchronization between Context.openFileInput and Context.openFileOutput
Asked Answered
F

1

7

I have an Android Service running daily which does some data synchronization. Once a day it downloads a file and caches it to disk via context.openFileOutput:

String fileName = Uri.parse(url).getLastPathSegment();
try (FileOutputStream outputStream =
     context.openFileOutput(fileName, Context.MODE_PRIVATE)) {
   outputStream.write(bytes);
   // ...
} catch (IOException e) {
   // logging ...
}

This happens on a background thread. I also have a UI which contains a WebView. The WebView uses those cached resources if they are available via context.openFileInput:

@Override
public WebResourceResponse shouldInterceptRequest(
    WebView view, WebResourceRequest request) {
  String url = request.getUrl().toString();
  if (shouldUseCache(url)) {
     try {
        return new WebResourceResponse(
             "video/webm",
             Charsets.UTF_8.name(),
             context.openFileInput(obtainFileName(url)));
     } catch (IOException e) {
       // If a cached resource fails to load, just let the WebView load it the normal way.
       // logging ...
     }
  }
  return super.shouldInterceptRequest(view, request);
}

This happens on another background thread independently from the service.

Can I rely on Context implementation and be sure that file reads and writes are safe, or do I have to take care of the synchronization myself? E.g. if the Service is currently writing data to a file and the WebView is trying to access it, will I run into a problem? If so, how should I implement the synchronization?

Frazzle answered 18/6, 2018 at 16:22 Comment(4)
You can use FileObserver to reload the content into WebView if anything is modified.Horizon
You can also use ReentrantLock if you are going for handling synchronisation yourself.Horizon
@RahulKumar thanks for your reply! The thing is that shouldInterceptRequest is actually called by the web view, so it will work independently from the file observer so I don't see how it's going to help.. Regarding the lock, are you saying that I do need to implement synchronization, i.e. it's not synchronized already when I call context.openFileInput? if so, how do I share the lock if those are two different background threads? One in a service and one in a web view. This seems very complicated and I still hope that the synchronization is actually implemented by Android for me.Frazzle
As far as I'm aware these methods merely act as a convenience for constructing FileInputStream and FileOutputStream objects (See ContextImpl). So you are reliant upon the OS handling the locking of the files. As a result I'd be cautious and manually do the file locking. You could look at FileChannel (accessible from FileInputStream and FileOutputStream) and FileLock, perhaps they are sufficient.Record
L
1

If the Service is currently writing data to a file and the WebView is trying to access it, will I run into a problem?

In such cases you can write data to a file by appending something to file name and changing its name back once download is finished. e.g.

context.openFileOutput(fileName + ".downloading", Context.MODE_PRIVATE))

and later once download is finished rename the file to original fileName. I am sure you check for file presence in shouldUseCache(url) so it will keep working normally. This will avoid situations where a file is still downloading while you try to read it.

Lovegrass answered 28/6, 2018 at 4:24 Comment(1)
So far there is no reason to think that those methods are synchronized by Android so I think I have to assume they are not.. I was looking for a simple way to implement synchronization and what you suggested is a really nice option that I, for some reason, haven't think of.. I'll go this way for sure. Thank you!Frazzle

© 2022 - 2024 — McMap. All rights reserved.