Reduce number of invalidations in AsyncDrawable

This commit is contained in:
Dimitry Ivanov 2020-01-14 16:05:28 +03:00
parent 2e7d0aa46b
commit b55b1f0dcc
5 changed files with 70 additions and 13 deletions

View File

@ -4,6 +4,8 @@
* Fix SpannableBuilder `subSequence` method * Fix SpannableBuilder `subSequence` method
* Introduce Nougat check in `BulletListItemSpan` to position bullet (for bullets to be * Introduce Nougat check in `BulletListItemSpan` to position bullet (for bullets to be
positioned correctly when nested inside other `LeadingMarginSpan`s) 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 # 4.2.0
* `MarkwonEditor` to highlight markdown input whilst editing (new module: `markwon-editor`) * `MarkwonEditor` to highlight markdown input whilst editing (new module: `markwon-editor`)

View File

@ -4,7 +4,6 @@
package="io.noties.markwon.app"> package="io.noties.markwon.app">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_ALL_DOWNLOADS" />
<application <application
android:name=".App" android:name=".App"

View File

@ -25,6 +25,7 @@ import io.noties.markwon.ext.tasklist.TaskListPlugin;
import io.noties.markwon.html.HtmlPlugin; import io.noties.markwon.html.HtmlPlugin;
import io.noties.markwon.image.ImagesPlugin; import io.noties.markwon.image.ImagesPlugin;
import io.noties.markwon.image.file.FileSchemeHandler; import io.noties.markwon.image.file.FileSchemeHandler;
import io.noties.markwon.image.gif.GifMediaDecoder;
import io.noties.markwon.image.network.OkHttpNetworkSchemeHandler; import io.noties.markwon.image.network.OkHttpNetworkSchemeHandler;
import io.noties.markwon.syntax.Prism4jTheme; import io.noties.markwon.syntax.Prism4jTheme;
import io.noties.markwon.syntax.Prism4jThemeDarkula; import io.noties.markwon.syntax.Prism4jThemeDarkula;
@ -105,7 +106,8 @@ public class MarkdownRenderer {
// default-media-decoder is also added automatically // default-media-decoder is also added automatically
plugin plugin
.addSchemeHandler(OkHttpNetworkSchemeHandler.create()) .addSchemeHandler(OkHttpNetworkSchemeHandler.create())
.addSchemeHandler(FileSchemeHandler.createWithAssets(context.getAssets())); .addSchemeHandler(FileSchemeHandler.createWithAssets(context.getAssets()))
.addMediaDecoder(GifMediaDecoder.create(false));
} }
})) }))
.usePlugin(SyntaxHighlightPlugin.create(prism4j, prism4jTheme)) .usePlugin(SyntaxHighlightPlugin.create(prism4j, prism4jTheme))

View File

@ -55,6 +55,7 @@ public class AsyncDrawable extends Drawable {
/** /**
* @since 4.0.0 * @since 4.0.0
*/ */
@SuppressWarnings("WeakerAccess")
@Nullable @Nullable
public ImageSize getImageSize() { public ImageSize getImageSize() {
return imageSize; return imageSize;
@ -63,20 +64,33 @@ public class AsyncDrawable extends Drawable {
/** /**
* @since 4.0.0 * @since 4.0.0
*/ */
@SuppressWarnings("unused")
@NonNull @NonNull
public ImageSizeResolver getImageSizeResolver() { public ImageSizeResolver getImageSizeResolver() {
return imageSizeResolver; return imageSizeResolver;
} }
/** /**
* @see #hasKnownDimensions()
* @since 4.0.0 * @since 4.0.0
* @deprecated 4.2.1-SNAPSHOT
*/ */
@SuppressWarnings({"unused", "WeakerAccess"})
@Deprecated
public boolean hasKnownDimentions() { public boolean hasKnownDimentions() {
return canvasWidth > 0; return canvasWidth > 0;
} }
/** /**
* @see #hasKnownDimentions() * @since 4.2.1-SNAPSHOT
*/
@SuppressWarnings({"unused", "WeakerAccess"})
public boolean hasKnownDimensions() {
return canvasWidth > 0;
}
/**
* @see #hasKnownDimensions()
* @since 4.0.0 * @since 4.0.0
*/ */
public int getLastKnownCanvasWidth() { public int getLastKnownCanvasWidth() {
@ -84,9 +98,10 @@ public class AsyncDrawable extends Drawable {
} }
/** /**
* @see #hasKnownDimentions() * @see #hasKnownDimensions()
* @since 4.0.0 * @since 4.0.0
*/ */
@SuppressWarnings("WeakerAccess")
public float getLastKnowTextSize() { public float getLastKnowTextSize() {
return textSize; return textSize;
} }
@ -95,6 +110,7 @@ public class AsyncDrawable extends Drawable {
return result; return result;
} }
@SuppressWarnings("WeakerAccess")
public boolean hasResult() { public boolean hasResult() {
return result != null; return result != null;
} }
@ -104,10 +120,17 @@ public class AsyncDrawable extends Drawable {
} }
// yeah // yeah
public void setCallback2(@Nullable Callback callback) { @SuppressWarnings("WeakerAccess")
public void setCallback2(@Nullable Callback cb) {
this.callback = callback; // @since 4.2.1-SNAPSHOT
super.setCallback(callback); // 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 not null -> means we are attached
if (callback != null) { if (callback != null) {
@ -138,6 +161,7 @@ public class AsyncDrawable extends Drawable {
/** /**
* @since 3.0.1 * @since 3.0.1
*/ */
@SuppressWarnings("WeakerAccess")
protected void setPlaceholderResult(@NonNull Drawable placeholder) { protected void setPlaceholderResult(@NonNull Drawable placeholder) {
// okay, if placeholder has bounds -> use it, otherwise use original imageSize // 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 = result;
this.result.setCallback(callback);
initBounds(); initBounds();
} }
@ -210,6 +233,12 @@ public class AsyncDrawable extends Drawable {
final Rect bounds = resolveBounds(); final Rect bounds = resolveBounds();
result.setBounds(bounds); 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); setBounds(bounds);
invalidateSelf(); invalidateSelf();
@ -291,6 +320,7 @@ public class AsyncDrawable extends Drawable {
return imageSizeResolver.resolveImageSize(this); return imageSizeResolver.resolveImageSize(this);
} }
@NonNull
@Override @Override
public String toString() { public String toString() {
return "AsyncDrawable{" + return "AsyncDrawable{" +
@ -302,4 +332,30 @@ public class AsyncDrawable extends Drawable {
", waitingForDimensions=" + waitingForDimensions + ", 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);
}
}
} }

View File

@ -42,11 +42,9 @@ public class AsyncDrawableSpan extends ReplacementSpan {
this.alignment = alignment; this.alignment = alignment;
this.replacementTextIsLink = replacementTextIsLink; this.replacementTextIsLink = replacementTextIsLink;
// additionally set intrinsic bounds if empty // @since 4.2.1-SNAPSHOT we do not set intrinsic bounds
final Rect rect = drawable.getBounds(); // at this point they will always be 0,0-1,1, but this
if (rect.isEmpty()) { // will trigger another invalidation when we will have bounds
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
}
} }
@Override @Override