mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-23 04:52:48 +00:00
Use weak references to allow the image views to be garbage collected while a load is in progress
This commit is contained in:
parent
c5293ef21f
commit
98638186b5
@ -13,18 +13,21 @@ import com.limelight.grid.assets.MemoryAssetLoader;
|
|||||||
import com.limelight.grid.assets.NetworkAssetLoader;
|
import com.limelight.grid.assets.NetworkAssetLoader;
|
||||||
import com.limelight.nvstream.http.ComputerDetails;
|
import com.limelight.nvstream.http.ComputerDetails;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.security.KeyManagementException;
|
import java.security.KeyManagementException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
|
public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
|
||||||
private final Activity activity;
|
private final Activity activity;
|
||||||
|
|
||||||
private final CachedAppAssetLoader loader;
|
private final CachedAppAssetLoader loader;
|
||||||
private final ConcurrentHashMap<ImageView, CachedAppAssetLoader.LoaderTuple> loadingTuples = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<WeakReference<ImageView>, CachedAppAssetLoader.LoaderTuple> loadingTuples = new ConcurrentHashMap<>();
|
||||||
private final ConcurrentHashMap<Object, CachedAppAssetLoader.LoaderTuple> backgroundLoadingTuples = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<Object, CachedAppAssetLoader.LoaderTuple> backgroundLoadingTuples = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public AppGridAdapter(Activity activity, boolean listMode, boolean small, ComputerDetails computer, String uniqueId) throws KeyManagementException, NoSuchAlgorithmException {
|
public AppGridAdapter(Activity activity, boolean listMode, boolean small, ComputerDetails computer, String uniqueId) throws KeyManagementException, NoSuchAlgorithmException {
|
||||||
@ -79,33 +82,49 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
|
|||||||
private final CachedAppAssetLoader.LoadListener imageViewLoadListener = new CachedAppAssetLoader.LoadListener() {
|
private final CachedAppAssetLoader.LoadListener imageViewLoadListener = new CachedAppAssetLoader.LoadListener() {
|
||||||
@Override
|
@Override
|
||||||
public void notifyLongLoad(Object object) {
|
public void notifyLongLoad(Object object) {
|
||||||
final ImageView view = (ImageView) object;
|
final WeakReference<ImageView> viewRef = (WeakReference<ImageView>) object;
|
||||||
|
|
||||||
activity.runOnUiThread(new Runnable() {
|
// If the view isn't there anymore, don't bother scheduling on the UI thread
|
||||||
@Override
|
if (viewRef.get() == null) {
|
||||||
public void run() {
|
|
||||||
view.setImageResource(R.drawable.image_loading);
|
|
||||||
fadeInImage(view);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void notifyLoadComplete(Object object, final Bitmap bitmap) {
|
|
||||||
final ImageView view = (ImageView) object;
|
|
||||||
|
|
||||||
loadingTuples.remove(view);
|
|
||||||
|
|
||||||
// Just leave the loading icon in place
|
|
||||||
if (bitmap == null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
activity.runOnUiThread(new Runnable() {
|
activity.runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
view.setImageBitmap(bitmap);
|
ImageView view = viewRef.get();
|
||||||
fadeInImage(view);
|
if (view != null) {
|
||||||
|
view.setImageResource(R.drawable.image_loading);
|
||||||
|
fadeInImage(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void notifyLoadComplete(Object object, final Bitmap bitmap) {
|
||||||
|
final WeakReference<ImageView> viewRef = (WeakReference<ImageView>) object;
|
||||||
|
|
||||||
|
loadingTuples.remove(viewRef);
|
||||||
|
|
||||||
|
// Just leave the loading icon in place
|
||||||
|
if (bitmap == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the view isn't there anymore, don't bother scheduling on the UI thread
|
||||||
|
if (viewRef.get() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
activity.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
ImageView view = viewRef.get();
|
||||||
|
if (view != null) {
|
||||||
|
view.setImageBitmap(bitmap);
|
||||||
|
fadeInImage(view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -121,23 +140,38 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private void reapLoaderTuples(ImageView view) {
|
||||||
|
// Poor HashMap doesn't deserve this...
|
||||||
|
Iterator<Map.Entry<WeakReference<ImageView>, CachedAppAssetLoader.LoaderTuple>> i = loadingTuples.entrySet().iterator();
|
||||||
|
while (i.hasNext()) {
|
||||||
|
Map.Entry<WeakReference<ImageView>, CachedAppAssetLoader.LoaderTuple> entry = i.next();
|
||||||
|
ImageView imageView = entry.getKey().get();
|
||||||
|
|
||||||
|
// Remove tuples that refer to this view or no view
|
||||||
|
if (imageView == null || imageView == view) {
|
||||||
|
// FIXME: There's a small chance that this can race if we've already gone down
|
||||||
|
// the path to notification but haven't been notified yet
|
||||||
|
entry.getValue().cancel();
|
||||||
|
|
||||||
|
// Remove it from the tuple list
|
||||||
|
i.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean populateImageView(final ImageView imgView, final AppView.AppObject obj) {
|
public boolean populateImageView(final ImageView imgView, final AppView.AppObject obj) {
|
||||||
// Cancel pending loads on this image view
|
// Cancel pending loads on this image view
|
||||||
CachedAppAssetLoader.LoaderTuple tuple = loadingTuples.remove(imgView);
|
reapLoaderTuples(imgView);
|
||||||
if (tuple != null) {
|
|
||||||
// FIXME: There's a small chance that this can race if we've already gone down
|
|
||||||
// the path to notification but haven't been notified yet
|
|
||||||
tuple.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear existing contents of the image view
|
// Clear existing contents of the image view
|
||||||
imgView.setAlpha(0.0f);
|
imgView.setAlpha(0.0f);
|
||||||
|
|
||||||
// Start loading the bitmap
|
// Start loading the bitmap
|
||||||
tuple = loader.loadBitmapWithContext(obj.app, imgView, imageViewLoadListener);
|
WeakReference<ImageView> viewRef = new WeakReference<>(imgView);
|
||||||
|
CachedAppAssetLoader.LoaderTuple tuple = loader.loadBitmapWithContext(obj.app, viewRef, imageViewLoadListener);
|
||||||
if (tuple != null) {
|
if (tuple != null) {
|
||||||
// The load was issued asynchronously
|
// The load was issued asynchronously
|
||||||
loadingTuples.put(imgView, tuple);
|
loadingTuples.put(viewRef, tuple);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user