Fix thumb load
This commit is contained in:
10 files changed
+1158
-3
No files matched your search
@@ -101,7 +101,6 @@ dependencies {
|
||||
implementation 'com.github.seven332.a7zip:extract-lite:1b21783'
|
||||
implementation 'com.github.seven332:android-resource:0.1.0'
|
||||
implementation 'com.github.seven332:beerbelly:0.1.4'
|
||||
implementation 'com.github.seven332:conaco:0.1.5-eh'
|
||||
implementation 'com.github.seven332:glgallery:0.1.2'
|
||||
implementation 'com.github.seven332:glview:0.1.0'
|
||||
implementation 'com.github.seven332:glview-image:0.1.0'
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright 2015-2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.conaco;
|
||||
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.hippo.beerbelly.BeerBelly;
|
||||
import com.hippo.yorozuya.thread.PriorityThreadFactory;
|
||||
import com.hippo.yorozuya.thread.SerialThreadExecutor;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public final class Conaco<V> {
|
||||
|
||||
private static final String TAG = Conaco.class.getSimpleName();
|
||||
|
||||
@IntDef({SOURCE_MEMORY, SOURCE_DISK, SOURCE_NETWORK})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Source {
|
||||
}
|
||||
|
||||
public static final int SOURCE_MEMORY = 0;
|
||||
public static final int SOURCE_DISK = 1;
|
||||
public static final int SOURCE_NETWORK = 2;
|
||||
|
||||
private final ValueHelper<V> mHelper;
|
||||
private final ValueCache<V> mCache;
|
||||
private final OkHttpClient mOkHttpClient;
|
||||
private final Register<V> mRegister;
|
||||
|
||||
private final SerialThreadExecutor mDiskExecutor;
|
||||
private final ThreadPoolExecutor mNetworkExecutor;
|
||||
private final AtomicInteger mIdGenerator;
|
||||
|
||||
private final boolean mDebug;
|
||||
|
||||
private Conaco(Builder<V> builder) {
|
||||
mHelper = builder.objectHelper;
|
||||
|
||||
BeerBelly.BeerBellyParams beerBellyParams = new BeerBelly.BeerBellyParams();
|
||||
beerBellyParams.hasMemoryCache = builder.hasMemoryCache;
|
||||
beerBellyParams.memoryCacheMaxSize = builder.memoryCacheMaxSize;
|
||||
beerBellyParams.hasDiskCache = builder.hasDiskCache;
|
||||
beerBellyParams.diskCacheDir = builder.diskCacheDir;
|
||||
beerBellyParams.diskCacheMaxSize = builder.diskCacheMaxSize;
|
||||
|
||||
mCache = new ValueCache<>(beerBellyParams, mHelper);
|
||||
mOkHttpClient = builder.okHttpClient;
|
||||
|
||||
mRegister = new Register<>();
|
||||
|
||||
mDiskExecutor = new SerialThreadExecutor(3000L, new LinkedList<>(),
|
||||
new PriorityThreadFactory("Conaco-Disk", Process.THREAD_PRIORITY_BACKGROUND));
|
||||
mNetworkExecutor = new ThreadPoolExecutor(3, 3, 5L, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(),
|
||||
new PriorityThreadFactory("Conaco-Network", Process.THREAD_PRIORITY_BACKGROUND));
|
||||
|
||||
mIdGenerator = new AtomicInteger();
|
||||
|
||||
mDebug = builder.debug;
|
||||
}
|
||||
|
||||
public BeerBelly getBeerBelly() {
|
||||
return mCache;
|
||||
}
|
||||
|
||||
private void onUnregisterConacoTask(ConacoTask<V> task) {
|
||||
ConacoTask<V> next = mRegister.getByKey(task.getKey());
|
||||
if (next != null) {
|
||||
startConacoTask(next);
|
||||
}
|
||||
}
|
||||
|
||||
private void startConacoTask(ConacoTask<V> task) {
|
||||
Unikery<V> unikery = task.getUnikery();
|
||||
|
||||
// Can't unikery is gone, finish the task now
|
||||
if (unikery == null) {
|
||||
finishConacoTask(task);
|
||||
return;
|
||||
}
|
||||
|
||||
String key = task.getKey();
|
||||
V value = null;
|
||||
|
||||
// Get from memory
|
||||
if (key != null && task.useMemoryCache() && mHelper.useMemoryCache(key, null)) {
|
||||
value = mCache.getFromMemory(key);
|
||||
}
|
||||
|
||||
if (value == null || !unikery.onGetValue(value, SOURCE_MEMORY)) {
|
||||
unikery.onMiss(SOURCE_MEMORY);
|
||||
task.start();
|
||||
} else {
|
||||
// Get the object, finish the task
|
||||
finishConacoTask(task);
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void load(ConacoTask.Builder<V> builder) {
|
||||
builder.isValid();
|
||||
|
||||
if (mDebug) {
|
||||
Log.d(TAG, "Key " + builder.getKey());
|
||||
Log.d(TAG, "Url " + builder.getUrl());
|
||||
}
|
||||
|
||||
Unikery<V> unikery = builder.getUnikery();
|
||||
|
||||
// Cancel first
|
||||
cancel(unikery);
|
||||
|
||||
// Build conaco task
|
||||
int id;
|
||||
//noinspection StatementWithEmptyBody
|
||||
while ((id = mIdGenerator.getAndIncrement()) == Unikery.INVALID_ID) ;
|
||||
unikery.setTaskId(id);
|
||||
builder.setId(id)
|
||||
.setHelper(mHelper)
|
||||
.setCache(mCache)
|
||||
.setOkHttpClient(mOkHttpClient)
|
||||
.setDiskExecutor(mDiskExecutor)
|
||||
.setNetworkExecutor(mNetworkExecutor)
|
||||
.setConaco(this);
|
||||
ConacoTask<V> task = builder.build();
|
||||
|
||||
if (!mRegister.register(id, task)) {
|
||||
startConacoTask(task);
|
||||
} else {
|
||||
// The key is repeated, wait
|
||||
unikery.onWait();
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public void cancel(Unikery<V> unikery) {
|
||||
int id = unikery.getTaskId();
|
||||
if (id != Unikery.INVALID_ID) {
|
||||
unikery.setTaskId(Unikery.INVALID_ID);
|
||||
ConacoTask<V> task = mRegister.unregister(id);
|
||||
if (task != null) {
|
||||
task.stop();
|
||||
// Don't need unikery anymore
|
||||
task.clearUnikery();
|
||||
// Callback
|
||||
onUnregisterConacoTask(task);
|
||||
} else {
|
||||
Log.e(TAG, "Can't find conaco task by id " + id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isLoading(Unikery<V> unikery) {
|
||||
int id = unikery.getTaskId();
|
||||
return id != Unikery.INVALID_ID && mRegister.contain(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister task, reset unikery id, clear unikery in task, call next
|
||||
*/
|
||||
void finishConacoTask(ConacoTask<V> task) {
|
||||
mRegister.unregister(task.getId());
|
||||
Unikery unikery = task.getUnikery();
|
||||
if (unikery != null) {
|
||||
unikery.setTaskId(Unikery.INVALID_ID);
|
||||
}
|
||||
task.clearUnikery();
|
||||
|
||||
onUnregisterConacoTask(task);
|
||||
}
|
||||
|
||||
public static class Builder<T> extends BeerBelly.BeerBellyParams {
|
||||
/**
|
||||
* The client to get image from internet
|
||||
*/
|
||||
public OkHttpClient okHttpClient = null;
|
||||
|
||||
/**
|
||||
* Decode, get size and others
|
||||
*/
|
||||
public ValueHelper<T> objectHelper = null;
|
||||
|
||||
public boolean debug = false;
|
||||
|
||||
@Override
|
||||
public void isValid() throws IllegalStateException {
|
||||
super.isValid();
|
||||
|
||||
if (okHttpClient == null) {
|
||||
throw new IllegalStateException("No http client? How can I load image via url?");
|
||||
}
|
||||
}
|
||||
|
||||
public Conaco<T> build() {
|
||||
isValid();
|
||||
return new Conaco<>(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,588 @@
|
||||
/*
|
||||
* Copyright 2015-2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.conaco;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.hippo.beerbelly.SimpleDiskCache;
|
||||
import com.hippo.okhttp.ChromeRequestBuilder;
|
||||
import com.hippo.streampipe.InputStreamPipe;
|
||||
import com.hippo.streampipe.OutputStreamPipe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
@SuppressWarnings({"rawtypes", "deprecation"})
|
||||
public final class ConacoTask<V> {
|
||||
|
||||
private static final String TAG = ConacoTask.class.getSimpleName();
|
||||
|
||||
private final int mId;
|
||||
private final WeakReference<Unikery<V>> mUnikeryWeakReference;
|
||||
private final String mKey;
|
||||
private final String mUrl;
|
||||
private final DataContainer mDataContainer;
|
||||
private final boolean mUseMemoryCache;
|
||||
private final boolean mUseDiskCache;
|
||||
private final boolean mUseNetwork;
|
||||
private final ValueHelper<V> mHelper;
|
||||
private final ValueCache<V> mCache;
|
||||
private final OkHttpClient mOkHttpClient;
|
||||
private final Executor mDiskExecutor;
|
||||
private final Executor mNetworkExecutor;
|
||||
private final Conaco<V> mConaco;
|
||||
|
||||
private DiskLoadTask mDiskLoadTask;
|
||||
private NetworkLoadTask mNetworkLoadTask;
|
||||
private Call mCall;
|
||||
private boolean mStart;
|
||||
private volatile boolean mStop;
|
||||
|
||||
private ConacoTask(Builder<V> builder) {
|
||||
mId = builder.mId;
|
||||
mUnikeryWeakReference = new WeakReference<>(builder.mUnikery);
|
||||
mKey = builder.mKey;
|
||||
mUrl = builder.mUrl;
|
||||
mDataContainer = builder.mDataContainer;
|
||||
mUseMemoryCache = builder.mUseMemoryCache;
|
||||
mUseDiskCache = builder.mUseDiskCache;
|
||||
mUseNetwork = builder.mUseNetwork;
|
||||
mHelper = builder.mHelper;
|
||||
mCache = builder.mCache;
|
||||
mOkHttpClient = builder.mOkHttpClient;
|
||||
mDiskExecutor = builder.mDiskExecutor;
|
||||
mNetworkExecutor = builder.mNetworkExecutor;
|
||||
mConaco = builder.mConaco;
|
||||
}
|
||||
|
||||
int getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
String getKey() {
|
||||
return mKey;
|
||||
}
|
||||
|
||||
boolean useMemoryCache() {
|
||||
return mUseMemoryCache;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Unikery<V> getUnikery() {
|
||||
return mUnikeryWeakReference.get();
|
||||
}
|
||||
|
||||
void clearUnikery() {
|
||||
mUnikeryWeakReference.clear();
|
||||
}
|
||||
|
||||
private void onFinish() {
|
||||
if (!mStop) {
|
||||
mConaco.finishConacoTask(this);
|
||||
}/* else {
|
||||
// It is done by Conaco
|
||||
}*/
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void start() {
|
||||
if (mStop || mStart) {
|
||||
return;
|
||||
}
|
||||
|
||||
mStart = true;
|
||||
|
||||
Unikery unikery = mUnikeryWeakReference.get();
|
||||
if (unikery != null && unikery.getTaskId() == mId) {
|
||||
if (mUseDiskCache) {
|
||||
mDiskLoadTask = new DiskLoadTask();
|
||||
mDiskLoadTask.executeOnExecutor(mDiskExecutor);
|
||||
return;
|
||||
} else if (mUseNetwork) {
|
||||
unikery.onMiss(Conaco.SOURCE_DISK);
|
||||
unikery.onRequest();
|
||||
mNetworkLoadTask = new NetworkLoadTask();
|
||||
mNetworkLoadTask.executeOnExecutor(mNetworkExecutor);
|
||||
return;
|
||||
} else {
|
||||
unikery.onMiss(Conaco.SOURCE_DISK);
|
||||
unikery.onMiss(Conaco.SOURCE_NETWORK);
|
||||
unikery.onFailure();
|
||||
}
|
||||
}
|
||||
|
||||
onFinish();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void stop() {
|
||||
if (mStop) {
|
||||
return;
|
||||
}
|
||||
|
||||
mStop = true;
|
||||
|
||||
// Stop jobs
|
||||
if (mDiskLoadTask != null) { // Getting from disk
|
||||
mDiskLoadTask.cancel(false);
|
||||
} else if (mNetworkLoadTask != null) { // Getting from network
|
||||
mNetworkLoadTask.cancel(false);
|
||||
if (mCall != null) {
|
||||
mCall.cancel();
|
||||
mCall = null;
|
||||
}
|
||||
}
|
||||
|
||||
Unikery unikery = mUnikeryWeakReference.get();
|
||||
if (unikery != null) {
|
||||
unikery.onCancel();
|
||||
}
|
||||
|
||||
// Conaco handle the clean up
|
||||
}
|
||||
|
||||
private boolean isNotNecessary(AsyncTask asyncTask) {
|
||||
Unikery unikery = mUnikeryWeakReference.get();
|
||||
return mStop || asyncTask.isCancelled() || unikery == null || unikery.getTaskId() != mId;
|
||||
}
|
||||
|
||||
private static void putFromDiskCacheToDataContainer(String key, ValueCache cache, DataContainer container) {
|
||||
SimpleDiskCache diskCache = cache.getDiskCache();
|
||||
if (diskCache != null) {
|
||||
InputStreamPipe pipe = diskCache.getInputStreamPipe(key);
|
||||
if (pipe != null) {
|
||||
try {
|
||||
pipe.obtain();
|
||||
container.save(pipe.open(), -1L, null, null);
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Can't save value from disk cache to data container");
|
||||
e.printStackTrace();
|
||||
container.remove();
|
||||
} finally {
|
||||
pipe.close();
|
||||
pipe.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void putFromDataContainerToDiskCache(String key, ValueCache cache, DataContainer container) {
|
||||
InputStreamPipe pipe = container.get();
|
||||
if (pipe != null) {
|
||||
try {
|
||||
pipe.obtain();
|
||||
cache.putRawToDisk(key, pipe.open());
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Can't save value from data container to disk cache", e);
|
||||
cache.removeFromDisk(key);
|
||||
} finally {
|
||||
pipe.close();
|
||||
pipe.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private class DiskLoadTask extends AsyncTask<Void, Void, V> {
|
||||
|
||||
@Override
|
||||
protected V doInBackground(Void... params) {
|
||||
if (isNotNecessary(this)) {
|
||||
return null;
|
||||
} else {
|
||||
V value = null;
|
||||
|
||||
// First check data container
|
||||
if (mDataContainer != null && mDataContainer.isEnabled()) {
|
||||
InputStreamPipe isp = mDataContainer.get();
|
||||
if (isp != null) {
|
||||
value = mHelper.decode(isp);
|
||||
}
|
||||
}
|
||||
|
||||
// Then check disk cache
|
||||
if (mKey != null) {
|
||||
if (value == null && mUseDiskCache) {
|
||||
value = mCache.getFromDisk(mKey);
|
||||
// Put back to data container
|
||||
if (value != null && mDataContainer != null && mDataContainer.isEnabled()) {
|
||||
putFromDiskCacheToDataContainer(mKey, mCache, mDataContainer);
|
||||
}
|
||||
}
|
||||
|
||||
if (value != null && mUseMemoryCache && mHelper.useMemoryCache(mKey, value)) {
|
||||
// Put it to memory
|
||||
mCache.putToMemory(mKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(V value) {
|
||||
mDiskLoadTask = null;
|
||||
|
||||
if (isCancelled() || mStop) {
|
||||
onCancelled(value);
|
||||
} else {
|
||||
Unikery<V> unikery = mUnikeryWeakReference.get();
|
||||
if (unikery != null && unikery.getTaskId() == mId) {
|
||||
boolean getValue = false;
|
||||
if ((value == null || !(getValue = unikery.onGetValue(value, Conaco.SOURCE_DISK))) && mUseNetwork) {
|
||||
unikery.onMiss(Conaco.SOURCE_DISK);
|
||||
unikery.onRequest();
|
||||
mNetworkLoadTask = new NetworkLoadTask();
|
||||
mNetworkLoadTask.executeOnExecutor(mNetworkExecutor);
|
||||
return;
|
||||
} else if (!getValue) {
|
||||
unikery.onMiss(Conaco.SOURCE_DISK);
|
||||
unikery.onFailure();
|
||||
}
|
||||
}
|
||||
onFinish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled(V holder) {
|
||||
onFinish();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private class NetworkLoadTask extends AsyncTask<Void, Long, V> implements ProgressNotifier {
|
||||
|
||||
@Override
|
||||
public void notifyProgress(long singleReceivedSize, long receivedSize, long totalSize) {
|
||||
if (!isNotNecessary(this)) {
|
||||
publishProgress(singleReceivedSize, receivedSize, totalSize);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean putToDiskCache(InputStream is, long length) {
|
||||
SimpleDiskCache diskCache = mCache.getDiskCache();
|
||||
if (diskCache == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OutputStreamPipe pipe = diskCache.getOutputStreamPipe(mKey);
|
||||
try {
|
||||
pipe.obtain();
|
||||
OutputStream os = pipe.open();
|
||||
|
||||
final byte[] buffer = new byte[1024 * 4];
|
||||
long receivedSize = 0;
|
||||
int bytesRead;
|
||||
|
||||
while ((bytesRead = is.read(buffer)) != -1) {
|
||||
os.write(buffer, 0, bytesRead);
|
||||
receivedSize += bytesRead;
|
||||
notifyProgress(bytesRead, receivedSize, length);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
} finally {
|
||||
pipe.close();
|
||||
pipe.release();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean putToDataContainer(InputStream is, ResponseBody body) {
|
||||
// Get media type
|
||||
String mediaType;
|
||||
MediaType mt = body.contentType();
|
||||
if (mt != null) {
|
||||
mediaType = mt.type() + '/' + mt.subtype();
|
||||
} else {
|
||||
mediaType = null;
|
||||
}
|
||||
return mDataContainer.save(is, body.contentLength(), mediaType, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected V doInBackground(Void... params) {
|
||||
if (isNotNecessary(this)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
V value;
|
||||
InputStream is = null;
|
||||
try {
|
||||
Log.d("TAG", "Conaco " + mUrl);
|
||||
|
||||
// Load it from internet
|
||||
Request request = new ChromeRequestBuilder(mUrl).build();
|
||||
mCall = mOkHttpClient.newCall(request);
|
||||
|
||||
Response response = mCall.execute();
|
||||
ResponseBody body = response.body();
|
||||
if (body == null) {
|
||||
return null;
|
||||
}
|
||||
is = body.byteStream();
|
||||
|
||||
if (isNotNecessary(this)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((mDataContainer == null || !mDataContainer.isEnabled()) && mKey != null) {
|
||||
if (putToDiskCache(is, body.contentLength())) {
|
||||
// Get object from disk cache
|
||||
value = mCache.getFromDisk(mKey);
|
||||
if (value == null) {
|
||||
// Maybe bad download, remove it from disk cache
|
||||
mCache.removeFromDisk(mKey);
|
||||
} else if (mUseMemoryCache && mHelper.useMemoryCache(mKey, value)) {
|
||||
// Put it to memory
|
||||
mCache.putToMemory(mKey, value);
|
||||
}
|
||||
return value;
|
||||
} else {
|
||||
// Maybe bad download, remove it from disk cache
|
||||
mCache.removeFromDisk(mKey);
|
||||
return null;
|
||||
}
|
||||
} else if (mDataContainer != null && mDataContainer.isEnabled()) {
|
||||
// Check url Moved
|
||||
HttpUrl requestHttpUrl = request.url();
|
||||
HttpUrl responseHttpUrl = response.request().url();
|
||||
if (!responseHttpUrl.equals(requestHttpUrl)) {
|
||||
mDataContainer.onUrlMoved(mUrl, responseHttpUrl.url().toString());
|
||||
}
|
||||
|
||||
// Put to data container
|
||||
if (!putToDataContainer(is, body)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get value from data container
|
||||
InputStreamPipe isp = mDataContainer.get();
|
||||
if (isp == null) {
|
||||
return null;
|
||||
}
|
||||
value = mHelper.decode(isp);
|
||||
if (value == null) {
|
||||
mDataContainer.remove();
|
||||
} else if (mKey != null) {
|
||||
// Put to disk cache
|
||||
putFromDataContainerToDiskCache(mKey, mCache, mDataContainer);
|
||||
|
||||
if (mUseMemoryCache && mHelper.useMemoryCache(mKey, value)) {
|
||||
// Put it to memory
|
||||
mCache.putToMemory(mKey, value);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
mCall = null;
|
||||
try {
|
||||
if (is != null) {
|
||||
is.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(V holder) {
|
||||
mNetworkLoadTask = null;
|
||||
|
||||
if (isCancelled() || mStop) {
|
||||
onCancelled(holder);
|
||||
} else {
|
||||
Unikery<V> unikery = mUnikeryWeakReference.get();
|
||||
if (unikery != null && unikery.getTaskId() == mId) {
|
||||
if (holder == null || !unikery.onGetValue(holder, Conaco.SOURCE_NETWORK)) {
|
||||
unikery.onFailure();
|
||||
}
|
||||
}
|
||||
onFinish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled(V value) {
|
||||
onFinish();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Long... values) {
|
||||
Unikery unikery = mUnikeryWeakReference.get();
|
||||
if (!mStop && !isCancelled() && unikery != null && unikery.getTaskId() == mId) {
|
||||
unikery.onProgress(values[0], values[1], values[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unused", "UnusedReturnValue", "RedundantSuppression"})
|
||||
public static class Builder<T> {
|
||||
|
||||
private int mId;
|
||||
private Unikery<T> mUnikery;
|
||||
private String mKey;
|
||||
private String mUrl;
|
||||
private DataContainer mDataContainer;
|
||||
private boolean mUseMemoryCache = true;
|
||||
private boolean mUseDiskCache = true;
|
||||
private boolean mUseNetwork = true;
|
||||
private ValueHelper<T> mHelper;
|
||||
private ValueCache<T> mCache;
|
||||
private OkHttpClient mOkHttpClient;
|
||||
private Executor mDiskExecutor;
|
||||
private Executor mNetworkExecutor;
|
||||
private Conaco<T> mConaco;
|
||||
|
||||
public Builder<T> setId(int id) {
|
||||
mId = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> setUnikery(Unikery<T> unikery) {
|
||||
mUnikery = unikery;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Unikery<T> getUnikery() {
|
||||
return mUnikery;
|
||||
}
|
||||
|
||||
public Builder<T> setKey(String key) {
|
||||
mKey = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return mKey;
|
||||
}
|
||||
|
||||
public Builder<T> setUrl(String url) {
|
||||
mUrl = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return mUrl;
|
||||
}
|
||||
|
||||
public Builder<T> setDataContainer(DataContainer dataContainer) {
|
||||
mDataContainer = dataContainer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> setUseMemoryCache(boolean useMemoryCache) {
|
||||
mUseMemoryCache = useMemoryCache;
|
||||
return this;
|
||||
}
|
||||
|
||||
boolean isUseMemoryCache() {
|
||||
return mUseMemoryCache;
|
||||
}
|
||||
|
||||
public Builder<T> setUseDiskCache(boolean useDiskCache) {
|
||||
mUseDiskCache = useDiskCache;
|
||||
return this;
|
||||
}
|
||||
|
||||
boolean isUseDiskCache() {
|
||||
return mUseDiskCache;
|
||||
}
|
||||
|
||||
public Builder<T> setUseNetwork(boolean useNetwork) {
|
||||
mUseNetwork = useNetwork;
|
||||
return this;
|
||||
}
|
||||
|
||||
boolean isUseNetwork() {
|
||||
return mUseNetwork;
|
||||
}
|
||||
|
||||
Builder<T> setHelper(ValueHelper<T> helper) {
|
||||
mHelper = helper;
|
||||
return this;
|
||||
}
|
||||
|
||||
ValueHelper<T> getHelper() {
|
||||
return mHelper;
|
||||
}
|
||||
|
||||
Builder<T> setCache(ValueCache<T> cache) {
|
||||
mCache = cache;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder<T> setOkHttpClient(OkHttpClient okHttpClient) {
|
||||
mOkHttpClient = okHttpClient;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder<T> setDiskExecutor(Executor diskExecutor) {
|
||||
mDiskExecutor = diskExecutor;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder<T> setNetworkExecutor(Executor networkExecutor) {
|
||||
mNetworkExecutor = networkExecutor;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder<T> setConaco(Conaco<T> conaco) {
|
||||
mConaco = conaco;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void isValid() {
|
||||
if (mUnikery == null) {
|
||||
throw new IllegalStateException("Must set unikery");
|
||||
}
|
||||
if (mKey == null && mUrl == null && mDataContainer == null) {
|
||||
throw new IllegalStateException("At least one of mKey and mUrl and mDataContainer have to not be null");
|
||||
}
|
||||
}
|
||||
|
||||
public ConacoTask<T> build() {
|
||||
return new ConacoTask<>(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2015-2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.conaco;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.streampipe.InputStreamPipe;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
public interface DataContainer {
|
||||
|
||||
/**
|
||||
* Returns the enabled status for this DataContainer.
|
||||
*/
|
||||
boolean isEnabled();
|
||||
|
||||
/**
|
||||
* Get 304 or something like that
|
||||
*/
|
||||
void onUrlMoved(String requestUrl, String responseUrl);
|
||||
|
||||
/**
|
||||
* Save the {@code InputStream}
|
||||
*/
|
||||
boolean save(InputStream is, long length, @Nullable String mediaType, @Nullable ProgressNotifier notify);
|
||||
|
||||
/**
|
||||
* Get {@code InputStreamPipe} for saved before
|
||||
*/
|
||||
@Nullable
|
||||
InputStreamPipe get();
|
||||
|
||||
/**
|
||||
* Remove saved stuff
|
||||
*/
|
||||
void remove();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2015 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.conaco;
|
||||
|
||||
public interface ProgressNotifier {
|
||||
|
||||
void notifyProgress(long singleReceivedSize, long receivedSize, long totalSize);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.conaco;
|
||||
|
||||
import com.hippo.yorozuya.collect.Multimap;
|
||||
import com.hippo.yorozuya.collect.SparseILArray;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class Register<V> {
|
||||
|
||||
private final SparseILArray<ConacoTask<V>> mIdMap = new SparseILArray<>();
|
||||
private final Multimap<String, ConacoTask<V>> mKeyMap = Multimap.create();
|
||||
|
||||
/**
|
||||
* @return true for the key is already registered
|
||||
*/
|
||||
public synchronized boolean register(int id, ConacoTask<V> task) {
|
||||
boolean repeatedKey = false;
|
||||
String taskKey = task.getKey();
|
||||
|
||||
// For no-key task, there no need to check
|
||||
if (taskKey != null) {
|
||||
repeatedKey = mKeyMap.countElements(taskKey) != 0;
|
||||
}
|
||||
|
||||
// Append task
|
||||
mKeyMap.putElement(taskKey, task);
|
||||
mIdMap.append(id, task);
|
||||
|
||||
return repeatedKey;
|
||||
}
|
||||
|
||||
public synchronized ConacoTask<V> unregister(int id) {
|
||||
ConacoTask<V> task = mIdMap.remove(id);
|
||||
if (task != null) {
|
||||
mKeyMap.removeElement(task.getKey(), task);
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
public synchronized boolean contain(int id) {
|
||||
return mIdMap.indexOfKey(id) >= 0;
|
||||
}
|
||||
|
||||
public synchronized ConacoTask<V> getByKey(String key) {
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<ConacoTask<V>> list = mKeyMap.get(key);
|
||||
if (list != null && list.size() > 0) {
|
||||
return list.get(0);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2015 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.conaco;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public interface Unikery<V> {
|
||||
|
||||
int INVALID_ID = -1;
|
||||
|
||||
/**
|
||||
* Store the conaco task id
|
||||
*
|
||||
* @param id the conaco task id
|
||||
*/
|
||||
void setTaskId(int id);
|
||||
|
||||
/**
|
||||
* Get the conaco task id
|
||||
*
|
||||
* @return the conaco task id
|
||||
*/
|
||||
int getTaskId();
|
||||
|
||||
/**
|
||||
* On miss in the source
|
||||
*/
|
||||
void onMiss(@Conaco.Source int source);
|
||||
|
||||
/**
|
||||
* On start http request
|
||||
*/
|
||||
void onRequest();
|
||||
|
||||
/**
|
||||
* On http request progress
|
||||
*/
|
||||
void onProgress(long singleReceivedSize, long receivedSize, long totalSize);
|
||||
|
||||
/**
|
||||
* On start to wait because repeated key
|
||||
*/
|
||||
void onWait();
|
||||
|
||||
/**
|
||||
* On get the value
|
||||
*
|
||||
* @return {@code true} for the {@code Unikery} accepts the value
|
||||
* and the task ends. {@code false} for the {@code Unikery}
|
||||
* reject the value and the task goes on or failed
|
||||
*/
|
||||
boolean onGetValue(@NonNull V value, @Conaco.Source int source);
|
||||
|
||||
/**
|
||||
* On failed to get value
|
||||
*/
|
||||
void onFailure();
|
||||
|
||||
/**
|
||||
* On user cancel the conaco task
|
||||
*/
|
||||
void onCancel();
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.conaco;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.hippo.beerbelly.BeerBelly;
|
||||
import com.hippo.streampipe.InputStreamPipe;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
class ValueCache<V> extends BeerBelly<V> {
|
||||
|
||||
private final ValueHelper<V> mHelper;
|
||||
|
||||
public ValueCache(BeerBelly.BeerBellyParams params, ValueHelper<V> helper) {
|
||||
super(params);
|
||||
mHelper = helper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int sizeOf(String key, V value) {
|
||||
return mHelper.sizeOf(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void memoryEntryAdded(V value) {
|
||||
mHelper.onAddToMemoryCache(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void memoryEntryRemoved(boolean evicted, String key, V oldValue, V newValue) {
|
||||
if (oldValue != null) {
|
||||
mHelper.onRemoveFromMemoryCache(key, oldValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected V read(@NonNull InputStreamPipe isPipe) {
|
||||
return mHelper.decode(isPipe);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean write(OutputStream os, V value) {
|
||||
throw new UnsupportedOperationException("Not support write object");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.conaco;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.streampipe.InputStreamPipe;
|
||||
|
||||
public interface ValueHelper<V> {
|
||||
|
||||
/**
|
||||
* Decode object for the {@code InputStreamPipe}
|
||||
*
|
||||
* @param isPipe the {@code InputStreamPipe}
|
||||
* @return the decoded object
|
||||
*/
|
||||
@Nullable
|
||||
V decode(@NonNull InputStreamPipe isPipe);
|
||||
|
||||
/**
|
||||
* Get the size of the object
|
||||
*/
|
||||
int sizeOf(@NonNull String key, @NonNull V value);
|
||||
|
||||
/**
|
||||
* Called when the object added to memory cache
|
||||
*/
|
||||
void onAddToMemoryCache(@NonNull V oldValue);
|
||||
|
||||
/**
|
||||
* Called when the object removed from memory cache
|
||||
*/
|
||||
void onRemoveFromMemoryCache(@NonNull String key, @NonNull V oldValue);
|
||||
|
||||
/**
|
||||
* Use cache memory or not. Sometimes large object
|
||||
* should not be stored in memory cache.
|
||||
*/
|
||||
boolean useMemoryCache(@NonNull String key, V holder);
|
||||
}
|
||||
@@ -244,8 +244,10 @@ public class EhEngine {
|
||||
}
|
||||
}
|
||||
|
||||
for (GalleryInfo info : list) {
|
||||
info.thumb = EhUrl.getFixedPreviewThumbUrl(info.thumb);
|
||||
if (Settings.getFixThumbUrl()) {
|
||||
for (GalleryInfo info : list) {
|
||||
info.thumb = EhUrl.getFixedPreviewThumbUrl(info.thumb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user