Merge branch 'improve-downloader-close-handling' into 'master'
Make closing of `Downloader`s more concise. *NOTE: This is only a WIP branch in so far as I haven't tested it, as it was done on the train before my holiday. The code is final though, so if people are happy it works, please merge.* The base `Downloader` class now wraps the `InputStream` returned by any child classes, in order to notify the child class when that stream is closed. This prevents each child class having to figure out a way to be notified of this. This helps keep the API concise, because close handling is dealt with without the need to add any public methods to the `Downloader` class hierarchy. Also removed some dead code which was unused. See merge request !139
This commit is contained in:
commit
47196234fe
@ -108,7 +108,6 @@ public class RepoUpdater {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (downloader != null && downloader.getFile() != null) {
|
if (downloader != null && downloader.getFile() != null) {
|
||||||
downloader.getFile().delete();
|
downloader.getFile().delete();
|
||||||
downloader.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new UpdateException(repo, "Error getting index file from " + repo.address, e);
|
throw new UpdateException(repo, "Error getting index file from " + repo.address, e);
|
||||||
@ -133,8 +132,6 @@ public class RepoUpdater {
|
|||||||
// successful download, then we will have a file ready to use:
|
// successful download, then we will have a file ready to use:
|
||||||
processDownloadedFile(downloader.getFile(), downloader.getCacheTag());
|
processDownloadedFile(downloader.getFile(), downloader.getCacheTag());
|
||||||
}
|
}
|
||||||
|
|
||||||
downloader.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processDownloadedFile(File downloadedFile, String cacheTag) throws UpdateException {
|
protected void processDownloadedFile(File downloadedFile, String cacheTag) throws UpdateException {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.fdroid.fdroid.net;
|
package org.fdroid.fdroid.net;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.apache.commons.io.input.BoundedInputStream;
|
import org.apache.commons.io.input.BoundedInputStream;
|
||||||
@ -31,12 +32,11 @@ public class BluetoothDownloader extends Downloader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream getInputStream() throws IOException {
|
protected InputStream getDownloadersInputStream() throws IOException {
|
||||||
Request request = Request.createGET(sourcePath, connection);
|
Request request = Request.createGET(sourcePath, connection);
|
||||||
Response response = request.send();
|
Response response = request.send();
|
||||||
fileDetails = response.toFileDetails();
|
fileDetails = response.toFileDetails();
|
||||||
|
|
||||||
|
|
||||||
// TODO: Manage the dependency which includes this class better?
|
// TODO: Manage the dependency which includes this class better?
|
||||||
// Right now, I only needed the one class from apache commons.
|
// Right now, I only needed the one class from apache commons.
|
||||||
// There are countless classes online which provide this functionality,
|
// There are countless classes online which provide this functionality,
|
||||||
@ -53,10 +53,8 @@ public class BluetoothDownloader extends Downloader {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* May return null if an error occurred while getting file details.
|
* May return null if an error occurred while getting file details.
|
||||||
* TODO: Should we throw an exception? Everywhere else in this blue package throws IO exceptions weely-neely.
|
|
||||||
* Will probably require some thought as to how the API looks, with regards to all of the public methods
|
|
||||||
* and their signatures.
|
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
public FileDetails getFileDetails() {
|
public FileDetails getFileDetails() {
|
||||||
if (fileDetails == null) {
|
if (fileDetails == null) {
|
||||||
Utils.debugLog(TAG, "Going to Bluetooth \"server\" to get file details.");
|
Utils.debugLog(TAG, "Going to Bluetooth \"server\" to get file details.");
|
||||||
@ -71,12 +69,14 @@ public class BluetoothDownloader extends Downloader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasChanged() {
|
public boolean hasChanged() {
|
||||||
return getFileDetails().getCacheTag() == null || getFileDetails().getCacheTag().equals(getCacheTag());
|
FileDetails details = getFileDetails();
|
||||||
|
return details == null || details.getCacheTag() == null || details.getCacheTag().equals(getCacheTag());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int totalDownloadSize() {
|
public int totalDownloadSize() {
|
||||||
return getFileDetails().getFileSize();
|
FileDetails details = getFileDetails();
|
||||||
|
return details != null ? details.getFileSize() : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -94,7 +94,7 @@ public class BluetoothDownloader extends Downloader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
protected void close() {
|
||||||
if (connection != null)
|
if (connection != null)
|
||||||
connection.closeQuietly();
|
connection.closeQuietly();
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package org.fdroid.fdroid.net;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
|
|
||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.Utils;
|
||||||
@ -35,9 +36,9 @@ public abstract class Downloader {
|
|||||||
protected int bytesRead;
|
protected int bytesRead;
|
||||||
protected int totalBytes;
|
protected int totalBytes;
|
||||||
|
|
||||||
public abstract InputStream getInputStream() throws IOException;
|
protected abstract InputStream getDownloadersInputStream() throws IOException;
|
||||||
|
|
||||||
public abstract void close();
|
protected abstract void close() throws IOException;
|
||||||
|
|
||||||
Downloader(Context context, URL url, File destFile)
|
Downloader(Context context, URL url, File destFile)
|
||||||
throws FileNotFoundException, MalformedURLException {
|
throws FileNotFoundException, MalformedURLException {
|
||||||
@ -47,6 +48,10 @@ public abstract class Downloader {
|
|||||||
localBroadcastManager = LocalBroadcastManager.getInstance(context);
|
localBroadcastManager = LocalBroadcastManager.getInstance(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final InputStream getInputStream() throws IOException {
|
||||||
|
return new WrappedInputStream(getDownloadersInputStream());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If you ask for the cacheTag before calling download(), you will get the
|
* If you ask for the cacheTag before calling download(), you will get the
|
||||||
* same one you passed in (if any). If you call it after download(), you
|
* same one you passed in (if any). If you call it after download(), you
|
||||||
@ -197,4 +202,66 @@ public abstract class Downloader {
|
|||||||
public int getTotalBytes() {
|
public int getTotalBytes() {
|
||||||
return totalBytes;
|
return totalBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides every method in {@link InputStream} and delegates to the wrapped stream.
|
||||||
|
* The only difference is that when we call the {@link WrappedInputStream#close()} method,
|
||||||
|
* after delegating to the wrapped stream we invoke the {@link Downloader#close()} method
|
||||||
|
* on the {@link Downloader} which created this.
|
||||||
|
*/
|
||||||
|
private class WrappedInputStream extends InputStream {
|
||||||
|
|
||||||
|
private InputStream toWrap;
|
||||||
|
|
||||||
|
public WrappedInputStream(InputStream toWrap) {
|
||||||
|
super();
|
||||||
|
this.toWrap = toWrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
toWrap.close();
|
||||||
|
Downloader.this.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int available() throws IOException {
|
||||||
|
return toWrap.available();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mark(int readlimit) {
|
||||||
|
toWrap.mark(readlimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean markSupported() {
|
||||||
|
return toWrap.markSupported();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(@NonNull byte[] buffer) throws IOException {
|
||||||
|
return toWrap.read(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(@NonNull byte[] buffer, int byteOffset, int byteCount) throws IOException {
|
||||||
|
return toWrap.read(buffer, byteOffset, byteCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void reset() throws IOException {
|
||||||
|
toWrap.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long byteCount) throws IOException {
|
||||||
|
return toWrap.skip(byteCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
return toWrap.read();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,6 +108,10 @@ public class DownloaderFactory {
|
|||||||
// We support onion addresses through our own downloader.
|
// We support onion addresses through our own downloader.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (isBluetoothAddress(url)) {
|
||||||
|
// Completely differnet protocol not understood by the download manager.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return hasDownloadManager(context);
|
return hasDownloadManager(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,12 +8,10 @@ import org.fdroid.fdroid.Preferences;
|
|||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.Utils;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
@ -30,7 +28,6 @@ public class HttpDownloader extends Downloader {
|
|||||||
protected static final String HEADER_FIELD_ETAG = "ETag";
|
protected static final String HEADER_FIELD_ETAG = "ETag";
|
||||||
|
|
||||||
protected HttpURLConnection connection;
|
protected HttpURLConnection connection;
|
||||||
private InputStream stream;
|
|
||||||
private int statusCode = -1;
|
private int statusCode = -1;
|
||||||
|
|
||||||
HttpDownloader(Context context, URL url, File destFile)
|
HttpDownloader(Context context, URL url, File destFile)
|
||||||
@ -38,14 +35,6 @@ public class HttpDownloader extends Downloader {
|
|||||||
super(context, url, destFile);
|
super(context, url, destFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calling this makes this downloader not download a file. Instead, it will
|
|
||||||
* only stream the file through the {@link HttpDownloader#getInputStream()}
|
|
||||||
*/
|
|
||||||
public HttpDownloader streamDontDownload() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note: Doesn't follow redirects (as far as I'm aware).
|
* Note: Doesn't follow redirects (as far as I'm aware).
|
||||||
* {@link BaseImageDownloader#getStreamFromNetwork(String, Object)} has an implementation worth
|
* {@link BaseImageDownloader#getStreamFromNetwork(String, Object)} has an implementation worth
|
||||||
@ -55,14 +44,9 @@ public class HttpDownloader extends Downloader {
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public InputStream getInputStream() throws IOException {
|
protected InputStream getDownloadersInputStream() throws IOException {
|
||||||
setupConnection();
|
setupConnection();
|
||||||
stream = new BufferedInputStream(connection.getInputStream());
|
return new BufferedInputStream(connection.getInputStream());
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BufferedReader getBufferedReader() throws IOException {
|
|
||||||
return new BufferedReader(new InputStreamReader(getInputStream()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a remote file. Returns the HTTP response code.
|
// Get a remote file. Returns the HTTP response code.
|
||||||
@ -155,18 +139,8 @@ public class HttpDownloader extends Downloader {
|
|||||||
return this.statusCode != 304;
|
return this.statusCode != 304;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getStatusCode() {
|
@Override
|
||||||
return statusCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
try {
|
|
||||||
if (stream != null)
|
|
||||||
stream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
connection.disconnect();
|
connection.disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,8 @@ import android.content.Context;
|
|||||||
|
|
||||||
import com.nostra13.universalimageloader.core.download.BaseImageDownloader;
|
import com.nostra13.universalimageloader.core.download.BaseImageDownloader;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
public class IconDownloader extends BaseImageDownloader {
|
public class IconDownloader extends BaseImageDownloader {
|
||||||
|
|
||||||
@ -22,38 +19,7 @@ public class IconDownloader extends BaseImageDownloader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream getStream(String imageUri, Object extra) throws IOException {
|
public InputStream getStream(String imageUri, Object extra) throws IOException {
|
||||||
|
return DownloaderFactory.create(context, imageUri).getInputStream();
|
||||||
Scheme scheme = Scheme.ofUri(imageUri);
|
|
||||||
|
|
||||||
switch (scheme) {
|
|
||||||
case HTTP:
|
|
||||||
case HTTPS:
|
|
||||||
Downloader downloader = DownloaderFactory.create(context, imageUri);
|
|
||||||
return downloader.getInputStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
//bluetooth isn't a scheme in the Scheme. library, so we can add a check here
|
|
||||||
if (imageUri.toLowerCase(Locale.ENGLISH).startsWith("bluetooth")) {
|
|
||||||
Downloader downloader = DownloaderFactory.create(context, imageUri);
|
|
||||||
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
InputStream is = downloader.getInputStream();
|
|
||||||
|
|
||||||
int b;
|
|
||||||
while ((b = is.read()) != -1)
|
|
||||||
baos.write(b);
|
|
||||||
|
|
||||||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
|
|
||||||
|
|
||||||
downloader.close();
|
|
||||||
|
|
||||||
return bais;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return super.getStream(imageUri, extra);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.fdroid.fdroid.views.swap;
|
package org.fdroid.fdroid.views.swap;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -330,9 +331,12 @@ public class SwapAppsView extends ListView implements
|
|||||||
private final ContentObserver appObserver = new ContentObserver(new Handler()) {
|
private final ContentObserver appObserver = new ContentObserver(new Handler()) {
|
||||||
@Override
|
@Override
|
||||||
public void onChange(boolean selfChange) {
|
public void onChange(boolean selfChange) {
|
||||||
app = AppProvider.Helper.findById(getActivity().getContentResolver(), app.id);
|
Activity activity = getActivity();
|
||||||
apkToInstall = null; // Force lazy loading to fetch correct apk next time.
|
if (activity != null) {
|
||||||
resetView();
|
app = AppProvider.Helper.findById(getActivity().getContentResolver(), app.id);
|
||||||
|
apkToInstall = null; // Force lazy loading to fetch correct apk next time.
|
||||||
|
resetView();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user