diff --git a/modules/android/pool/src/main/java/com/openhippy/pool/ImageDataPool.java b/modules/android/pool/src/main/java/com/openhippy/pool/ImageDataPool.java index b6ac448490b..af5258655b5 100644 --- a/modules/android/pool/src/main/java/com/openhippy/pool/ImageDataPool.java +++ b/modules/android/pool/src/main/java/com/openhippy/pool/ImageDataPool.java @@ -22,7 +22,7 @@ public class ImageDataPool extends BasePool { - private static final int DEFAULT_IMAGE_POOL_SIZE = 24; + private static final int DEFAULT_IMAGE_POOL_SIZE = 16; private LruCache mPools; public ImageDataPool() { diff --git a/modules/vfs/android/src/main/java/com/tencent/vfs/ResourceDataHolder.java b/modules/vfs/android/src/main/java/com/tencent/vfs/ResourceDataHolder.java index 57cdb91c0b7..813b4d668af 100644 --- a/modules/vfs/android/src/main/java/com/tencent/vfs/ResourceDataHolder.java +++ b/modules/vfs/android/src/main/java/com/tencent/vfs/ResourceDataHolder.java @@ -16,6 +16,7 @@ package com.tencent.vfs; +import android.graphics.Bitmap; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -48,6 +49,8 @@ public enum TransferType { @Nullable public byte[] bytes; @Nullable + public Bitmap bitmap; + @Nullable public HashMap requestHeaders; @Nullable public HashMap requestParams; @@ -104,6 +107,7 @@ public static ResourceDataHolder obtain() { public void recycle() { buffer = null; bytes = null; + bitmap = null; callback = null; errorMessage = null; processorTag = null; diff --git a/renderer/native/android/src/main/java/androidx/recyclerview/widget/HippyOverPullHelper.java b/renderer/native/android/src/main/java/androidx/recyclerview/widget/HippyOverPullHelper.java index 85d1e59a615..7fa4d26c291 100644 --- a/renderer/native/android/src/main/java/androidx/recyclerview/widget/HippyOverPullHelper.java +++ b/renderer/native/android/src/main/java/androidx/recyclerview/widget/HippyOverPullHelper.java @@ -128,7 +128,6 @@ private boolean checkOverDrag(MotionEvent event) { setOverPullState(OVER_PULL_UP_ING); } Number deltaY = (event.getRawY() - lastRawY) / 3.0f; - LogUtils.e("maxli", "checkOverDrag: deltaY " + deltaY + ", offset " + offset); recyclerView.offsetChildrenVertical(deltaY.intValue()); if (overPullListener != null) { overPullListener.onOverPullStateChanged(overPullState, overPullState, diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/component/image/ImageDataHolder.java b/renderer/native/android/src/main/java/com/tencent/renderer/component/image/ImageDataHolder.java index 73fd8dec0e2..ac95a4761ac 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/component/image/ImageDataHolder.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/component/image/ImageDataHolder.java @@ -19,7 +19,6 @@ import static com.tencent.renderer.NativeRenderException.ExceptionCode.IMAGE_DATA_DECODE_ERR; import android.graphics.ImageDecoder; -import android.graphics.drawable.Animatable; import android.os.Build.VERSION_CODES; import androidx.annotation.RequiresApi; @@ -45,7 +44,6 @@ import java.nio.ByteBuffer; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; public class ImageDataHolder extends ImageRecycleObject implements ImageDataSupplier { @@ -61,6 +59,11 @@ public class ImageDataHolder extends ImageRecycleObject implements ImageDataSupp * Mark that image data is decoded internally and needs to recycle. */ private static final int FLAG_RECYCLABLE = 0x00000004; + /** + * Mark that image bitmap is provided by host, do not need cache. + */ + private static final int FLAG_CACHEABLE = 0x00000008; + private int mStateFlags = 0; private int mWidth; private int mHeight; @@ -77,23 +80,34 @@ public class ImageDataHolder extends ImageRecycleObject implements ImageDataSupp private BitmapFactory.Options mOptions; public ImageDataHolder(@NonNull String source) { - init(source, null, 0, 0); + init(source, null, null, 0, 0); } public ImageDataHolder(@NonNull String source, int width, int height) { - init(source, null, width, height); + init(source, null, null, width, height); + } + + public ImageDataHolder(@NonNull String source, @Nullable Bitmap bitmap, int width, int height) { + init(source, null, bitmap, width, height); } public ImageDataHolder(@NonNull String source, @NonNull ImageDataKey key, int width, int height) { - init(source, key, width, height); + init(source, key, null, width, height); + } + + public ImageDataHolder(@NonNull String source, @NonNull ImageDataKey key, @Nullable Bitmap bitmap, int width, + int height) { + init(source, key, bitmap, width, height); } - public void init(@NonNull String source, @Nullable ImageDataKey key, int width, int height) { + public void init(@NonNull String source, @Nullable ImageDataKey key, @Nullable Bitmap bitmap, int width, + int height) { mSource = source; mWidth = width; mHeight = height; mKey = (key == null) ? new ImageDataKey(source) : key; + mBitmap = bitmap; } @Nullable @@ -172,6 +186,9 @@ public Movie getGifMovie() { @Override public boolean isScraped() { + if (!checkStateFlag(FLAG_CACHEABLE)) { + return mBitmap == null || mBitmap.isRecycled(); + } if (mOptions == null) { return true; } @@ -194,14 +211,33 @@ public boolean isRecyclable() { return checkStateFlag(FLAG_RECYCLABLE); } + @Override + public boolean isCacheable() { + return checkStateFlag(FLAG_CACHEABLE); + } + @Override public int getImageWidth() { - return (mOptions != null) ? mOptions.outWidth : 0; + if (mOptions != null) { + return mOptions.outWidth; + } else if (mBitmap != null) { + return mBitmap.getWidth(); + } else if (mGifMovie != null) { + return mGifMovie.width(); + } + return 0; } @Override public int getImageHeight() { - return (mOptions != null) ? mOptions.outHeight : 0; + if (mOptions != null) { + return mOptions.outHeight; + } else if (mBitmap != null) { + return mBitmap.getHeight(); + } else if (mGifMovie != null) { + return mGifMovie.height(); + } + return 0; } @Override @@ -216,7 +252,7 @@ public int getLayoutHeight() { @Override public boolean isAnimated() { - return mOptions != null && ImageDataUtils.isGif(mOptions); + return ImageDataUtils.isGif(mOptions); } @Nullable @@ -256,6 +292,7 @@ public void decodeImageData(@NonNull byte[] data, @Nullable Map if (imageDecoderAdapter != null) { imageDecoderAdapter.afterDecode(initProps, this, mOptions); } + setStateFlag(FLAG_CACHEABLE); } catch (OutOfMemoryError | Exception e) { throw new NativeRenderException(IMAGE_DATA_DECODE_ERR, e.getMessage()); } @@ -275,9 +312,8 @@ public void setBitmap(Bitmap bitmap) { * Decode image data with ImageDecoder. * *

- * Warning! AnimatedImageDrawable start will cause crash in some android platform when use - * ImageDecoder createSource API with ByteBuffer. Therefore, AnimatedImageDrawable is not - * supported at present. + * Warning! AnimatedImageDrawable start will cause crash in some android platform when use ImageDecoder createSource + * API with ByteBuffer. Therefore, AnimatedImageDrawable is not supported at present. *

*/ @RequiresApi(api = VERSION_CODES.P) @@ -290,7 +326,8 @@ private Drawable decodeGifForTarget28(@NonNull byte[] data) throws IOException { // will work around the issue ImageDecoder.Source source = ImageDecoder.createSource(ByteBuffer.wrap(data)); return ImageDecoder.decodeDrawable(source, - (decoder, info, source1) -> {}); + (decoder, info, source1) -> { + }); } @RequiresApi(api = VERSION_CODES.P) diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/component/image/ImageDataSupplier.java b/renderer/native/android/src/main/java/com/tencent/renderer/component/image/ImageDataSupplier.java index 3aaa950aea9..d8c980368f6 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/component/image/ImageDataSupplier.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/component/image/ImageDataSupplier.java @@ -39,6 +39,8 @@ public interface ImageDataSupplier { boolean isRecyclable(); + boolean isCacheable(); + boolean isAnimated(); @NonNull diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/component/image/ImageLoader.java b/renderer/native/android/src/main/java/com/tencent/renderer/component/image/ImageLoader.java index 28ccb2308be..493a0b845e8 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/component/image/ImageLoader.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/component/image/ImageLoader.java @@ -64,7 +64,9 @@ public ImageDataSupplier getImageFromCache(@NonNull String url) { @Override public void saveImageToCache(@NonNull ImageDataSupplier data) { - mImagePool.release((ImageDataHolder) data); + if (data.isCacheable()) { + mImagePool.release((ImageDataHolder) data); + } } private void doListenerCallback(@NonNull final ImageRequestListener listener, @@ -81,16 +83,13 @@ private Runnable generateCallbackRunnable(final ImageDataKey urlKey, @Nullable final ImageDataHolder imageHolder, @Nullable String errorMessage) { final String error = (errorMessage != null) ? errorMessage : ""; - return new Runnable() { - @Override - public void run() { - ArrayList listeners = mListenersMap.get(urlKey); - mListenersMap.remove(urlKey); - if (listeners != null && listeners.size() > 0) { - for (ImageRequestListener listener : listeners) { - if (listener != null) { - doListenerCallback(listener, imageHolder, error); - } + return () -> { + ArrayList listeners = mListenersMap.get(urlKey); + mListenersMap.remove(urlKey); + if (listeners != null && listeners.size() > 0) { + for (ImageRequestListener listener : listeners) { + if (listener != null) { + doListenerCallback(listener, imageHolder, error); } } } @@ -104,20 +103,26 @@ private void handleResourceData(@NonNull String url, @NonNull final ImageDataKey String errorMessage = null; byte[] bytes = dataHolder.getBytes(); if (dataHolder.resultCode - == ResourceDataHolder.RESOURCE_LOAD_SUCCESS_CODE && bytes != null) { - imageHolder = new ImageDataHolder(url, urlKey, width, height); - try { - imageHolder.decodeImageData(bytes, initProps, mImageDecoderAdapter); - // Should check the request data returned from the host, if the data is - // invalid, the request is considered to have failed - if (!imageHolder.checkImageData()) { + == ResourceDataHolder.RESOURCE_LOAD_SUCCESS_CODE) { + if (dataHolder.bitmap != null) { + imageHolder = new ImageDataHolder(url, urlKey, dataHolder.bitmap, width, height); + } else if (bytes != null) { + imageHolder = new ImageDataHolder(url, urlKey, width, height); + try { + imageHolder.decodeImageData(bytes, initProps, mImageDecoderAdapter); + // Should check the request data returned from the host, if the data is + // invalid, the request is considered to have failed + if (!imageHolder.checkImageData()) { + imageHolder = null; + errorMessage = "Image data decoding failed!"; + } + } catch (NativeRenderException e) { + e.printStackTrace(); imageHolder = null; - errorMessage = "Image data decoding failed!"; + errorMessage = e.getMessage(); } - } catch (NativeRenderException e) { - e.printStackTrace(); - imageHolder = null; - errorMessage = e.getMessage(); + } else { + errorMessage = dataHolder.errorMessage; } } else { errorMessage = dataHolder.errorMessage; @@ -133,15 +138,12 @@ private void handleResourceData(@NonNull String url, @NonNull final ImageDataKey private void handleRequestProgress(final long total, final long loaded, @NonNull final ImageDataKey urlKey) { - Runnable progressRunnable = new Runnable() { - @Override - public void run() { - ArrayList listeners = mListenersMap.get(urlKey); - if (listeners != null) { - for (ImageRequestListener listener : listeners) { - if (listener != null) { - listener.onRequestProgress(total, loaded); - } + Runnable progressRunnable = () -> { + ArrayList listeners = mListenersMap.get(urlKey); + if (listeners != null) { + for (ImageRequestListener listener : listeners) { + if (listener != null) { + listener.onRequestProgress(total, loaded); } } } @@ -169,24 +171,23 @@ public ImageDataSupplier fetchImageSync(@NonNull String url, ResourceDataHolder dataHolder = mVfsManager.fetchResourceSync(url, null, requestParams); byte[] bytes = dataHolder.getBytes(); if (dataHolder.resultCode - != ResourceDataHolder.RESOURCE_LOAD_SUCCESS_CODE || bytes == null) { + != ResourceDataHolder.RESOURCE_LOAD_SUCCESS_CODE) { return null; } - ImageDataHolder imageHolder = ImageDataHolder.obtain(); - if (imageHolder != null) { - imageHolder.init(url, null, width, height); - } else { - imageHolder = new ImageDataHolder(url, width, height); - } - try { - imageHolder.decodeImageData(bytes, initProps, mImageDecoderAdapter); - if (imageHolder.checkImageData()) { - return imageHolder; + if (dataHolder.bitmap != null) { + return new ImageDataHolder(url, dataHolder.bitmap, width, height); + } else if (bytes != null) { + ImageDataHolder imageHolder = new ImageDataHolder(url, width, height); + try { + imageHolder.decodeImageData(bytes, initProps, mImageDecoderAdapter); + if (imageHolder.checkImageData()) { + return imageHolder; + } + } catch (NativeRenderException e) { + e.printStackTrace(); + } finally { + dataHolder.recycle(); } - } catch (NativeRenderException e) { - e.printStackTrace(); - } finally { - dataHolder.recycle(); } return null; } diff --git a/renderer/native/android/src/main/java/com/tencent/renderer/utils/ImageDataUtils.java b/renderer/native/android/src/main/java/com/tencent/renderer/utils/ImageDataUtils.java index 19d213b64cb..f0c1f18485a 100644 --- a/renderer/native/android/src/main/java/com/tencent/renderer/utils/ImageDataUtils.java +++ b/renderer/native/android/src/main/java/com/tencent/renderer/utils/ImageDataUtils.java @@ -19,6 +19,7 @@ import android.graphics.BitmapFactory; import android.text.TextUtils; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; public class ImageDataUtils { @@ -36,23 +37,23 @@ public static BitmapFactory.Options generateBitmapOptions(@NonNull byte[] data) return options; } - public static boolean isWebp(@NonNull BitmapFactory.Options options) { - return !TextUtils.isEmpty(options.outMimeType) && options.outMimeType.equalsIgnoreCase( + public static boolean isWebp(@Nullable BitmapFactory.Options options) { + return options != null && !TextUtils.isEmpty(options.outMimeType) && options.outMimeType.equalsIgnoreCase( IMAGE_TYPE_WEBP); } - public static boolean isJpeg(@NonNull BitmapFactory.Options options) { - return !TextUtils.isEmpty(options.outMimeType) && options.outMimeType.equalsIgnoreCase( + public static boolean isJpeg(@Nullable BitmapFactory.Options options) { + return options != null && !TextUtils.isEmpty(options.outMimeType) && options.outMimeType.equalsIgnoreCase( IMAGE_TYPE_JPEG); } - public static boolean isPng(@NonNull BitmapFactory.Options options) { - return !TextUtils.isEmpty(options.outMimeType) && options.outMimeType.equalsIgnoreCase( + public static boolean isPng(@Nullable BitmapFactory.Options options) { + return options != null && !TextUtils.isEmpty(options.outMimeType) && options.outMimeType.equalsIgnoreCase( IMAGE_TYPE_PNG); } - public static boolean isGif(@NonNull BitmapFactory.Options options) { - return !TextUtils.isEmpty(options.outMimeType) && options.outMimeType.equalsIgnoreCase( + public static boolean isGif(@Nullable BitmapFactory.Options options) { + return options != null && !TextUtils.isEmpty(options.outMimeType) && options.outMimeType.equalsIgnoreCase( IMAGE_TYPE_GIF); }