refactor: use JNI Arena for automatic resource management

This commit is contained in:
Koji Wakamiya 2025-12-29 06:58:46 +09:00
parent 95f864213e
commit a0b8a68cdb
No known key found for this signature in database
1 changed files with 12 additions and 30 deletions

View File

@ -40,20 +40,13 @@ final class ImageConverterAndroid implements ImageConverterPlatform {
int quality = 100, int quality = 100,
ResizeMode resizeMode = const OriginalResizeMode(), ResizeMode resizeMode = const OriginalResizeMode(),
}) async { }) async {
JByteArray? inputJBytes; return using((arena) {
Bitmap? originalBitmap; final inputJBytes = JByteArray.from(inputData)..releasedBy(arena);
Bitmap? scaledBitmap; final originalBitmap = BitmapFactory.decodeByteArray(
Bitmap? bitmapToCompress;
Bitmap$CompressFormat? compressFormat;
ByteArrayOutputStream? outputStream;
JByteArray? outputJBytes;
try {
inputJBytes = JByteArray.from(inputData);
originalBitmap = BitmapFactory.decodeByteArray(
inputJBytes, inputJBytes,
0, 0,
inputData.length, inputData.length,
); )?..releasedBy(arena);
if (originalBitmap == null) { if (originalBitmap == null) {
throw const ImageDecodingException('Invalid image data.'); throw const ImageDecodingException('Invalid image data.');
} }
@ -65,26 +58,24 @@ final class ImageConverterAndroid implements ImageConverterPlatform {
originalHeight, originalHeight,
); );
final Bitmap? bitmapToCompress;
if (newWidth == originalWidth && newHeight == originalHeight) { if (newWidth == originalWidth && newHeight == originalHeight) {
bitmapToCompress = originalBitmap; bitmapToCompress = originalBitmap;
} else { } else {
scaledBitmap = Bitmap.createScaledBitmap( bitmapToCompress = Bitmap.createScaledBitmap(
originalBitmap, originalBitmap,
newWidth, newWidth,
newHeight, newHeight,
true, // filter true, // filter
); )?..releasedBy(arena);
bitmapToCompress = scaledBitmap;
} }
if (bitmapToCompress == null) { if (bitmapToCompress == null) {
// This should not happen if originalBitmap is valid
throw const ImageConversionException( throw const ImageConversionException(
'Bitmap could not be prepared for compression.', 'Bitmap could not be prepared for compression.',
); );
} }
compressFormat = switch (format) { final compressFormat = switch (format) {
OutputFormat.jpeg => Bitmap$CompressFormat.JPEG, OutputFormat.jpeg => Bitmap$CompressFormat.JPEG,
OutputFormat.png => Bitmap$CompressFormat.PNG, OutputFormat.png => Bitmap$CompressFormat.PNG,
// TODO: WebP is deprecated since Android 10, consider using WebP_LOSSY or WebP_LOSSLESS // TODO: WebP is deprecated since Android 10, consider using WebP_LOSSY or WebP_LOSSLESS
@ -92,9 +83,9 @@ final class ImageConverterAndroid implements ImageConverterPlatform {
OutputFormat.heic => throw UnsupportedError( OutputFormat.heic => throw UnsupportedError(
'HEIC output format is not supported on Android.', 'HEIC output format is not supported on Android.',
), ),
}; }..releasedBy(arena);
outputStream = ByteArrayOutputStream(); final outputStream = ByteArrayOutputStream()..releasedBy(arena);
final success = bitmapToCompress.compress( final success = bitmapToCompress.compress(
compressFormat, compressFormat,
quality, quality,
@ -104,7 +95,7 @@ final class ImageConverterAndroid implements ImageConverterPlatform {
throw ImageEncodingException(format, 'Failed to compress bitmap.'); throw ImageEncodingException(format, 'Failed to compress bitmap.');
} }
outputJBytes = outputStream.toByteArray(); final outputJBytes = outputStream.toByteArray()?..releasedBy(arena);
if (outputJBytes == null) { if (outputJBytes == null) {
throw ImageEncodingException( throw ImageEncodingException(
format, format,
@ -113,15 +104,6 @@ final class ImageConverterAndroid implements ImageConverterPlatform {
} }
return Uint8List.fromList(outputJBytes.toList()); return Uint8List.fromList(outputJBytes.toList());
} finally { });
inputJBytes?.release();
originalBitmap?.recycle();
originalBitmap?.release();
scaledBitmap?.recycle();
scaledBitmap?.release();
compressFormat?.release();
outputStream?.release();
outputJBytes?.release();
}
} }
} }