mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
refactor: migrate BlobProvider.java to Kotlin (#50756)
Summary: This PR aims to migrate BlobProvider from Java to kotlin as part of [50513](https://github.com/facebook/react-native/issues/50513) ## Changelog: [ANDROID][CHANGED] – Migrate BlobProvider to Kotlin Pull Request resolved: https://github.com/facebook/react-native/pull/50756 Test Plan: Tested on RN tester with both new and old arch Reviewed By: rshest Differential Revision: D73420896 Pulled By: cortinico fbshipit-source-id: 4c9a26452a7e45a78c9698f699822b4ac855628c
This commit is contained in:
committed by
Facebook GitHub Bot
parent
1fb04e2134
commit
5d1febf7de
-131
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.modules.blob;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.infer.annotation.Nullsafe;
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@Nullsafe(Nullsafe.Mode.LOCAL)
|
||||
public final class BlobProvider extends ContentProvider {
|
||||
|
||||
private static final int PIPE_CAPACITY = 65536;
|
||||
|
||||
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Cursor query(
|
||||
Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getType(Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Uri insert(Uri uri, ContentValues values) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ParcelFileDescriptor openFile(Uri uri, String mode)
|
||||
throws FileNotFoundException {
|
||||
if (!mode.equals("r")) {
|
||||
throw new FileNotFoundException("Cannot open " + uri.toString() + " in mode '" + mode + "'");
|
||||
}
|
||||
|
||||
BlobModule blobModule = null;
|
||||
Context context = getContext().getApplicationContext();
|
||||
if (context instanceof ReactApplication) {
|
||||
ReactNativeHost host = ((ReactApplication) context).getReactNativeHost();
|
||||
ReactContext reactContext = host.getReactInstanceManager().getCurrentReactContext();
|
||||
if (reactContext == null) {
|
||||
throw new RuntimeException("No ReactContext associated with BlobProvider");
|
||||
}
|
||||
blobModule = reactContext.getNativeModule(BlobModule.class);
|
||||
}
|
||||
|
||||
if (blobModule == null) {
|
||||
throw new RuntimeException("No blob module associated with BlobProvider");
|
||||
}
|
||||
|
||||
final byte[] data = blobModule.resolve(uri);
|
||||
if (data == null) {
|
||||
throw new FileNotFoundException("Cannot open " + uri + ", blob not found.");
|
||||
}
|
||||
|
||||
ParcelFileDescriptor[] pipe;
|
||||
try {
|
||||
pipe = ParcelFileDescriptor.createPipe();
|
||||
} catch (IOException exception) {
|
||||
return null;
|
||||
}
|
||||
ParcelFileDescriptor readSide = pipe[0];
|
||||
final ParcelFileDescriptor writeSide = pipe[1];
|
||||
|
||||
if (data.length <= PIPE_CAPACITY) {
|
||||
// If the blob length is less than or equal to pipe capacity (64 KB),
|
||||
// we can write the data synchronously to the pipe buffer.
|
||||
try (OutputStream outputStream = new ParcelFileDescriptor.AutoCloseOutputStream(writeSide)) {
|
||||
outputStream.write(data);
|
||||
} catch (IOException exception) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// For blobs larger than 64 KB, a synchronous write would fill up the whole buffer
|
||||
// and block forever, because there are no readers to empty the buffer.
|
||||
// Writing from a separate thread allows us to return the read side descriptor
|
||||
// immediately so that both writer and reader can work concurrently.
|
||||
// Reading from the pipe empties the buffer and allows the next chunks to be written.
|
||||
Runnable writer =
|
||||
new Runnable() {
|
||||
public void run() {
|
||||
try (OutputStream outputStream =
|
||||
new ParcelFileDescriptor.AutoCloseOutputStream(writeSide)) {
|
||||
outputStream.write(data);
|
||||
} catch (IOException exception) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
};
|
||||
executor.submit(writer);
|
||||
}
|
||||
|
||||
return readSide;
|
||||
}
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.modules.blob
|
||||
|
||||
import android.content.ContentProvider
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.os.ParcelFileDescriptor
|
||||
import com.facebook.react.ReactApplication
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
public final class BlobProvider : ContentProvider() {
|
||||
private val executor: ExecutorService = Executors.newSingleThreadExecutor()
|
||||
|
||||
override fun onCreate(): Boolean = true
|
||||
|
||||
override fun query(
|
||||
uri: Uri,
|
||||
projection: Array<String>?,
|
||||
selection: String?,
|
||||
selectionArgs: Array<String>?,
|
||||
sortOrder: String?
|
||||
): Cursor? = null
|
||||
|
||||
override fun getType(uri: Uri): String? = null
|
||||
|
||||
override fun insert(uri: Uri, values: ContentValues?): Uri? = null
|
||||
|
||||
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int = 0
|
||||
|
||||
override fun update(
|
||||
uri: Uri,
|
||||
values: ContentValues?,
|
||||
selection: String?,
|
||||
selectionArgs: Array<String>?
|
||||
): Int = 0
|
||||
|
||||
@Throws(FileNotFoundException::class)
|
||||
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {
|
||||
if (mode != "r") {
|
||||
throw FileNotFoundException("Cannot open $uri in mode '$mode'")
|
||||
}
|
||||
|
||||
var blobModule: BlobModule? = null
|
||||
val context = context?.applicationContext
|
||||
if (context is ReactApplication) {
|
||||
val host = (context as ReactApplication).reactNativeHost
|
||||
val reactContext =
|
||||
host.reactInstanceManager.currentReactContext
|
||||
?: throw RuntimeException("No ReactContext associated with BlobProvider")
|
||||
blobModule = reactContext.getNativeModule(BlobModule::class.java)
|
||||
}
|
||||
|
||||
if (blobModule == null) {
|
||||
throw RuntimeException("No blob module associated with BlobProvider")
|
||||
}
|
||||
|
||||
val data =
|
||||
blobModule.resolve(uri) ?: throw FileNotFoundException("Cannot open $uri, blob not found.")
|
||||
|
||||
val pipe: Array<ParcelFileDescriptor>
|
||||
try {
|
||||
pipe = ParcelFileDescriptor.createPipe()
|
||||
} catch (exception: IOException) {
|
||||
return null
|
||||
}
|
||||
val readSide = pipe[0]
|
||||
val writeSide = pipe[1]
|
||||
|
||||
if (data.size <= PIPE_CAPACITY) {
|
||||
// If the blob length is less than or equal to pipe capacity (64 KB),
|
||||
// we can write the data synchronously to the pipe buffer.
|
||||
try {
|
||||
ParcelFileDescriptor.AutoCloseOutputStream(writeSide).use { outputStream ->
|
||||
outputStream.write(data)
|
||||
}
|
||||
} catch (exception: IOException) {
|
||||
return null
|
||||
}
|
||||
} else {
|
||||
// For blobs larger than 64 KB, a synchronous write would fill up the whole buffer
|
||||
// and block forever, because there are no readers to empty the buffer.
|
||||
// Writing from a separate thread allows us to return the read side descriptor
|
||||
// immediately so that both writer and reader can work concurrently.
|
||||
// Reading from the pipe empties the buffer and allows the next chunks to be written.
|
||||
val writer = Runnable {
|
||||
try {
|
||||
ParcelFileDescriptor.AutoCloseOutputStream(writeSide).use { outputStream ->
|
||||
outputStream.write(data)
|
||||
}
|
||||
} catch (exception: IOException) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
executor.submit(writer)
|
||||
}
|
||||
|
||||
return readSide
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val PIPE_CAPACITY = 65536
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user