From 7ac20d59fc5226e747fe1126dfefde7c711d5840 Mon Sep 17 00:00:00 2001 From: tarsin Date: Mon, 21 Nov 2022 12:11:35 +0800 Subject: [PATCH] Use DirectByteBuffer Seems my mimalloc(I use it as Android's system allocator) doesn't like release memory to system since I replace MADV_DONTNEED with MADV_FREE https://github.com/AcmeUI/android_external_mimalloc but there is no memleak I sure --- app/src/main/cpp/archive.c | 28 +++++++++++++++---- .../main/java/com/hippo/UriArchiveAccessor.kt | 5 +++- .../gallery/ArchiveGalleryProvider.java | 20 +++++++------ app/src/main/java/com/hippo/image/Image.kt | 13 ++++++++- 4 files changed, 50 insertions(+), 16 deletions(-) diff --git a/app/src/main/cpp/archive.c b/app/src/main/cpp/archive.c index bc61cc7a6..416a0f550 100644 --- a/app/src/main/cpp/archive.c +++ b/app/src/main/cpp/archive.c @@ -250,8 +250,8 @@ Java_com_hippo_UriArchiveAccessor_openArchive(JNIEnv *env, jobject thiz, jint fd return r; } -JNIEXPORT jint JNICALL -Java_com_hippo_UriArchiveAccessor_extractToMemfd(JNIEnv *env, jobject thiz, jint index) { +JNIEXPORT jobject JNICALL +Java_com_hippo_UriArchiveAccessor_extractToByteBuffer(JNIEnv *env, jobject thiz, jint index) { EH_UNUSED(env); EH_UNUSED(thiz); archive_ctx *ctx = NULL; @@ -259,13 +259,23 @@ Java_com_hippo_UriArchiveAccessor_extractToMemfd(JNIEnv *env, jobject thiz, jint ret = archive_get_ctx(&ctx, index); if (ret) return 0; - int memfd = syscall(SYS_memfd_create, "ArchiveMemFD", 0); - ret = archive_read_data_into_fd(ctx->arc, memfd); + long long size = archive_entry_size(ctx->entry); + void *buffer = malloc(size); + if (!buffer) { + ctx->using = 0; + LOGE("Allocate buffer for decompression failed:ENOMEM"); + return 0; + } + ret = archive_read_data(ctx->arc, buffer, size); ctx->using = 0; + if (ret == size) + return (*env)->NewDirectByteBuffer(env, buffer, size); + if (ret != size) + LOGE("%s", "No enough data read, WTF?"); if (ret < 0) LOGE("%s%s", "Archive read failed:", archive_error_string(ctx->arc)); - lseek64(memfd, 0, SEEK_SET); - return memfd; + free(buffer); + return 0; } JNIEXPORT void JNICALL @@ -344,4 +354,10 @@ Java_com_hippo_UriArchiveAccessor_extractToFd(JNIEnv *env, jobject thiz, jint in archive_read_data_into_fd(ctx->arc, fd); ctx->using = 0; } +} + +JNIEXPORT void JNICALL +Java_com_hippo_UriArchiveAccessor_releaseByteBuffer(JNIEnv *env, jobject thiz, jobject buffer) { + void *addr = (*env)->GetDirectBufferAddress(env, buffer); + free(addr); } \ No newline at end of file diff --git a/app/src/main/java/com/hippo/UriArchiveAccessor.kt b/app/src/main/java/com/hippo/UriArchiveAccessor.kt index a1bfd1d05..04f1bf613 100644 --- a/app/src/main/java/com/hippo/UriArchiveAccessor.kt +++ b/app/src/main/java/com/hippo/UriArchiveAccessor.kt @@ -19,6 +19,7 @@ package com.hippo import android.content.Context import android.net.Uri +import java.nio.ByteBuffer class UriArchiveAccessor(ctx: Context, uri: Uri) { val pfd by lazy { ctx.contentResolver.openFileDescriptor(uri, "r")!! } @@ -27,7 +28,9 @@ class UriArchiveAccessor(ctx: Context, uri: Uri) { } private external fun openArchive(fd: Int, size: Long): Int - external fun extractToMemfd(index: Int): Int + external fun extractToByteBuffer(index: Int): ByteBuffer + + external fun releaseByteBuffer(buffer: ByteBuffer) external fun extractToFd(index: Int, fd: Int) external fun getFilename(index: Int): String external fun needPassword(): Boolean diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/ArchiveGalleryProvider.java b/app/src/main/java/com/hippo/ehviewer/gallery/ArchiveGalleryProvider.java index c5093fc36..49c5e29f5 100644 --- a/app/src/main/java/com/hippo/ehviewer/gallery/ArchiveGalleryProvider.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/ArchiveGalleryProvider.java @@ -39,6 +39,7 @@ import com.hippo.yorozuya.thread.PriorityThread; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.nio.ByteBuffer; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; @@ -53,7 +54,7 @@ public class ArchiveGalleryProvider extends GalleryProvider2 { public static PVLock pv = new PVLock(0); private final UriArchiveAccessor archiveAccessor; private final Stack requests = new Stack<>(); - private final LinkedHashMap streams = new LinkedHashMap<>(); + private final LinkedHashMap streams = new LinkedHashMap<>(); private final Thread[] decodeThread = new Thread[]{ new Thread(new DecodeTask()), new Thread(new DecodeTask()), @@ -299,10 +300,10 @@ public class ArchiveGalleryProvider extends GalleryProvider2 { } } - int fd = archiveAccessor.extractToMemfd(index); + ByteBuffer buffer = archiveAccessor.extractToByteBuffer(index); synchronized (streams) { - streams.put(index, fd); + streams.put(index, buffer); streams.notify(); } } @@ -314,7 +315,7 @@ public class ArchiveGalleryProvider extends GalleryProvider2 { public void run() { while (!Thread.currentThread().isInterrupted()) { int index; - int fd; + ByteBuffer buffer; synchronized (streams) { if (streams.isEmpty()) { try { @@ -325,15 +326,18 @@ public class ArchiveGalleryProvider extends GalleryProvider2 { continue; } - Iterator> iterator = streams.entrySet().iterator(); - Map.Entry entry = iterator.next(); + Iterator> iterator = streams.entrySet().iterator(); + Map.Entry entry = iterator.next(); iterator.remove(); index = entry.getKey(); - fd = entry.getValue(); + buffer = entry.getValue(); } Image image; - image = Image.decode(new FileInputStream(ParcelFileDescriptor.adoptFd(fd).getFileDescriptor()), false); // we take ownership here since this memfd is anon fd + image = Image.decode(buffer, false, () -> { + archiveAccessor.releaseByteBuffer(buffer); + return null; + }); if (image != null) { notifyPageSucceed(index, image); } else { diff --git a/app/src/main/java/com/hippo/image/Image.kt b/app/src/main/java/com/hippo/image/Image.kt index d0dcda288..8874a9766 100644 --- a/app/src/main/java/com/hippo/image/Image.kt +++ b/app/src/main/java/com/hippo/image/Image.kt @@ -30,11 +30,13 @@ import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import androidx.core.graphics.drawable.toDrawable import java.io.FileInputStream +import java.nio.ByteBuffer import java.nio.channels.FileChannel class Image private constructor( source: Source?, drawable: Drawable? = null, - val hardware: Boolean = true + val hardware: Boolean = true, + val release: () -> Unit? = {} ) { internal var mObtainedDrawable: Drawable? private var mBitmap: Bitmap? = null @@ -72,6 +74,7 @@ class Image private constructor( mObtainedDrawable = null mBitmap?.recycle() mBitmap = null + release() } private fun prepareBitmap() { @@ -155,6 +158,14 @@ class Image private constructor( return Image(src, hardware = hardware) } + @JvmStatic + fun decode(buffer: ByteBuffer, hardware: Boolean = true, release: () -> Unit? = {}): Image { + val src = ImageDecoder.createSource(buffer) + return Image(src, hardware = hardware) { + release() + } + } + @JvmStatic fun create(bitmap: Bitmap): Image { return Image(null, bitmap.toDrawable(Resources.getSystem()), false)