Refactor AsyncDownloader to be an interface.

The interface is used by both AsyncDownloadWrapper and
AsyncDownloaderFromAndroid.
This commit is contained in:
Peter Serwylo 2015-09-09 17:13:30 +10:00
parent d0d287f668
commit 0a9941d93d
4 changed files with 106 additions and 99 deletions

View File

@ -0,0 +1,98 @@
package org.fdroid.fdroid.net;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import java.io.IOException;
class AsyncDownloadWrapper extends Handler implements AsyncDownloader {
private static final String TAG = "AsyncDownloadWrapper";
private static final int MSG_DOWNLOAD_COMPLETE = 2;
private static final int MSG_DOWNLOAD_CANCELLED = 3;
private static final int MSG_ERROR = 4;
private static final String MSG_DATA = "data";
private final Downloader downloader;
private DownloadThread downloadThread = null;
private final Listener listener;
/**
* Normally the listener would be provided using a setListener method.
* However for the purposes of this async downloader, it doesn't make
* sense to have an async task without any way to notify the outside
* world about completion. Therefore, we require the listener as a
* parameter to the constructor.
*/
public AsyncDownloadWrapper(Downloader downloader, Listener listener) {
this.downloader = downloader;
this.listener = listener;
}
public int getBytesRead() {
return downloader.getBytesRead();
}
public int getTotalBytes() {
return downloader.getTotalBytes();
}
public void download() {
downloadThread = new DownloadThread();
downloadThread.start();
}
public void attemptCancel(boolean userRequested) {
if (downloadThread != null) {
downloadThread.interrupt();
}
}
/**
* Receives "messages" from the download thread, and passes them onto the
* relevant {@link AsyncDownloader.Listener}
*/
public void handleMessage(Message message) {
switch (message.arg1) {
case MSG_DOWNLOAD_COMPLETE:
listener.onDownloadComplete();
break;
case MSG_DOWNLOAD_CANCELLED:
listener.onDownloadCancelled();
break;
case MSG_ERROR:
listener.onErrorDownloading(message.getData().getString(MSG_DATA));
break;
}
}
private class DownloadThread extends Thread {
public void run() {
try {
downloader.download();
sendMessage(MSG_DOWNLOAD_COMPLETE);
} catch (InterruptedException e) {
sendMessage(MSG_DOWNLOAD_CANCELLED);
} catch (IOException e) {
Log.e(TAG, "I/O exception in download thread", e);
Bundle data = new Bundle(1);
data.putString(MSG_DATA, e.getLocalizedMessage());
Message message = new Message();
message.arg1 = MSG_ERROR;
message.setData(data);
AsyncDownloadWrapper.this.sendMessage(message);
}
}
private void sendMessage(int messageType) {
Message message = new Message();
message.arg1 = messageType;
AsyncDownloadWrapper.this.sendMessage(message);
}
}
}

View File

@ -9,106 +9,17 @@ import org.fdroid.fdroid.ProgressListener;
import java.io.IOException;
/**
* Given a {@link org.fdroid.fdroid.net.Downloader}, this wrapper will conduct the download operation on a
* separate thread. All progress/status/error/etc events will be forwarded from that thread to the thread
* that {@link AsyncDownloader#download()} was invoked on. If you want to respond with UI feedback
* to these events, it is important that you execute the download method of this class from the UI thread.
* That way, all forwarded events will be handled on that thread.
*/
@SuppressWarnings("serial")
public class AsyncDownloader extends Handler {
public interface AsyncDownloader {
private static final String TAG = "AsyncDownloadWrapper";
private static final int MSG_DOWNLOAD_COMPLETE = 2;
private static final int MSG_DOWNLOAD_CANCELLED = 3;
private static final int MSG_ERROR = 4;
private static final String MSG_DATA = "data";
private final Downloader downloader;
private final Listener listener;
private DownloadThread downloadThread = null;
/**
* Normally the listener would be provided using a setListener method.
* However for the purposes of this async downloader, it doesn't make
* sense to have an async task without any way to notify the outside
* world about completion. Therefore, we require the listener as a
* parameter to the constructor.
*/
public AsyncDownloader(Downloader downloader, Listener listener) {
this.downloader = downloader;
this.listener = listener;
}
public void download() {
downloadThread = new DownloadThread();
downloadThread.start();
}
public void attemptCancel(boolean userRequested) {
if (downloadThread != null) {
downloadThread.interrupt();
}
}
/**
* Receives "messages" from the download thread, and passes them onto the
* relevant {@link AsyncDownloader.Listener}
* @param message
*/
public void handleMessage(Message message) {
switch (message.arg1) {
case MSG_DOWNLOAD_COMPLETE:
listener.onDownloadComplete();
break;
case MSG_DOWNLOAD_CANCELLED:
listener.onDownloadCancelled();
break;
case MSG_ERROR:
listener.onErrorDownloading(message.getData().getString(MSG_DATA));
break;
}
}
public int getBytesRead() {
return downloader.getBytesRead();
}
public int getTotalBytes() {
return downloader.getTotalBytes();
}
public interface Listener extends ProgressListener {
interface Listener extends ProgressListener {
void onErrorDownloading(String localisedExceptionDetails);
void onDownloadComplete();
void onDownloadCancelled();
}
private class DownloadThread extends Thread {
int getBytesRead();
int getTotalBytes();
void download();
void attemptCancel(boolean userRequested);
public void run() {
try {
downloader.download();
sendMessage(MSG_DOWNLOAD_COMPLETE);
} catch (InterruptedException e) {
sendMessage(MSG_DOWNLOAD_CANCELLED);
} catch (IOException e) {
Log.e(TAG, "I/O exception in download thread", e);
Bundle data = new Bundle(1);
data.putString(MSG_DATA, e.getLocalizedMessage());
Message message = new Message();
message.arg1 = MSG_ERROR;
message.setData(data);
AsyncDownloader.this.sendMessage(message);
}
}
private void sendMessage(int messageType) {
Message message = new Message();
message.arg1 = messageType;
AsyncDownloader.this.sendMessage(message);
}
}
}

View File

@ -23,7 +23,7 @@ import java.io.OutputStream;
* A downloader that uses Android's DownloadManager to perform a download.
*/
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
public class AsyncDownloaderFromAndroid extends AsyncDownloader {
public class AsyncDownloaderFromAndroid implements AsyncDownloader {
private final Context context;
private final DownloadManager dm;
private File localFile;
@ -42,7 +42,6 @@ public class AsyncDownloaderFromAndroid extends AsyncDownloader {
* parameter to the constructor.
*/
public AsyncDownloaderFromAndroid(Context context, Listener listener, String appName, String appId, String remoteAddress, File localFile) {
super(null, listener);
this.context = context;
this.appName = appName;
this.appId = appId;

View File

@ -5,7 +5,6 @@ import android.os.Build;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
public class DownloaderFactory {
@ -62,7 +61,7 @@ public class DownloaderFactory {
if (canUseDownloadManager(url)) {
return new AsyncDownloaderFromAndroid(context, listener, title, id, url.toString(), destFile);
} else {
return new AsyncDownloader(create(context, url, destFile), listener);
return new AsyncDownloadWrapper(create(context, url, destFile), listener);
}
}