-
Notifications
You must be signed in to change notification settings - Fork 25.2k
Fix support for blobs larger than 64 KB on Android #31789
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
1b92132
2116f50
2ffbd5a
57e3173
005d4a6
82d3284
4b886e2
58c8354
f8d3b85
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -72,7 +72,7 @@ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundEx | |||||
| throw new RuntimeException("No blob module associated with BlobProvider"); | ||||||
| } | ||||||
|
|
||||||
| byte[] data = blobModule.resolve(uri); | ||||||
| final byte[] data = blobModule.resolve(uri); | ||||||
| if (data == null) { | ||||||
| throw new FileNotFoundException("Cannot open " + uri.toString() + ", blob not found."); | ||||||
| } | ||||||
|
|
@@ -84,13 +84,24 @@ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundEx | |||||
| return null; | ||||||
| } | ||||||
| ParcelFileDescriptor readSide = pipe[0]; | ||||||
| ParcelFileDescriptor writeSide = pipe[1]; | ||||||
|
|
||||||
| try (OutputStream outputStream = new ParcelFileDescriptor.AutoCloseOutputStream(writeSide)) { | ||||||
| outputStream.write(data); | ||||||
| } catch (IOException exception) { | ||||||
| return null; | ||||||
| } | ||||||
| final ParcelFileDescriptor writeSide = pipe[1]; | ||||||
|
|
||||||
| Thread writer = | ||||||
| new Thread() { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One pretty valid concern raised by @ShikaSD is that creating a new thread for each URI is a good idea can consume quite a bit of memory by accident. What do you think about going ahead with your suggestion about conditionally creating he thread when the blob is bigger than the pipe? Additionally, @ShikaSD suggested using an executor here to schedule things on a single thread.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I tried to determine the pipe capacity using int F_GETPIPE_SZ = 1032; // Linux 2.6.35+
FileDescriptor fd = writeSide.getFileDescriptor();
int pipeCapacity = Os.fcntlInt(fd, F_GETPIPE_SZ, 0);Probably we could call I've also updated the example in RNTester so now it includes two images – one smaller and one larger than 64 KB. Just to be safe, I've also checked if it works properly for an image exactly of size equal to pipe capacity. In order to generate images of arbitrary size, I've implemented a HTTP server in Flask which appends null bytes to an existing JPG image and returns the modified image in the response: import urllib
from flask import Flask, request, make_response
app = Flask(__name__)
url = 'https://upload.wikimedia.org/wikipedia/commons/thumb/9/97/The_Earth_seen_from_Apollo_17.jpg/240px-The_Earth_seen_from_Apollo_17.jpg'
original = urllib.request.urlopen(url).read()
@app.route("/image.jpg")
def image():
size = int(request.args['size'])
diff = size - len(original)
assert diff >= 0
output = original + b'\0' * diff
assert len(output) == size
response = make_response(output)
response.headers.set('Content-Type', 'image/jpeg')
return responseIn RNTester app I've replaced the URLs: <BlobImageExample
urls={[
'http://10.0.2.2:5000/image.jpg?size=65534',
'http://10.0.2.2:5000/image.jpg?size=65535',
'http://10.0.2.2:5000/image.jpg?size=65536', // max pipe capacity
'http://10.0.2.2:5000/image.jpg?size=65537',
'http://10.0.2.2:5000/image.jpg?size=65538',
]}
/>
Using the new implementation with conditional write, all images are loaded properly, so it should be safe to use the condition
Good idea! Done. |
||||||
| public void run() { | ||||||
| try (OutputStream outputStream = | ||||||
| new ParcelFileDescriptor.AutoCloseOutputStream(writeSide)) { | ||||||
| // If the blob data is larger than pipe capacity (64 KB), a synchronous write call | ||||||
| // would fill up the whole buffer and block until the bytes are read (i.e. never). | ||||||
| // Writing from a separate thread allows us to return the read side descriptor | ||||||
| // immediately so that the reader can start reading. | ||||||
| outputStream.write(data); | ||||||
| } catch (IOException exception) { | ||||||
| // no-op | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens in this situation? Should we somehow close or notify with failure? Previously, the parent method would return
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the new implementation returns a
According to Android documentation, the reader is responsible for closing the read side descriptor:
So it should be safe to return The opposite case is when the reader closes the read side descriptor on purpose, for example when unmounting an image component before it was fully loaded (i.e. partial read). In such case, the write call fails and the write side descriptor gets closed as well, so there is no resource leak.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Amazing! Thank you for the detailed explanation.
tomekzaw marked this conversation as resolved.
Outdated
|
||||||
| } | ||||||
| } | ||||||
| }; | ||||||
| writer.start(); | ||||||
|
|
||||||
| return readSide; | ||||||
| } | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| <resources> | ||
| <string name="app_name">RNTester App</string> | ||
| <string name="blob_provider_authority">com.facebook.react.uiapp.blobs</string> | ||
| </resources> |



Uh oh!
There was an error while loading. Please reload this page.