Question

I am implementing the new Storage Access Framework API's into my app. Everything seems to be working well, except for one minor detail. When I use the Document picker to open a file from my own application (say from a different account within the app), openDocument on my DocumentsProvider implementation gets called on the main thread. This is fine if the requested file has been cached locally, but if it is not, then a network request is made which results in a NetworkInMainThreadException. Interestingly, the documentation mentions "It's OK to do network operations in this method to download the document". Is this a known bug and if so, does anyone know of a way around this?

Here is my code to launch the picker:

Intent target = new Intent(Intent.ACTION_OPEN_DOCUMENT); 
target.setType("*/*"); 
target.addCategory(Intent.CATEGORY_OPENABLE);
final Intent intent = Intent.createChooser(target, getString(R.string.document_choose));
try {
    startActivityForResult(intent, SELECT_FILE_REQUEST_CODE);
} catch (ActivityNotFoundException e) {
    e.printStackTrace();
}

Then when the user has selected a file, this is roughly how I handle openDocument:

@Override
public ParcelFileDescriptor openDocument(final String documentId, String mode,
        CancellationSignal signal) throws FileNotFoundException {

    final File file = getFileFromId(documentId);
    if(!file.exists()) {
        // This is where I have problems
        if("main".equalsIgnoreCase(Thread.currentThread().getName())) {
            throw new FileNotFoundException("File has not been cached locally.");
        } else {
            downloadFile(app, file, document, folder);
        }
    }
}

Notice the check for being invoked on the main thread. This doesn't happen when an external app uses my app to pick a file, as openDocument is then called on a background thread. It only happens when I am trying to pick a file from my own app (but from a different account, and therefore a different ROOT).

However, when I tried doing the same thing on Google Drive (that is, launch the app, then use its own picker to pick a file), it seemed to be able to download the file over the network without crashing the app.

Était-ce utile?

La solution 2

Ok I figured it out. The trick is openDocument is indirectly invoked (via ContentResolver) from my Activity's onActivityResult. When I called openInputStream() on the ContentResolver, I was calling it from the main thread which in turn was calling openDocument on the main thread. Moving this call to a background thread fixed the issue.

The documentation does mention this in the preceding paragraph to the code snippet:

Note that you should not do this operation on the UI thread. Do it in the background, using AsyncTask.

Autres conseils

It's not the problem of the SAF client. The DocumentsProvider should not do any network operations directly in its API methods, otherwise the client could be killed by the system. Unfortunately I could not find any useful hints in Google's official Android documentation, but fortunately there is the abandoned SMB DocumentsProvider that was started by Google but never finished. A look at its source code, especially this file repo reveals that Google uses a combination of a pipe and a reading or writing task for pre-O devices and some strange mClient.openProxyFile() for newer versions of Android. Further investigation of the SMB client source code is necessary to understand the underlying mechanisms.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top