From 08f94ed17f9514da19214bcba22bb70a92a8bd38 Mon Sep 17 00:00:00 2001 From: Floens Date: Sat, 31 Jan 2015 16:15:37 +0100 Subject: [PATCH] Swap Ion for okhttp. --- Clover/app/build.gradle | 4 +- Clover/app/proguard.cfg | 3 + .../java/org/floens/chan/ChanApplication.java | 10 - .../chan/ui/view/ThumbnailImageView.java | 14 +- .../java/org/floens/chan/utils/FileCache.java | 238 ++++++++++++++---- .../org/floens/chan/utils/ImageSaver.java | 4 +- 6 files changed, 198 insertions(+), 75 deletions(-) diff --git a/Clover/app/build.gradle b/Clover/app/build.gradle index 339a5adb..e772fb32 100644 --- a/Clover/app/build.gradle +++ b/Clover/app/build.gradle @@ -9,7 +9,7 @@ android { targetSdkVersion 21 versionName "v1.2.6" - versionCode 44 + versionCode 45 } compileOptions { @@ -71,8 +71,8 @@ dependencies { compile 'com.j256.ormlite:ormlite-core:4.48' compile 'com.j256.ormlite:ormlite-android:4.48' compile 'com.android.support:support-v13:18.0.0' - compile 'com.koushikdutta.ion:ion:2.0.5' compile 'pl.droidsonroids.gif:android-gif-drawable:1.0.12' + compile 'com.squareup.okhttp:okhttp:2.2.0' compile files('libs/httpclientandroidlib-1.2.1.jar') } diff --git a/Clover/app/proguard.cfg b/Clover/app/proguard.cfg index 7f3763cc..b1606d14 100644 --- a/Clover/app/proguard.cfg +++ b/Clover/app/proguard.cfg @@ -132,3 +132,6 @@ } -keep public class pl.droidsonroids.gif.GifIOException{*;} + +-dontwarn java.nio.** +-dontwarn org.codehaus.mojo.** diff --git a/Clover/app/src/main/java/org/floens/chan/ChanApplication.java b/Clover/app/src/main/java/org/floens/chan/ChanApplication.java index f3c5a7dd..a03f3393 100644 --- a/Clover/app/src/main/java/org/floens/chan/ChanApplication.java +++ b/Clover/app/src/main/java/org/floens/chan/ChanApplication.java @@ -25,9 +25,6 @@ import android.view.ViewConfiguration; import com.android.volley.RequestQueue; import com.android.volley.toolbox.ImageLoader; import com.android.volley.toolbox.Volley; -import com.koushikdutta.ion.Ion; -import com.koushikdutta.ion.builder.Builders; -import com.koushikdutta.ion.builder.LoadBuilder; import org.floens.chan.chan.ChanUrls; import org.floens.chan.core.ChanPreferences; @@ -101,10 +98,6 @@ public class ChanApplication extends Application { return fileCache; } - public static LoadBuilder getIon() { - return Ion.getDefault(getInstance()).build(getInstance()); - } - public static SharedPreferences getPreferences() { return PreferenceManager.getDefaultSharedPreferences(instance); } @@ -134,9 +127,6 @@ public class ChanApplication extends Application { IconCache.createIcons(this); - Ion.getDefault(getInstance()).getCache().clear(); - Ion.getDefault(getInstance()).getCache().setMaxSize(1 * 1024 * 1024); - File cacheDir = getExternalCacheDir() != null ? getExternalCacheDir() : getCacheDir(); volleyRequestQueue = Volley.newRequestQueue(this, null, new File(cacheDir, Volley.DEFAULT_CACHE_DIR), VOLLEY_CACHE_SIZE); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/ThumbnailImageView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/ThumbnailImageView.java index fd9945da..03cae364 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/ThumbnailImageView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/ThumbnailImageView.java @@ -33,7 +33,6 @@ import android.widget.VideoView; import com.android.volley.Request; import com.android.volley.VolleyError; import com.android.volley.toolbox.ImageLoader.ImageContainer; -import com.koushikdutta.async.future.Future; import org.floens.chan.ChanApplication; import org.floens.chan.R; @@ -44,6 +43,7 @@ import org.floens.chan.utils.Utils; import java.io.File; import java.io.IOException; +import java.util.concurrent.Future; import pl.droidsonroids.gif.GifDrawable; import pl.droidsonroids.gif.GifImageView; @@ -61,7 +61,7 @@ public class ThumbnailImageView extends LoadView implements View.OnClickListener private boolean thumbnailNeeded = true; private Request imageRequest; - private Future ionRequest; + private Future request; private VideoView videoView; private GifDrawable gifDrawable; @@ -121,7 +121,7 @@ public class ThumbnailImageView extends LoadView implements View.OnClickListener } callback.setProgress(true); - ionRequest = ChanApplication.getFileCache().downloadFile(getContext(), imageUrl, new FileCache.DownloadedCallback() { + request = ChanApplication.getFileCache().downloadFile(imageUrl, new FileCache.DownloadedCallback() { @Override public void onProgress(long downloaded, long total, boolean done) { if (done) { @@ -177,7 +177,7 @@ public class ThumbnailImageView extends LoadView implements View.OnClickListener } callback.setProgress(true); - ionRequest = ChanApplication.getFileCache().downloadFile(getContext(), gifUrl, new FileCache.DownloadedCallback() { + request = ChanApplication.getFileCache().downloadFile(gifUrl, new FileCache.DownloadedCallback() { @Override public void onProgress(long downloaded, long total, boolean done) { if (done) { @@ -228,7 +228,7 @@ public class ThumbnailImageView extends LoadView implements View.OnClickListener public void setVideo(String videoUrl) { callback.setProgress(true); - ionRequest = ChanApplication.getFileCache().downloadFile(getContext(), videoUrl, new FileCache.DownloadedCallback() { + request = ChanApplication.getFileCache().downloadFile(videoUrl, new FileCache.DownloadedCallback() { @Override public void onProgress(long downloaded, long total, boolean done) { if (done) { @@ -327,8 +327,8 @@ public class ThumbnailImageView extends LoadView implements View.OnClickListener imageRequest = null; } - if (ionRequest != null) { - ionRequest.cancel(true); + if (request != null) { + request.cancel(true); } } diff --git a/Clover/app/src/main/java/org/floens/chan/utils/FileCache.java b/Clover/app/src/main/java/org/floens/chan/utils/FileCache.java index 3ea6ee97..554d67a3 100644 --- a/Clover/app/src/main/java/org/floens/chan/utils/FileCache.java +++ b/Clover/app/src/main/java/org/floens/chan/utils/FileCache.java @@ -1,21 +1,32 @@ package org.floens.chan.utils; -import android.content.Context; import android.util.Log; -import com.koushikdutta.async.future.Future; -import com.koushikdutta.async.future.FutureCallback; -import com.koushikdutta.ion.ProgressCallback; -import com.koushikdutta.ion.Response; - -import org.floens.chan.ChanApplication; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.Response; +import com.squareup.okhttp.ResponseBody; +import com.squareup.okhttp.internal.Util; +import java.io.BufferedOutputStream; +import java.io.Closeable; import java.io.File; -import java.util.concurrent.CancellationException; +import java.io.FileOutputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import okio.BufferedSource; public class FileCache { private static final String TAG = "FileCache"; + private static final ExecutorService executor = Executors.newFixedThreadPool(2); + + private OkHttpClient httpClient; + private final File directory; private final long maxSize; @@ -25,6 +36,8 @@ public class FileCache { this.directory = directory; this.maxSize = maxSize; + httpClient = new OkHttpClient(); + makeDir(); calculateSize(); } @@ -47,57 +60,16 @@ public class FileCache { return file.delete(); } - public Future> downloadFile(Context context, String url, final DownloadedCallback callback) { - File file = get(url); + public Future downloadFile(final String urlString, final DownloadedCallback callback) { + File file = get(urlString); if (file.exists()) { file.setLastModified(Time.get()); callback.onProgress(0, 0, true); callback.onSuccess(file); return null; } else { - return ChanApplication.getIon() - .load(url) - .progress(new ProgressCallback() { - @Override - public void onProgress(final long downloaded, final long total) { - Utils.runOnUiThread(new Runnable() { - @Override - public void run() { - callback.onProgress(downloaded, total, false); - } - }); - } - }) - .write(file) - .withResponse() - .setCallback(new FutureCallback>() { - @Override - public void onCompleted(Exception e, Response result) { - callback.onProgress(0, 0, true); - - if (result != null && result.getHeaders() != null && result.getHeaders().code() / 100 != 2) { - if (result.getResult() != null) { - delete(result.getResult()); - } - callback.onFail(true); - return; - } - - if (e != null && !(e instanceof CancellationException)) { - e.printStackTrace(); - if (result != null && result.getResult() != null) { - delete(result.getResult()); - } - callback.onFail(false); - return; - } - - if (result != null && result.getResult() != null) { - put(result.getResult()); - callback.onSuccess(result.getResult()); - } - } - }); + FileCacheDownloader downloader = new FileCacheDownloader(this, urlString, file, callback); + return executor.submit(downloader); } } @@ -163,4 +135,164 @@ public class FileCache { public void onFail(boolean notFound); } + + private static class FileCacheDownloader implements Runnable { + private final FileCache fileCache; + private final String url; + private final File output; + private final DownloadedCallback callback; + private boolean cancelled = false; + + private Closeable downloadInput; + private Closeable downloadOutput; + + public FileCacheDownloader(FileCache fileCache, String url, File output, DownloadedCallback callback) { + this.fileCache = fileCache; + this.url = url; + this.output = output; + this.callback = callback; + } + + public void run() { + try { + execute(); + } catch (InterruptedIOException | InterruptedException e) { + cancelDueToCancellation(e); + } catch (Exception e) { + cancelDueToException(e); + } + } + + private void cancelDueToException(Exception e) { + if (cancelled) return; + cancelled = true; + + Log.w(TAG, "IOException downloading file", e); + + purgeOutput(); + closeStreams(); + + post(new Runnable() { + @Override + public void run() { + callback.onProgress(0, 0, true); + callback.onFail(false); + } + }); + } + + private void cancelDueToHttpError(final int code) { + if (cancelled) return; + cancelled = true; + + Log.w(TAG, "Cancel due to http error, code: " + code); + + purgeOutput(); + closeStreams(); + + post(new Runnable() { + @Override + public void run() { + callback.onProgress(0, 0, true); + callback.onFail(code == 404); + } + }); + } + + private void cancelDueToCancellation(Exception e) { + if (cancelled) return; + cancelled = true; + + Log.d(TAG, "Cancel due to cancellation"); + + purgeOutput(); + closeStreams(); + + // No callback + } + + private void closeStreams() { + Util.closeQuietly(downloadInput); + Util.closeQuietly(downloadOutput); + } + + private void purgeOutput() { + if (output.exists()) { + if (!output.delete()) { + Log.w(TAG, "Could not delete the file in purgeOutput"); + } + } + } + + private long progressDownloaded; + private long progressTotal; + private boolean progressDone; + private final Runnable progressRunnable = new Runnable() { + @Override + public void run() { + callback.onProgress(progressDownloaded, progressTotal, progressDone); + } + }; + + private void progress(long downloaded, long total, boolean done) { + progressDownloaded = downloaded; + progressTotal = total; + progressDone = done; + post(progressRunnable); + } + + private void success() { + fileCache.put(output); + + post(new Runnable() { + @Override + public void run() { + callback.onProgress(0, 0, true); + callback.onSuccess(output); + } + }); + } + + private void post(Runnable runnable) { + Utils.runOnUiThread(runnable); + } + + private void execute() throws Exception { + Request request = new Request.Builder().url(url).build(); + + Response response = fileCache.httpClient.newCall(request).execute(); + if (!response.isSuccessful()) { + cancelDueToHttpError(response.code()); + return; + } + + ResponseBody body = response.body(); + long contentLength = body.contentLength(); + BufferedSource source = body.source(); + OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(output)); + + downloadInput = source; + downloadOutput = outputStream; + + int read; + long total = 0; + long totalLast = 0; + byte[] buffer = new byte[4096]; + while ((read = source.read(buffer)) != -1) { + outputStream.write(buffer, 0, read); + total += read; + + if (total >= totalLast + 16384) { + totalLast = total; + progress(total, contentLength, false); + } + + if (Thread.currentThread().isInterrupted()) throw new InterruptedIOException(); + } + + closeStreams(); + + success(); + } + } } diff --git a/Clover/app/src/main/java/org/floens/chan/utils/ImageSaver.java b/Clover/app/src/main/java/org/floens/chan/utils/ImageSaver.java index b9e9c526..59e4afd4 100644 --- a/Clover/app/src/main/java/org/floens/chan/utils/ImageSaver.java +++ b/Clover/app/src/main/java/org/floens/chan/utils/ImageSaver.java @@ -28,8 +28,6 @@ import android.media.MediaScannerConnection; import android.net.Uri; import android.widget.Toast; -import com.koushikdutta.async.future.Future; - import org.floens.chan.ChanApplication; import org.floens.chan.R; import org.floens.chan.core.ChanPreferences; @@ -67,7 +65,7 @@ public class ImageSaver { } public void saveImage(final Context context, String imageUrl, final String name, final String extension, final boolean share) { - Future ionRequest = ChanApplication.getFileCache().downloadFile(context, imageUrl, new FileCache.DownloadedCallback() { + ChanApplication.getFileCache().downloadFile(imageUrl, new FileCache.DownloadedCallback() { @Override @SuppressWarnings("deprecation") public void onProgress(long downloaded, long total, boolean done) {