mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-20 08:39:22 +01:00
Add CachedLayoutInflater to improve conversation render performance.
This commit is contained in:
committed by
Alex Hart
parent
7fd3bfa30c
commit
ed33e048ad
@@ -0,0 +1,133 @@
|
||||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.MainThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A class that can be used to pre-cache layouts. Usage flow:
|
||||
*
|
||||
* - At some point before you want to use the views, call {@link #cacheUntilLimit(int, ViewGroup, int)}.
|
||||
* - Later, use {@link #inflate(int, ViewGroup, boolean)}, which will prefer using cached views
|
||||
* before inflating new ones.
|
||||
*/
|
||||
public class CachedInflater {
|
||||
|
||||
private static final String TAG = Log.tag(CachedInflater.class);
|
||||
|
||||
private final Context context;
|
||||
|
||||
/**
|
||||
* Does *not* work with the application context.
|
||||
*/
|
||||
public static CachedInflater from(@NonNull Context context) {
|
||||
return new CachedInflater(context);
|
||||
}
|
||||
|
||||
private CachedInflater(@NonNull Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identical to {@link LayoutInflater#inflate(int, ViewGroup, boolean)}, but will prioritize
|
||||
* pulling a cached view first.
|
||||
*/
|
||||
@MainThread
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V extends View> V inflate(@LayoutRes int layoutRes, @Nullable ViewGroup parent, boolean attachToRoot) {
|
||||
View cached = ViewCache.getInstance().pull(layoutRes);
|
||||
if (cached != null) {
|
||||
if (parent != null && attachToRoot) {
|
||||
parent.addView(cached);
|
||||
}
|
||||
return (V) cached;
|
||||
} else {
|
||||
return (V) LayoutInflater.from(context).inflate(layoutRes, parent, attachToRoot);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will inflate as many views as necessary until the cache holds the amount you specify.
|
||||
*/
|
||||
@MainThread
|
||||
public void cacheUntilLimit(@LayoutRes int layoutRes, @Nullable ViewGroup parent, int limit) {
|
||||
ViewCache.getInstance().cacheUntilLimit(context, layoutRes, parent, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all cached views. This should be done if, for instance, the theme changes.
|
||||
*/
|
||||
@MainThread
|
||||
public void clear() {
|
||||
Log.d(TAG, "Clearing view cache.");
|
||||
ViewCache.getInstance().clear();
|
||||
}
|
||||
|
||||
private static class ViewCache {
|
||||
|
||||
private static final ViewCache INSTANCE = new ViewCache();
|
||||
|
||||
private final Map<Integer, List<View>> cache = new HashMap<>();
|
||||
|
||||
private long lastClearTime;
|
||||
|
||||
static ViewCache getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@MainThread
|
||||
void cacheUntilLimit(Context context, @LayoutRes int layoutRes, @Nullable ViewGroup parent, int limit) {
|
||||
AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
|
||||
|
||||
int existingCount = Util.getOrDefault(cache, layoutRes, Collections.emptyList()).size();
|
||||
int inflateCount = Math.max(limit - existingCount, 0);
|
||||
|
||||
for (int i = 0; i < inflateCount; i++) {
|
||||
final long enqueueTime = System.currentTimeMillis();
|
||||
inflater.inflate(layoutRes, parent, (view, resId, p) -> {
|
||||
Util.assertMainThread();
|
||||
if (enqueueTime < lastClearTime) {
|
||||
Log.d(TAG, "Prefetch is no longer valid. Ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<View> views = cache.get(resId);
|
||||
|
||||
views = views == null ? new LinkedList<>() : views;
|
||||
views.add(view);
|
||||
|
||||
cache.put(resId, views);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@MainThread
|
||||
@Nullable View pull(@LayoutRes int layoutRes) {
|
||||
List<View> views = cache.get(layoutRes);
|
||||
return views != null && !views.isEmpty() ? views.remove(0)
|
||||
: null;
|
||||
}
|
||||
|
||||
@MainThread
|
||||
void clear() {
|
||||
lastClearTime = System.currentTimeMillis();
|
||||
cache.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ public class DynamicTheme {
|
||||
OverridePendingTransition.invoke(activity);
|
||||
activity.startActivity(intent);
|
||||
OverridePendingTransition.invoke(activity);
|
||||
CachedInflater.from(activity).clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user