Split net.Downloader into abstract Downloader and concrete HttpDownloader.
This will allow the more general, non HTTP related stuff (progress events, stream copying) to occur in a separate base class. HTTP specific stuff (HTTP status codes, etag cache checking) is done in the HTTPDownloader base class.
This commit is contained in:
parent
d850ae2307
commit
2c80f1a758
@ -5,24 +5,18 @@ import java.net.*;
|
||||
import android.content.*;
|
||||
import org.fdroid.fdroid.*;
|
||||
|
||||
public class Downloader {
|
||||
public abstract class Downloader {
|
||||
|
||||
private static final String HEADER_IF_NONE_MATCH = "If-None-Match";
|
||||
private static final String HEADER_FIELD_ETAG = "ETag";
|
||||
|
||||
private URL sourceUrl;
|
||||
private OutputStream outputStream;
|
||||
private ProgressListener progressListener = null;
|
||||
private ProgressListener.Event progressEvent = null;
|
||||
private String eTag = null;
|
||||
private final File outputFile;
|
||||
private HttpURLConnection connection;
|
||||
private int statusCode = -1;
|
||||
|
||||
public abstract InputStream inputStream() throws IOException;
|
||||
|
||||
// The context is required for opening the file to write to.
|
||||
public Downloader(String source, String destFile, Context ctx)
|
||||
public Downloader(String destFile, Context ctx)
|
||||
throws FileNotFoundException, MalformedURLException {
|
||||
sourceUrl = new URL(source);
|
||||
outputStream = ctx.openFileOutput(destFile, Context.MODE_PRIVATE);
|
||||
outputFile = new File(ctx.getFilesDir() + File.separator + destFile);
|
||||
}
|
||||
@ -32,16 +26,14 @@ public class Downloader {
|
||||
* you are done*.
|
||||
* @see org.fdroid.fdroid.net.Downloader#getFile()
|
||||
*/
|
||||
public Downloader(String source, Context ctx) throws IOException {
|
||||
public Downloader(Context ctx) throws IOException {
|
||||
// http://developer.android.com/guide/topics/data/data-storage.html#InternalCache
|
||||
outputFile = File.createTempFile("dl-", "", ctx.getCacheDir());
|
||||
outputStream = new FileOutputStream(outputFile);
|
||||
sourceUrl = new URL(source);
|
||||
}
|
||||
|
||||
public Downloader(String source, OutputStream output)
|
||||
public Downloader(OutputStream output)
|
||||
throws MalformedURLException {
|
||||
sourceUrl = new URL(source);
|
||||
outputStream = output;
|
||||
outputFile = null;
|
||||
}
|
||||
@ -61,82 +53,25 @@ public class Downloader {
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only available after downloading a file.
|
||||
*/
|
||||
public int getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* If you ask for the eTag before calling download(), you will get the
|
||||
* same one you passed in (if any). If you call it after download(), you
|
||||
* will get the new eTag from the server, or null if there was none.
|
||||
*/
|
||||
public String getETag() {
|
||||
return eTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this eTag matches that returned by the server, then no download will
|
||||
* take place, and a status code of 304 will be returned by download().
|
||||
*/
|
||||
public void setETag(String eTag) {
|
||||
this.eTag = eTag;
|
||||
}
|
||||
|
||||
// Get a remote file. Returns the HTTP response code.
|
||||
// If 'etag' is not null, it's passed to the server as an If-None-Match
|
||||
// header, in which case expect a 304 response if nothing changed.
|
||||
// In the event of a 200 response ONLY, 'retag' (which should be passed
|
||||
// empty) may contain an etag value for the response, or it may be left
|
||||
// empty if none was available.
|
||||
public int download() throws IOException {
|
||||
connection = (HttpURLConnection)sourceUrl.openConnection();
|
||||
setupCacheCheck();
|
||||
statusCode = connection.getResponseCode();
|
||||
if (statusCode == 200) {
|
||||
setupProgressListener();
|
||||
InputStream input = null;
|
||||
try {
|
||||
input = connection.getInputStream();
|
||||
Utils.copy(input, outputStream,
|
||||
progressListener, progressEvent);
|
||||
} finally {
|
||||
Utils.closeQuietly(outputStream);
|
||||
Utils.closeQuietly(input);
|
||||
}
|
||||
updateCacheCheck();
|
||||
}
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
protected void setupCacheCheck() {
|
||||
if (eTag != null) {
|
||||
connection.setRequestProperty(HEADER_IF_NONE_MATCH, eTag);
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateCacheCheck() {
|
||||
eTag = connection.getHeaderField(HEADER_FIELD_ETAG);
|
||||
}
|
||||
|
||||
protected void setupProgressListener() {
|
||||
private void setupProgressListener() {
|
||||
if (progressListener != null && progressEvent != null) {
|
||||
// Testing in the emulator for me, showed that figuring out the
|
||||
// filesize took about 1 to 1.5 seconds.
|
||||
// To put this in context, downloading a repo of:
|
||||
// - 400k takes ~6 seconds
|
||||
// - 5k takes ~3 seconds
|
||||
// on my connection. I think the 1/1.5 seconds is worth it,
|
||||
// because as the repo grows, the tradeoff will
|
||||
// become more worth it.
|
||||
progressEvent.total = connection.getContentLength();
|
||||
progressEvent.total = totalDownloadSize();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasChanged() {
|
||||
return this.statusCode == 200;
|
||||
protected abstract int totalDownloadSize();
|
||||
|
||||
public void download() throws IOException {
|
||||
setupProgressListener();
|
||||
InputStream input = null;
|
||||
try {
|
||||
input = inputStream();
|
||||
Utils.copy(input, outputStream,
|
||||
progressListener, progressEvent);
|
||||
} finally {
|
||||
Utils.closeQuietly(outputStream);
|
||||
Utils.closeQuietly(input);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
115
src/org/fdroid/fdroid/net/HttpDownloader.java
Normal file
115
src/org/fdroid/fdroid/net/HttpDownloader.java
Normal file
@ -0,0 +1,115 @@
|
||||
package org.fdroid.fdroid.net;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
public class HttpDownloader extends Downloader {
|
||||
|
||||
private static final String HEADER_IF_NONE_MATCH = "If-None-Match";
|
||||
private static final String HEADER_FIELD_ETAG = "ETag";
|
||||
|
||||
private URL sourceUrl;
|
||||
private String eTag = null;
|
||||
private HttpURLConnection connection;
|
||||
private int statusCode = -1;
|
||||
|
||||
// The context is required for opening the file to write to.
|
||||
public HttpDownloader(String source, String destFile, Context ctx)
|
||||
throws FileNotFoundException, MalformedURLException {
|
||||
super(destFile, ctx);
|
||||
sourceUrl = new URL(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads to a temporary file, which *you must delete yourself when
|
||||
* you are done*.
|
||||
* @see org.fdroid.fdroid.net.HttpDownloader#getFile()
|
||||
*/
|
||||
public HttpDownloader(String source, Context ctx) throws IOException {
|
||||
super(ctx);
|
||||
sourceUrl = new URL(source);
|
||||
}
|
||||
|
||||
public HttpDownloader(String source, OutputStream output)
|
||||
throws MalformedURLException {
|
||||
super(output);
|
||||
sourceUrl = new URL(source);
|
||||
}
|
||||
|
||||
public InputStream inputStream() throws IOException {
|
||||
return connection.getInputStream();
|
||||
}
|
||||
|
||||
// Get a remote file. Returns the HTTP response code.
|
||||
// If 'etag' is not null, it's passed to the server as an If-None-Match
|
||||
// header, in which case expect a 304 response if nothing changed.
|
||||
// In the event of a 200 response ONLY, 'retag' (which should be passed
|
||||
// empty) may contain an etag value for the response, or it may be left
|
||||
// empty if none was available.
|
||||
public int downloadHttpFile() throws IOException {
|
||||
connection = (HttpURLConnection)sourceUrl.openConnection();
|
||||
setupCacheCheck();
|
||||
statusCode = connection.getResponseCode();
|
||||
if (statusCode == 200) {
|
||||
download();
|
||||
updateCacheCheck();
|
||||
}
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only available after downloading a file.
|
||||
*/
|
||||
public int getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* If you ask for the eTag before calling download(), you will get the
|
||||
* same one you passed in (if any). If you call it after download(), you
|
||||
* will get the new eTag from the server, or null if there was none.
|
||||
*/
|
||||
public String getETag() {
|
||||
return eTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this eTag matches that returned by the server, then no download will
|
||||
* take place, and a status code of 304 will be returned by download().
|
||||
*/
|
||||
public void setETag(String eTag) {
|
||||
this.eTag = eTag;
|
||||
}
|
||||
|
||||
|
||||
protected void setupCacheCheck() {
|
||||
if (eTag != null) {
|
||||
connection.setRequestProperty(HEADER_IF_NONE_MATCH, eTag);
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateCacheCheck() {
|
||||
eTag = connection.getHeaderField(HEADER_FIELD_ETAG);
|
||||
}
|
||||
|
||||
// Testing in the emulator for me, showed that figuring out the
|
||||
// filesize took about 1 to 1.5 seconds.
|
||||
// To put this in context, downloading a repo of:
|
||||
// - 400k takes ~6 seconds
|
||||
// - 5k takes ~3 seconds
|
||||
// on my connection. I think the 1/1.5 seconds is worth it,
|
||||
// because as the repo grows, the tradeoff will
|
||||
// become more worth it.
|
||||
protected int totalDownloadSize() {
|
||||
return connection.getContentLength();
|
||||
}
|
||||
|
||||
public boolean hasChanged() {
|
||||
return this.statusCode == 200;
|
||||
}
|
||||
|
||||
}
|
@ -12,6 +12,7 @@ import org.fdroid.fdroid.data.App;
|
||||
import org.fdroid.fdroid.data.Repo;
|
||||
import org.fdroid.fdroid.data.RepoProvider;
|
||||
import org.fdroid.fdroid.net.Downloader;
|
||||
import org.fdroid.fdroid.net.HttpDownloader;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.XMLReader;
|
||||
@ -84,11 +85,11 @@ abstract public class RepoUpdater {
|
||||
|
||||
protected abstract String getIndexAddress();
|
||||
|
||||
protected Downloader downloadIndex() throws UpdateException {
|
||||
protected HttpDownloader downloadIndex() throws UpdateException {
|
||||
Bundle progressData = createProgressData(repo.address);
|
||||
Downloader downloader = null;
|
||||
HttpDownloader downloader = null;
|
||||
try {
|
||||
downloader = new Downloader(getIndexAddress(), context);
|
||||
downloader = new HttpDownloader(getIndexAddress(), context);
|
||||
downloader.setETag(repo.lastetag);
|
||||
|
||||
if (isInteractive()) {
|
||||
@ -98,7 +99,7 @@ abstract public class RepoUpdater {
|
||||
downloader.setProgressListener(progressListener, event);
|
||||
}
|
||||
|
||||
int status = downloader.download();
|
||||
int status = downloader.downloadHttpFile();
|
||||
|
||||
if (status == 304) {
|
||||
// The index is unchanged since we last read it. We just mark
|
||||
@ -169,7 +170,7 @@ abstract public class RepoUpdater {
|
||||
File indexFile = null;
|
||||
try {
|
||||
|
||||
Downloader downloader = downloadIndex();
|
||||
HttpDownloader downloader = downloadIndex();
|
||||
hasChanged = downloader.hasChanged();
|
||||
|
||||
if (hasChanged) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user