Created
March 25, 2016 12:06
-
-
Save allco/541a1f7f8c138ee60290 to your computer and use it in GitHub Desktop.
A way of implementation of UrlConnectionDownloader
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.asdevel.chatlib.utils; | |
import android.content.Context; | |
import android.net.Uri; | |
import android.support.annotation.NonNull; | |
import android.support.annotation.Nullable; | |
import com.asdevel.chatlib.BuildConfig; | |
import com.asdevel.chatlib.Constants; | |
import com.jakewharton.disklrucache.DiskLruCache; | |
import com.squareup.picasso.UrlConnectionDownloader; | |
import java.io.BufferedOutputStream; | |
import java.io.File; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.OutputStream; | |
import java.net.HttpURLConnection; | |
import java.security.MessageDigest; | |
import java.util.HashMap; | |
import java.util.Map; | |
import static android.text.TextUtils.isEmpty; | |
import static com.asdevel.chatlib.tools.Logger.log; | |
import static com.asdevel.chatlib.tools.Logger.logWrongArg; | |
import static com.asdevel.chatlib.tools.Logger.logWrongState; | |
import static com.asdevel.chatlib.utils.GsonHandler.toJson; | |
public class PicassoBitmapDownloader extends UrlConnectionDownloader { | |
private static boolean DEBUG = ChatUtils.DEBUG; | |
private static final int MIN_DISK_CACHE_SIZE = 5 * 1024 * 1024; // 5MB | |
private static final int MAX_DISK_CACHE_SIZE = 50 * 1024 * 1024; // 50MB | |
private HashMap<String, String> mapHeaders; | |
private Context context; | |
@Nullable private DiskLruCache diskCache; | |
public class IfModifiedResponse extends Response { | |
private final String ifModifiedSinceDate; | |
public IfModifiedResponse(InputStream stream, boolean loadedFromCache, long contentLength, String ifModifiedSinceDate) { | |
super(stream, loadedFromCache, contentLength); | |
this.ifModifiedSinceDate = ifModifiedSinceDate; | |
} | |
public String getIfModifiedSinceDate() { | |
return ifModifiedSinceDate; | |
} | |
} | |
private PicassoBitmapDownloader(Context context) { | |
super(context); | |
this.context = context; | |
} | |
@Nullable | |
public static PicassoBitmapDownloader newInstance(@Nullable Context context) { | |
if (context == null) { | |
logWrongArg(); | |
return null; | |
} | |
return new PicassoBitmapDownloader(context); | |
} | |
public PicassoBitmapDownloader addHeaders(@NonNull String header, @NonNull String value) { | |
if (isEmpty(header) || isEmpty(value)) { | |
logWrongArg(); | |
return this; | |
} | |
if (mapHeaders == null) { | |
mapHeaders = new HashMap<>(); | |
} | |
mapHeaders.put(header, value); | |
return this; | |
} | |
@Override | |
public Response load(final Uri uri, int networkPolicy) throws IOException { | |
final String key = getKey(uri); | |
{ | |
Response cachedResponse = getCachedBitmap(key); | |
if (cachedResponse != null) { | |
if (DEBUG) { log("received from cache uri[" + uri + "]"); } | |
return cachedResponse; | |
} | |
} | |
IfModifiedResponse response; | |
try { | |
response = _load(uri); | |
} | |
catch (IOException e) { | |
log(uri); | |
log(e); | |
throw e; | |
} | |
if (cacheBitmap(key, response.getInputStream(), response.getIfModifiedSinceDate())) { | |
if (DEBUG) { log("written to cache uri[" + uri + "]"); } | |
IfModifiedResponse cachedResponse = getCachedBitmap(key); | |
if (cachedResponse != null) { | |
return cachedResponse; | |
} | |
} | |
logWrongState("cache writing skipped"); | |
return response; | |
} | |
@NonNull | |
protected IfModifiedResponse _load(Uri uri) throws IOException { | |
HttpURLConnection connection = openConnection(uri); | |
int responseCode = connection.getResponseCode(); | |
if (responseCode >= 300) { | |
connection.disconnect(); | |
throw new ResponseException(responseCode + " " + connection.getResponseMessage(), | |
0, responseCode); | |
} | |
long contentLength = connection.getHeaderFieldInt("Content-Length", -1); | |
String lastModified = connection.getHeaderField(Constants.HEADER_LAST_MODIFIED); | |
return new IfModifiedResponse(connection.getInputStream(), false, contentLength, lastModified); | |
} | |
@Override | |
protected HttpURLConnection openConnection(Uri path) throws IOException { | |
HttpURLConnection conn = super.openConnection(path); | |
if (mapHeaders != null) { | |
for (Map.Entry<String, String> el : mapHeaders.entrySet()) { | |
conn.addRequestProperty(el.getKey(), el.getValue()); | |
} | |
} | |
DiskLruCache diskCache = getDiskCache(); | |
DiskLruCache.Snapshot snapshot = diskCache == null ? null : diskCache.get(getKey(path)); | |
if (snapshot != null) { | |
String ifModifiedSince = snapshot.getString(1); | |
if (!isEmpty(ifModifiedSince)) { | |
conn.addRequestProperty(Constants.HEADER_IF_MODIFIED_SINCE, ifModifiedSince); | |
} | |
} | |
if (DEBUG) { | |
log("path[" + path + "]\nheaders:" + toJson(conn.getRequestProperties())); | |
} | |
return conn; | |
} | |
@Override public void shutdown() { | |
try { | |
if (diskCache != null) { | |
diskCache.flush(); | |
diskCache.close(); | |
} | |
} | |
catch (IOException e) { | |
log(e); | |
} | |
super.shutdown(); | |
} | |
public boolean cacheBitmap(@Nullable String key, @Nullable InputStream inputStream, @Nullable String ifModifiedSince) { | |
if (inputStream == null || isEmpty(key)) { | |
logWrongArg(); | |
return false; | |
} | |
OutputStream outputStream = null; | |
DiskLruCache.Editor edit = null; | |
try { | |
DiskLruCache diskCache = getDiskCache(); | |
edit = diskCache == null ? null : diskCache.edit(key); | |
outputStream = edit == null ? null : new BufferedOutputStream(edit.newOutputStream(0)); | |
if (outputStream == null) { | |
return false; | |
} | |
ChatUtils.copy(inputStream, outputStream); | |
outputStream.flush(); | |
edit.set(1, ifModifiedSince == null ? "" : ifModifiedSince); | |
edit.commit(); | |
return true; | |
} | |
catch (Exception e) { | |
log(e); | |
} | |
finally { | |
if (edit != null) { | |
edit.abortUnlessCommitted(); | |
} | |
ChatUtils.closeQuietly(outputStream); | |
} | |
return false; | |
} | |
@Nullable | |
public IfModifiedResponse getCachedBitmap(String key) { | |
try { | |
DiskLruCache diskCache = getDiskCache(); | |
DiskLruCache.Snapshot snapshot = diskCache == null ? null : diskCache.get(key); | |
InputStream inputStream = snapshot == null ? null : snapshot.getInputStream(0); | |
if (inputStream == null) { | |
return null; | |
} | |
return new IfModifiedResponse(inputStream, true, snapshot.getLength(0), snapshot.getString(1)); | |
} | |
catch (Exception e) { | |
log(e); | |
} | |
return null; | |
} | |
@Nullable | |
synchronized public DiskLruCache getDiskCache() { | |
if (diskCache == null) { | |
try { | |
File file = new File(context.getCacheDir() + "/images"); | |
if (!file.exists()) { | |
//noinspection ResultOfMethodCallIgnored | |
file.mkdirs(); | |
} | |
long maxSize = calculateDiskCacheSize(file); | |
if (DEBUG) { | |
log("Opened cache at: " + file.getAbsolutePath() + " max_size: " + maxSize); | |
} | |
diskCache = DiskLruCache.open(file, BuildConfig.VERSION_CODE, 2, maxSize); | |
} | |
catch (Exception e) { | |
log(e); | |
} | |
} | |
return diskCache; | |
} | |
@NonNull | |
private String getKey(@NonNull Uri uri) { | |
String key = md5(uri.toString()); | |
return isEmpty(key) ? String.valueOf(uri.hashCode()) : key; | |
} | |
@Nullable | |
public static String md5(final String toEncrypt) { | |
try { | |
final MessageDigest digest = MessageDigest.getInstance("md5"); | |
digest.update(toEncrypt.getBytes()); | |
final byte[] bytes = digest.digest(); | |
final StringBuilder sb = new StringBuilder(); | |
for (byte aByte : bytes) { | |
sb.append(String.format("%02X", aByte)); | |
} | |
return sb.toString().toLowerCase(); | |
} | |
catch (Exception e) { | |
log(e); | |
return null; | |
} | |
} | |
static long calculateDiskCacheSize(File dir) { | |
long available = ChatUtils.bytesAvailable(dir); | |
// Target 2% of the total space. | |
long size = available / 50; | |
// Bound inside min/max size for disk cache. | |
return Math.max(Math.min(size, MAX_DISK_CACHE_SIZE), MIN_DISK_CACHE_SIZE); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment