Merge 708f9fc6d59632645ce233e6628ad7479612cd10 into 2ea148c30a07f91ffa37c0aa36af1cf2670441af
This commit is contained in:
commit
f0e2fe71a5
@ -0,0 +1,143 @@
|
|||||||
|
package io.noties.markwon.ext.latex;
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.util.LruCache;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import ru.noties.jlatexmath.JLatexMathDrawable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for JLatexMathDrawable instances to improve performance
|
||||||
|
* by avoiding redundant rendering of the same LaTeX formulas.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class JLatexMathDrawableCache {
|
||||||
|
|
||||||
|
private static final int DEFAULT_CACHE_SIZE = 32;
|
||||||
|
|
||||||
|
// Singleton instance
|
||||||
|
private static volatile JLatexMathDrawableCache instance;
|
||||||
|
|
||||||
|
// LRU cache to store rendered LaTeX drawables
|
||||||
|
private final LruCache<String, Object> cache;
|
||||||
|
|
||||||
|
// Whether to enable the cache
|
||||||
|
private final boolean enabled;
|
||||||
|
|
||||||
|
private JLatexMathDrawableCache(int maxSize, boolean enabled) {
|
||||||
|
this.cache = new LruCache<>(maxSize);
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the singleton instance of the cache
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public static JLatexMathDrawableCache getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
synchronized (JLatexMathDrawableCache.class) {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new JLatexMathDrawableCache(DEFAULT_CACHE_SIZE, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance with custom configuration
|
||||||
|
*
|
||||||
|
* @param maxSize maximum number of entries in the cache
|
||||||
|
* @param enabled whether the cache is enabled
|
||||||
|
* @return a new cache instance
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public static JLatexMathDrawableCache create(int maxSize, boolean enabled) {
|
||||||
|
return new JLatexMathDrawableCache(maxSize, enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a drawable from the cache
|
||||||
|
*
|
||||||
|
* @param key the LaTeX formula string
|
||||||
|
* @return the cached drawable or null if not found
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Cache entry containing both the drawable and its configuration
|
||||||
|
*/
|
||||||
|
private static class CacheEntry {
|
||||||
|
final Drawable drawable;
|
||||||
|
final JLatexMathDrawable.Builder builder;
|
||||||
|
|
||||||
|
CacheEntry(Drawable drawable, JLatexMathDrawable.Builder builder) {
|
||||||
|
this.drawable = drawable;
|
||||||
|
this.builder = builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Drawable get(@NonNull String key) {
|
||||||
|
if (!enabled) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (cache) {
|
||||||
|
final CacheEntry entry = (CacheEntry) cache.get(key);
|
||||||
|
if (entry != null) {
|
||||||
|
if (entry.drawable instanceof JLatexMathDrawable && entry.builder != null) {
|
||||||
|
// Recreate the drawable with the same configuration
|
||||||
|
return entry.builder.build();
|
||||||
|
}
|
||||||
|
return entry.drawable;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a drawable in the cache
|
||||||
|
*
|
||||||
|
* @param key the LaTeX formula string
|
||||||
|
* @param drawable the drawable to cache
|
||||||
|
*/
|
||||||
|
public void put(@NonNull String key, @NonNull Drawable drawable) {
|
||||||
|
put(key, drawable, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a drawable in the cache with its builder for recreation
|
||||||
|
*
|
||||||
|
* @param key the LaTeX formula string
|
||||||
|
* @param drawable the drawable to cache
|
||||||
|
* @param builder the builder used to create the drawable
|
||||||
|
*/
|
||||||
|
public void put(@NonNull String key, @NonNull Drawable drawable, @Nullable JLatexMathDrawable.Builder builder) {
|
||||||
|
if (!enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (cache) {
|
||||||
|
cache.put(key, new CacheEntry(drawable, builder));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the cache
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
synchronized (cache) {
|
||||||
|
cache.evictAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current size of the cache
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
synchronized (cache) {
|
||||||
|
return cache.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -359,16 +359,51 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
|||||||
private final Config config;
|
private final Config config;
|
||||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||||
private final Map<AsyncDrawable, Future<?>> cache = new HashMap<>(3);
|
private final Map<AsyncDrawable, Future<?>> cache = new HashMap<>(3);
|
||||||
|
private final JLatexMathDrawableCache drawableCache;
|
||||||
JLatextAsyncDrawableLoader(@NonNull Config config) {
|
JLatextAsyncDrawableLoader(@NonNull Config config) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
this.drawableCache = config.cacheEnabled
|
||||||
|
? JLatexMathDrawableCache.create(config.cacheSize, true)
|
||||||
|
: JLatexMathDrawableCache.create(0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void load(@NonNull final AsyncDrawable drawable) {
|
public void load(@NonNull final AsyncDrawable drawable) {
|
||||||
|
|
||||||
// this method must be called from main-thread only (thus synchronization can be skipped)
|
// this method must be called from main-thread only (thus synchronization can be skipped)
|
||||||
|
|
||||||
|
final String latex = drawable.getDestination();
|
||||||
|
|
||||||
|
// Check the cache first
|
||||||
|
final Drawable cachedDrawable = drawableCache.get(latex);
|
||||||
|
if (cachedDrawable != null) {
|
||||||
|
// Use cached drawable immediately
|
||||||
|
drawable.setResult(cachedDrawable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If synchronous rendering is enabled, render immediately on the main thread
|
||||||
|
if (config.syncRendering) {
|
||||||
|
try {
|
||||||
|
final JLatextAsyncDrawable jLatextAsyncDrawable = (JLatextAsyncDrawable) drawable;
|
||||||
|
final JLatexMathDrawable result;
|
||||||
|
|
||||||
|
if (jLatextAsyncDrawable.isBlock()) {
|
||||||
|
result = createBlockDrawable(jLatextAsyncDrawable);
|
||||||
|
} else {
|
||||||
|
result = createInlineDrawable(jLatextAsyncDrawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache the result
|
||||||
|
drawableCache.put(latex, result);
|
||||||
|
|
||||||
|
// Set the result immediately
|
||||||
|
drawable.setResult(result);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
handleRenderingError(drawable, t);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// check for currently running tasks associated with provided drawable
|
// check for currently running tasks associated with provided drawable
|
||||||
final Future<?> future = cache.get(drawable);
|
final Future<?> future = cache.get(drawable);
|
||||||
|
|
||||||
@ -376,7 +411,6 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
|||||||
// as asyncDrawable is immutable, it won't have destination changed (so there is no need
|
// as asyncDrawable is immutable, it won't have destination changed (so there is no need
|
||||||
// to cancel any started tasks)
|
// to cancel any started tasks)
|
||||||
if (future == null) {
|
if (future == null) {
|
||||||
|
|
||||||
cache.put(drawable, config.executorService.submit(new Runnable() {
|
cache.put(drawable, config.executorService.submit(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@ -384,6 +418,30 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
|||||||
try {
|
try {
|
||||||
execute();
|
execute();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
|
handleRenderingError(drawable, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void execute() {
|
||||||
|
final JLatexMathDrawable jLatexMathDrawable;
|
||||||
|
final JLatextAsyncDrawable jLatextAsyncDrawable = (JLatextAsyncDrawable) drawable;
|
||||||
|
|
||||||
|
if (jLatextAsyncDrawable.isBlock()) {
|
||||||
|
jLatexMathDrawable = createBlockDrawable(jLatextAsyncDrawable);
|
||||||
|
} else {
|
||||||
|
jLatexMathDrawable = createInlineDrawable(jLatextAsyncDrawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache the result
|
||||||
|
drawableCache.put(latex, jLatexMathDrawable);
|
||||||
|
|
||||||
|
setResult(drawable, jLatexMathDrawable);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRenderingError(@NonNull AsyncDrawable drawable, @NonNull Throwable t) {
|
||||||
// @since 4.3.0 add error handling
|
// @since 4.3.0 add error handling
|
||||||
final ErrorHandler errorHandler = config.errorHandler;
|
final ErrorHandler errorHandler = config.errorHandler;
|
||||||
if (errorHandler == null) {
|
if (errorHandler == null) {
|
||||||
@ -404,25 +462,7 @@ public class JLatexMathPlugin extends AbstractMarkwonPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void execute() {
|
|
||||||
|
|
||||||
final JLatexMathDrawable jLatexMathDrawable;
|
|
||||||
|
|
||||||
final JLatextAsyncDrawable jLatextAsyncDrawable = (JLatextAsyncDrawable) drawable;
|
|
||||||
|
|
||||||
if (jLatextAsyncDrawable.isBlock()) {
|
|
||||||
jLatexMathDrawable = createBlockDrawable(jLatextAsyncDrawable);
|
|
||||||
} else {
|
|
||||||
jLatexMathDrawable = createInlineDrawable(jLatextAsyncDrawable);
|
|
||||||
}
|
|
||||||
|
|
||||||
setResult(drawable, jLatexMathDrawable);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cancel(@NonNull AsyncDrawable drawable) {
|
public void cancel(@NonNull AsyncDrawable drawable) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user