Add gallery image cache

This commit is contained in:
seven332 committed 2016-05-01 13:26:13 +08:00
1 parent f672fe1e96
commit b973e0244e
8 files changed
+175 -60

No files matched your search

@@ -84,7 +84,7 @@ public class DirGalleryProvider extends GalleryProvider implements Runnable {
}
@Override
public void request(int index) {
protected void onRequest(int index) {
synchronized (mRequests) {
if (!mRequests.contains(index) && index != mDecodingIndex.get()) {
mRequests.add(index);
@@ -95,7 +95,12 @@ public class DirGalleryProvider extends GalleryProvider implements Runnable {
}
@Override
public void cancelRequest(int index) {
protected void onForceRequest(int index) {
onRequest(index);
}
@Override
public void onCancelRequest(int index) {
synchronized (mRequests) {
mRequests.remove(Integer.valueOf(index));
}
@@ -111,7 +111,7 @@ public class EhGalleryProvider extends GalleryProvider implements SpiderQueen.On
}
@Override
public void request(int index) {
protected void onRequest(int index) {
if (mSpiderQueen != null) {
Object object = mSpiderQueen.request(index);
if (object instanceof Float) {
@@ -125,7 +125,7 @@ public class EhGalleryProvider extends GalleryProvider implements SpiderQueen.On
}
@Override
public void forceRequest(int index) {
protected void onForceRequest(int index) {
if (mSpiderQueen != null) {
Object object = mSpiderQueen.forceRequest(index);
if (object instanceof Float) {
@@ -139,7 +139,7 @@ public class EhGalleryProvider extends GalleryProvider implements SpiderQueen.On
}
@Override
public void cancelRequest(int index) {
protected void onCancelRequest(int index) {
if (mSpiderQueen != null) {
mSpiderQueen.cancelRequest(index);
}
@@ -21,15 +21,25 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import com.hippo.beerbelly.LruCache;
import com.hippo.gl.glrenderer.GLCanvas;
import com.hippo.gl.view.GLRoot;
import com.hippo.image.Image;
import com.hippo.image.ImageWrapper;
import com.hippo.unifile.UniFile;
import com.hippo.yorozuya.ConcurrentPool;
import com.hippo.yorozuya.IOUtils;
import com.hippo.yorozuya.MathUtils;
import com.hippo.yorozuya.NumberUtils;
import com.hippo.yorozuya.OSUtils;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public abstract class GalleryProvider {
@@ -48,6 +58,8 @@ public abstract class GalleryProvider {
private volatile GalleryProviderListener mGalleryProviderListener;
private volatile GLRoot mGLRoot;
private final ImageCache mImageCache = new ImageCache();
private boolean mStarted = false;
@UiThread
@@ -63,25 +75,40 @@ public abstract class GalleryProvider {
@UiThread
public void stop() {
OSUtils.checkMainLoop();
mImageCache.close();
}
public void setGLRoot(GLRoot glRoot) {
mGLRoot = glRoot;
}
/**
* @return {@link #STATE_WAIT} for wait, 0 for empty
*/
public abstract int size();
public abstract void request(int index);
public void forceRequest(int index) {
request(index);
public final void request(int index) {
ImageWrapper imageWrapper = mImageCache.get(index);
if (imageWrapper != null) {
notifyPageSucceed(index, imageWrapper);
} else {
onRequest(index);
}
}
public abstract void cancelRequest(int index);
public final void forceRequest(int index) {
onForceRequest(index);
}
protected abstract void onRequest(int index);
protected abstract void onForceRequest(int index);
public final void cancelRequest(int index) {
onCancelRequest(index);
}
protected abstract void onCancelRequest(int index);
public abstract String getError();
@@ -126,6 +153,12 @@ public abstract class GalleryProvider {
}
public void notifyPageSucceed(int index, Image image) {
ImageWrapper imageWrapper = new ImageWrapper(image);
mImageCache.add(index, imageWrapper);
notifyPageSucceed(index, imageWrapper);
}
public void notifyPageSucceed(int index, ImageWrapper image) {
notify(NotifyTask.TYPE_SUCCEED, index, 0.0f, image, null);
}
@@ -133,7 +166,7 @@ public abstract class GalleryProvider {
notify(NotifyTask.TYPE_FAILED, index, 0.0f, null, error);
}
private void notify(@NotifyTask.Type int type, int index, float percent, Image image, String error) {
private void notify(@NotifyTask.Type int type, int index, float percent, ImageWrapper image, String error) {
GalleryProviderListener listener = mGalleryProviderListener;
if (listener == null) {
return;
@@ -171,7 +204,7 @@ public abstract class GalleryProvider {
private int mType;
private int mIndex;
private float mPercent;
private Image mImage;
private ImageWrapper mImage;
private String mError;
public NotifyTask(GalleryProviderListener galleryProviderListener, ConcurrentPool<NotifyTask> pool) {
@@ -179,7 +212,7 @@ public abstract class GalleryProvider {
mPool = pool;
}
public void setData(@Type int type, int index, float percent, Image image, String error) {
public void setData(@Type int type, int index, float percent, ImageWrapper image, String error) {
mType = type;
mIndex = index;
mPercent = percent;
@@ -220,4 +253,67 @@ public abstract class GalleryProvider {
return false;
}
}
private static class ImageCache extends LruCache<Integer, ImageWrapper> {
private static final long MAX_CACHE_SIZE = 128 * 1024 * 1024;
private static final long MIN_CACHE_SIZE = 32 * 1024 * 1024;
private static final String PROCFS_MEMFILE = "/proc/meminfo";
private static final Pattern PROCFS_MEMFILE_FORMAT =
Pattern.compile("^([a-zA-Z]*):[ \t]*([0-9]*)[ \t]kB");
private static final String MEMTOTAL_STRING = "MemTotal";
private static long sMemTotal = -2L;
private static long getTotalMemory() {
if (sMemTotal != -2L) {
return sMemTotal;
}
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(PROCFS_MEMFILE), 64);
String line;
while ((line = reader.readLine()) != null) {
Matcher matcher = PROCFS_MEMFILE_FORMAT.matcher(line);
if (matcher.find() && MEMTOTAL_STRING.equals(matcher.group(1))) {
long mem = NumberUtils.parseLongSafely(matcher.group(2), -1L);
if (mem != -1L) {
mem *= 1024;
}
sMemTotal = mem;
return mem;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(reader);
}
sMemTotal = -1L;
return -1L;
}
public ImageCache() {
super((int) MathUtils.clamp(getTotalMemory() / 16, MIN_CACHE_SIZE, MAX_CACHE_SIZE));
}
public void add(Integer key, ImageWrapper value) {
if (value.obtain()) {
put(key, value);
}
}
@Override
protected int sizeOf(Integer key, ImageWrapper value) {
return value.getWidth() * value.getHeight() * 4;
}
@Override
protected void entryRemoved(boolean evicted, Integer key, ImageWrapper oldValue, ImageWrapper newValue) {
if (oldValue != null) {
oldValue.release();
}
}
}
}
@@ -16,7 +16,7 @@
package com.hippo.ehviewer.gallery;
import com.hippo.image.Image;
import com.hippo.image.ImageWrapper;
public interface GalleryProviderListener {
@@ -26,7 +26,7 @@ public interface GalleryProviderListener {
void onPagePercent(int index, float percent);
void onPageSucceed(int index, Image image);
void onPageSucceed(int index, ImageWrapper image);
void onPageFailed(int index, String error);
@@ -85,7 +85,7 @@ public class ZipGalleryProvider extends GalleryProvider implements Runnable {
}
@Override
public void request(int index) {
protected void onRequest(int index) {
synchronized (mRequests) {
if (!mRequests.contains(index) && index != mDecodingIndex.get()) {
mRequests.add(index);
@@ -96,7 +96,12 @@ public class ZipGalleryProvider extends GalleryProvider implements Runnable {
}
@Override
public void cancelRequest(int index) {
protected void onForceRequest(int index) {
onRequest(index);
}
@Override
protected void onCancelRequest(int index) {
synchronized (mRequests) {
mRequests.remove(Integer.valueOf(index));
}
@@ -62,7 +62,7 @@ import com.hippo.ehviewer.widget.GalleryGuideView;
import com.hippo.ehviewer.widget.ReversibleSeekBar;
import com.hippo.gl.glrenderer.ImageTexture;
import com.hippo.gl.view.GLRootView;
import com.hippo.image.Image;
import com.hippo.image.ImageWrapper;
import com.hippo.unifile.UniFile;
import com.hippo.util.SystemUiHelper;
import com.hippo.widget.ColorView;
@@ -549,20 +549,26 @@ public class GalleryActivity extends EhActivity
}
@Override
public void onPageSucceed(int index, Image image) {
public void onPageSucceed(int index, ImageWrapper image) {
GalleryPageView page = findPageByIndex(index);
if (page != null) {
ImageTexture imageTexture = new ImageTexture(image);
if (mUploader != null) {
mUploader.addTexture(imageTexture);
if (image.obtain()) {
ImageTexture imageTexture = new ImageTexture(image);
if (mUploader != null) {
mUploader.addTexture(imageTexture);
}
page.showImage();
page.setImage(imageTexture);
page.setPage(index + 1);
page.setProgress(GalleryPageView.PROGRESS_GONE);
page.setError(null, null);
} else {
// The image is recycled, request again.
// TODO request loop ?
if (mGalleryProvider != null) {
mGalleryProvider.request(index);
}
}
page.showImage();
page.setImage(imageTexture);
page.setPage(index + 1);
page.setProgress(GalleryPageView.PROGRESS_GONE);
page.setError(null, null);
} else {
image.recycle();
}
}
@@ -24,7 +24,7 @@ import android.support.annotation.NonNull;
import com.hippo.gl.annotation.RenderThread;
import com.hippo.gl.view.GLRoot;
import com.hippo.image.Image;
import com.hippo.image.ImageWrapper;
import com.hippo.yorozuya.thread.PVLock;
import com.hippo.yorozuya.thread.PriorityThreadFactory;
@@ -66,10 +66,10 @@ public class ImageTexture implements Texture, Animatable {
private static Tile sLargeFreeTileHead = null;
private static final Object sFreeTileLock = new Object();
private final Image mImage;
private final ImageWrapper mImage;
private int mUploadIndex = 0;
private final Tile[] mTiles; // Can be modified in different threads.
// Should be protected by "synchronized."
// Should be protected by "synchronized."
private final int mWidth;
private final int mHeight;
@@ -79,7 +79,8 @@ public class ImageTexture implements Texture, Animatable {
private final AtomicBoolean mFrameDirty = new AtomicBoolean();
private boolean mImageBusy = false;
private final AtomicBoolean mNeedRecycle = new AtomicBoolean();
private final AtomicBoolean mNeedRelease = new AtomicBoolean();
private final AtomicBoolean mReleased = new AtomicBoolean();
private final AtomicReference<Thread> mAnimateThread = new AtomicReference<>();
@@ -142,7 +143,7 @@ public class ImageTexture implements Texture, Animatable {
private int mTileType;
public int offsetX;
public int offsetY;
public Image image;
public ImageWrapper image;
public Tile nextFreeTile;
public int contentWidth;
public int contentHeight;
@@ -244,8 +245,8 @@ public class ImageTexture implements Texture, Animatable {
long lastDelay = -1L;
synchronized (mImage) {
// Check recycled
if (mImage.isRecycled()) {
// Check released
if (mReleased.get()) {
return;
}
// Check image busy
@@ -253,8 +254,8 @@ public class ImageTexture implements Texture, Animatable {
// Image is busy, means it is recycling
return;
}
if (mNeedRecycle.get()) {
// Need recycle
if (mNeedRelease.get()) {
// Need release
return;
}
// Obtain image
@@ -270,7 +271,7 @@ public class ImageTexture implements Texture, Animatable {
} catch (InterruptedException e) {
interrupted = true;
}
if (!interrupted && !mNeedRecycle.get()) {
if (!interrupted && !mNeedRelease.get()) {
mImage.complete();
}
sPVLock.v();
@@ -284,15 +285,15 @@ public class ImageTexture implements Texture, Animatable {
mImageBusy = false;
}
if (interrupted || mNeedRecycle.get() || frameCount <= 1) {
if (interrupted || mNeedRelease.get() || frameCount <= 1) {
return;
}
while (!Thread.currentThread().isInterrupted()) {
// Obtain
synchronized (mImage) {
// Check recycled
if (mImage.isRecycled()) {
// Check released
if (mReleased.get()) {
return;
}
// Check image busy
@@ -300,8 +301,8 @@ public class ImageTexture implements Texture, Animatable {
// Image is busy, means it is recycling
return;
}
if (mNeedRecycle.get()) {
// Need recycle
if (mNeedRelease.get()) {
// Need release
return;
}
// Obtain image
@@ -338,11 +339,11 @@ public class ImageTexture implements Texture, Animatable {
public void run() {
doRun();
while (mNeedRecycle.get()) {
while (mNeedRelease.get()) {
// Obtain
synchronized (mImage) {
// Check recycled
if (mImage.isRecycled()) {
// Check released
if (mReleased.get()) {
break;
}
// Check image busy
@@ -354,7 +355,8 @@ public class ImageTexture implements Texture, Animatable {
mImageBusy = true;
}
mImage.recycle();
mImage.release();
mReleased.lazySet(true);
synchronized (mImage) {
// Release image
@@ -366,7 +368,7 @@ public class ImageTexture implements Texture, Animatable {
}
}
public ImageTexture(@NonNull Image image) {
public ImageTexture(@NonNull ImageWrapper image) {
mImage = image;
int width = mWidth = image.getWidth();
int height = mHeight = image.getHeight();
@@ -457,7 +459,7 @@ public class ImageTexture implements Texture, Animatable {
@Override
public void start() {
if (mImage.isRecycled() || (mImage.isCompleted() && mImage.getFrameCount() <= 1)) {
if (mReleased.get() || (mImage.isCompleted() && mImage.getFrameCount() <= 1)) {
return;
}
@@ -471,7 +473,7 @@ public class ImageTexture implements Texture, Animatable {
@Override
public void stop() {
if (!mImage.isRecycled() && (mImage.isCompleted() && mImage.getFrameCount() > 1)) {
if (!mReleased.get() && (mImage.isCompleted() && mImage.getFrameCount() > 1)) {
Thread thread = mAnimateThread.getAndSet(null);
if (null != thread) {
thread.interrupt();
@@ -481,7 +483,7 @@ public class ImageTexture implements Texture, Animatable {
@Override
public boolean isRunning() {
return false;
return mAnimateThread.get() != null;
}
@@ -654,24 +656,25 @@ public class ImageTexture implements Texture, Animatable {
mTile.free();
}
boolean recycleNow;
boolean releaseNow;
synchronized (mImage) {
if (!mImageBusy) {
recycleNow = true;
releaseNow = true;
mImageBusy = true;
} else {
recycleNow = false;
releaseNow = false;
}
}
if (recycleNow) {
mImage.recycle();
if (releaseNow) {
mImage.release();
mReleased.set(true);
synchronized (mImage) {
mImageBusy = false;
}
} else {
mNeedRecycle.set(true);
mNeedRelease.set(true);
}
Thread thread = mAnimateThread.getAndSet(null);