am 559d90d0: WidgetPreviewLoader concurrency issue / Caching improvement
* commit '559d90d0dafbac1d97a1e6f18062309831a25d51': WidgetPreviewLoader concurrency issue / Caching improvement
This commit is contained in:
@@ -34,7 +34,6 @@ import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
import com.android.launcher3.widget.WidgetCell;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -51,8 +50,13 @@ public class WidgetPreviewLoader {
|
||||
private static final float WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE = 0.25f;
|
||||
|
||||
private final HashMap<String, long[]> mPackageVersions = new HashMap<>();
|
||||
private final HashMap<WidgetCacheKey, WeakReference<Bitmap>> mLoadedPreviews = new HashMap<>();
|
||||
private Set<Bitmap> mUnusedBitmaps = Collections.newSetFromMap(new WeakHashMap<Bitmap, Boolean>());
|
||||
|
||||
/**
|
||||
* Weak reference objects, do not prevent their referents from being made finalizable,
|
||||
* finalized, and then reclaimed.
|
||||
*/
|
||||
private Set<Bitmap> mUnusedBitmaps =
|
||||
Collections.newSetFromMap(new WeakHashMap<Bitmap, Boolean>());
|
||||
|
||||
private final Context mContext;
|
||||
private final IconCache mIconCache;
|
||||
@@ -84,18 +88,9 @@ public class WidgetPreviewLoader {
|
||||
String size = previewWidth + "x" + previewHeight;
|
||||
WidgetCacheKey key = getObjectKey(o, size);
|
||||
|
||||
// Check if we have the preview loaded or not.
|
||||
synchronized (mLoadedPreviews) {
|
||||
WeakReference<Bitmap> ref = mLoadedPreviews.get(key);
|
||||
if (ref != null && ref.get() != null) {
|
||||
immediateResult[0] = ref.get();
|
||||
return new PreviewLoadRequest(null, key);
|
||||
}
|
||||
}
|
||||
|
||||
PreviewLoadTask task = new PreviewLoadTask(key, o, previewWidth, previewHeight, caller);
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
return new PreviewLoadRequest(task, key);
|
||||
return new PreviewLoadRequest(task);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,22 +187,6 @@ public class WidgetPreviewLoader {
|
||||
mPackageVersions.remove(packageName);
|
||||
}
|
||||
|
||||
synchronized (mLoadedPreviews) {
|
||||
Set<WidgetCacheKey> keysToRemove = new HashSet<>();
|
||||
for (WidgetCacheKey key : mLoadedPreviews.keySet()) {
|
||||
if (key.componentName.getPackageName().equals(packageName) && key.user.equals(user)) {
|
||||
keysToRemove.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
for (WidgetCacheKey key : keysToRemove) {
|
||||
WeakReference<Bitmap> req = mLoadedPreviews.remove(key);
|
||||
if (req != null && req.get() != null) {
|
||||
mUnusedBitmaps.add(req.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
mDb.getWritableDatabase().delete(CacheDb.TABLE_NAME,
|
||||
CacheDb.COLUMN_PACKAGE + " = ? AND " + CacheDb.COLUMN_USER + " = ?",
|
||||
@@ -549,26 +528,25 @@ public class WidgetPreviewLoader {
|
||||
public class PreviewLoadRequest {
|
||||
|
||||
private final PreviewLoadTask mTask;
|
||||
private final WidgetCacheKey mKey;
|
||||
|
||||
public PreviewLoadRequest(PreviewLoadTask task, WidgetCacheKey key) {
|
||||
public PreviewLoadRequest(PreviewLoadTask task) {
|
||||
mTask = task;
|
||||
mKey = key;
|
||||
}
|
||||
|
||||
public void cancel(boolean recycleImage) {
|
||||
public void cleanup() {
|
||||
if (mTask != null) {
|
||||
mTask.cancel(true);
|
||||
}
|
||||
|
||||
if (recycleImage) {
|
||||
synchronized(mLoadedPreviews) {
|
||||
WeakReference<Bitmap> result = mLoadedPreviews.remove(mKey);
|
||||
if (result != null && result.get() != null) {
|
||||
mUnusedBitmaps.add(result.get());
|
||||
}
|
||||
}
|
||||
if (mTask.mBitmap == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The preview is no longer bound to any view, move it to {@link WeakReference} list.
|
||||
synchronized(mUnusedBitmaps) {
|
||||
mUnusedBitmaps.add(mTask.mBitmap);
|
||||
}
|
||||
mTask.mBitmap = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,6 +557,7 @@ public class WidgetPreviewLoader {
|
||||
private final int mPreviewHeight;
|
||||
private final int mPreviewWidth;
|
||||
private final WidgetCell mCaller;
|
||||
private Bitmap mBitmap;
|
||||
|
||||
PreviewLoadTask(WidgetCacheKey key, Object info, int previewWidth,
|
||||
int previewHeight, WidgetCell caller) {
|
||||
@@ -597,7 +576,6 @@ public class WidgetPreviewLoader {
|
||||
protected Bitmap doInBackground(Void... params) {
|
||||
Bitmap unusedBitmap = null;
|
||||
|
||||
// TODO(hyunyoungs): Figure out why this path causes concurrency issue.
|
||||
synchronized (mUnusedBitmaps) {
|
||||
// Check if we can use a bitmap
|
||||
for (Bitmap candidate : mUnusedBitmaps) {
|
||||
@@ -638,10 +616,7 @@ public class WidgetPreviewLoader {
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Bitmap result) {
|
||||
synchronized(mLoadedPreviews) {
|
||||
mLoadedPreviews.put(mKey, new WeakReference<Bitmap>(result));
|
||||
}
|
||||
|
||||
mBitmap = result;
|
||||
mCaller.applyPreview(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,18 +94,25 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
|
||||
mOriginalImagePadding.right = mWidgetImage.getPaddingRight();
|
||||
mOriginalImagePadding.bottom = mWidgetImage.getPaddingBottom();
|
||||
|
||||
// Ensure we are using the right text size
|
||||
DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
|
||||
mWidgetName = ((TextView) findViewById(R.id.widget_name));
|
||||
mWidgetDims = ((TextView) findViewById(R.id.widget_dims));
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
/**
|
||||
* Called to clear the view and free attached resources. (e.g., {@link Bitmap}
|
||||
*/
|
||||
public void clear() {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "reset called on:" + mWidgetName.getText());
|
||||
}
|
||||
mWidgetImage.setImageDrawable(null);
|
||||
mWidgetName.setText(null);
|
||||
mWidgetDims.setText(null);
|
||||
|
||||
cancelLoader(false);
|
||||
if (mActiveRequest != null) {
|
||||
mActiveRequest.cleanup();
|
||||
mActiveRequest = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,13 +218,6 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
|
||||
return Math.min(size[0], info.spanX * cellWidth);
|
||||
}
|
||||
|
||||
private void cancelLoader(boolean recycleImage) {
|
||||
if (mActiveRequest != null) {
|
||||
mActiveRequest.cancel(recycleImage);
|
||||
mActiveRequest = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to get the string info of the tag.
|
||||
*/
|
||||
|
||||
@@ -26,7 +26,6 @@ import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
@@ -61,9 +60,6 @@ public class WidgetsContainerView extends FrameLayout implements Insettable,
|
||||
private static final String TAG = "WidgetsContainerView";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/* {@link RecyclerView} will keep following # of views in cache, before recycling. */
|
||||
private static final int WIDGET_CACHE_SIZE = 2;
|
||||
|
||||
private static final int SPRING_MODE_DELAY_MS = 150;
|
||||
|
||||
/* Global instances that are used inside this container. */
|
||||
@@ -119,7 +115,6 @@ public class WidgetsContainerView extends FrameLayout implements Insettable,
|
||||
mView = (RecyclerView) findViewById(R.id.widgets_list_view);
|
||||
mView.setAdapter(mAdapter);
|
||||
mView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
mView.setItemViewCacheSize(WIDGET_CACHE_SIZE);
|
||||
|
||||
mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
|
||||
getPaddingBottom());
|
||||
|
||||
@@ -120,7 +120,9 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
|
||||
mIconCache.getTitleAndIconForApp(infoOut.packageName,
|
||||
UserHandleCompat.myUserHandle(), false /* useLowResIcon */, infoOut);
|
||||
}
|
||||
((TextView) holder.getContent().findViewById(R.id.section)).setText(infoOut.title);
|
||||
|
||||
TextView tv = ((TextView) holder.getContent().findViewById(R.id.section));
|
||||
tv.setText(infoOut.title);
|
||||
ImageView iv = (ImageView) holder.getContent().findViewById(R.id.section_image);
|
||||
iv.setImageBitmap(infoOut.iconBitmap);
|
||||
|
||||
@@ -149,7 +151,7 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
|
||||
@Override
|
||||
public WidgetsRowViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, String.format("\nonCreateViewHolder, [widget#=%d]", viewType));
|
||||
Log.v(TAG, "\nonCreateViewHolder");
|
||||
}
|
||||
|
||||
ViewGroup container = (ViewGroup) mLayoutInflater.inflate(
|
||||
@@ -159,11 +161,15 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
|
||||
|
||||
@Override
|
||||
public void onViewRecycled(WidgetsRowViewHolder holder) {
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, String.format("onViewDetachedFromWindow, [pos=%d]",
|
||||
holder.getAdapterPosition()));
|
||||
}
|
||||
ViewGroup row = ((ViewGroup) holder.getContent().findViewById(R.id.widgets_cell_list));
|
||||
|
||||
for (int i = 0; i < row.getChildCount(); i++) {
|
||||
WidgetCell widget = (WidgetCell) row.getChildAt(i);
|
||||
widget.reset();
|
||||
widget.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ public class WidgetsRowViewHolder extends ViewHolder {
|
||||
|
||||
ViewGroup mContent;
|
||||
|
||||
public WidgetsRowViewHolder(ViewGroup v) {
|
||||
public WidgetsRowViewHolder(ViewGroup v) {
|
||||
super(v);
|
||||
mContent = v;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user