From b55b1f0dccd595a553d9c6cc56ef6e25eff5d99f Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Tue, 14 Jan 2020 16:05:28 +0300 Subject: [PATCH] Reduce number of invalidations in AsyncDrawable --- CHANGELOG.md | 2 + app/src/main/AndroidManifest.xml | 1 - .../noties/markwon/app/MarkdownRenderer.java | 4 +- .../noties/markwon/image/AsyncDrawable.java | 68 +++++++++++++++++-- .../markwon/image/AsyncDrawableSpan.java | 8 +-- 5 files changed, 70 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2445fff3..ecce3fae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ * Fix SpannableBuilder `subSequence` method * Introduce Nougat check in `BulletListItemSpan` to position bullet (for bullets to be positioned correctly when nested inside other `LeadingMarginSpan`s) +* Reduced number of invalidations in AsyncDrawable when result is ready +* AsyncDrawable#hasKnownDimentions -> AsyncDrawable#hasKnownDimensions typo fix # 4.2.0 * `MarkwonEditor` to highlight markdown input whilst editing (new module: `markwon-editor`) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9e5a6805..311a39e2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,7 +4,6 @@ package="io.noties.markwon.app"> - 0; } /** - * @see #hasKnownDimentions() + * @since 4.2.1-SNAPSHOT + */ + @SuppressWarnings({"unused", "WeakerAccess"}) + public boolean hasKnownDimensions() { + return canvasWidth > 0; + } + + /** + * @see #hasKnownDimensions() * @since 4.0.0 */ public int getLastKnownCanvasWidth() { @@ -84,9 +98,10 @@ public class AsyncDrawable extends Drawable { } /** - * @see #hasKnownDimentions() + * @see #hasKnownDimensions() * @since 4.0.0 */ + @SuppressWarnings("WeakerAccess") public float getLastKnowTextSize() { return textSize; } @@ -95,6 +110,7 @@ public class AsyncDrawable extends Drawable { return result; } + @SuppressWarnings("WeakerAccess") public boolean hasResult() { return result != null; } @@ -104,10 +120,17 @@ public class AsyncDrawable extends Drawable { } // yeah - public void setCallback2(@Nullable Callback callback) { + @SuppressWarnings("WeakerAccess") + public void setCallback2(@Nullable Callback cb) { - this.callback = callback; - super.setCallback(callback); + // @since 4.2.1-SNAPSHOT + // wrap callback so invalidation happens to this AsyncDrawable instance + // and not for wrapped result/placeholder + this.callback = cb == null + ? null + : new WrappedCallback(cb); + + super.setCallback(cb); // if not null -> means we are attached if (callback != null) { @@ -138,6 +161,7 @@ public class AsyncDrawable extends Drawable { /** * @since 3.0.1 */ + @SuppressWarnings("WeakerAccess") protected void setPlaceholderResult(@NonNull Drawable placeholder) { // okay, if placeholder has bounds -> use it, otherwise use original imageSize @@ -175,7 +199,6 @@ public class AsyncDrawable extends Drawable { } this.result = result; - this.result.setCallback(callback); initBounds(); } @@ -210,6 +233,12 @@ public class AsyncDrawable extends Drawable { final Rect bounds = resolveBounds(); result.setBounds(bounds); + // @since 4.2.1-SNAPSHOT, we set callback after bounds are resolved + // to reduce number of invalidations + result.setCallback(callback); + + // so, this method will check if there is previous bounds and call invalidate _BEFORE_ + // applying new bounds. This is why it is important to have initial bounds empty. setBounds(bounds); invalidateSelf(); @@ -291,6 +320,7 @@ public class AsyncDrawable extends Drawable { return imageSizeResolver.resolveImageSize(this); } + @NonNull @Override public String toString() { return "AsyncDrawable{" + @@ -302,4 +332,30 @@ public class AsyncDrawable extends Drawable { ", waitingForDimensions=" + waitingForDimensions + '}'; } + + // @since 4.2.1-SNAPSHOT + // Wrapped callback to trigger invalidation for this AsyncDrawable instance (and not result/placeholder) + private class WrappedCallback implements Callback { + + private final Callback callback; + + WrappedCallback(@NonNull Callback callback) { + this.callback = callback; + } + + @Override + public void invalidateDrawable(@NonNull Drawable who) { + callback.invalidateDrawable(AsyncDrawable.this); + } + + @Override + public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) { + callback.scheduleDrawable(AsyncDrawable.this, what, when); + } + + @Override + public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) { + callback.unscheduleDrawable(AsyncDrawable.this, what); + } + } } diff --git a/markwon-core/src/main/java/io/noties/markwon/image/AsyncDrawableSpan.java b/markwon-core/src/main/java/io/noties/markwon/image/AsyncDrawableSpan.java index 0d70b62a..7c979e23 100644 --- a/markwon-core/src/main/java/io/noties/markwon/image/AsyncDrawableSpan.java +++ b/markwon-core/src/main/java/io/noties/markwon/image/AsyncDrawableSpan.java @@ -42,11 +42,9 @@ public class AsyncDrawableSpan extends ReplacementSpan { this.alignment = alignment; this.replacementTextIsLink = replacementTextIsLink; - // additionally set intrinsic bounds if empty - final Rect rect = drawable.getBounds(); - if (rect.isEmpty()) { - drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); - } + // @since 4.2.1-SNAPSHOT we do not set intrinsic bounds + // at this point they will always be 0,0-1,1, but this + // will trigger another invalidation when we will have bounds } @Override