-
-
Notifications
You must be signed in to change notification settings - Fork 6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add CachedLayoutInflater to improve conversation render performance.
- Loading branch information
1 parent
7fd3bfa
commit ed33e04
Showing
6 changed files
with
171 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
133 changes: 133 additions & 0 deletions
133
app/src/main/java/org/thoughtcrime/securesms/util/CachedInflater.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters