这篇文章把F-droid的下载功能经过修改单独拿出来,而且做了一个demo。
希望能对自己后续起到借鉴作用。各位童鞋也可以去进行下载。
其实主要的思想有2个
1、使用接口进行回调
2、线程直接调用回调,由于无法知道主线程是否进行UI操作,所以把线程的回调进行了包装,使用Handler来发消息。保证不会崩溃。
项目下载地址:
http://download.csdn.net/download/leehu1987/7979253
尚未完成的功能:
1、断点下载(需要数据库)
2、如果下载完成了,下次下载应该是不需要下载了。
缺陷:
一个下载线程只能注册一个监听,比较好的办法是可以使用观察者模式通知各个页面。后续进行优化下。
一、定义一个接口,用于页面下载状态的监听;
<pre class="java" name="code">import java.io.Serializable;
public interface DownloadListener {
public static class Data implements Serializable {
private static final long serialVersionUID = 8954447444334039739L;
private long currentSize;
private long totalSize;
public Data() {
}
public Data(int currentSize, int totalSize) {
this.currentSize = currentSize;
this.totalSize = totalSize;
}
public long getCurrentSize() {
return currentSize;
}
public void setCurrentSize(long currentSize) {
this.currentSize = currentSize;
}
public long getTotalSize() {
return totalSize;
}
public void setTotalSize(long totalSize) {
this.totalSize = totalSize;
}
@Override
public String toString() {
return "Data [currentSize=" + currentSize + ", totalSize="
+ totalSize + "]";
}
}
/**
*
* @param data
* : transfer downloaded data
*/
public void onProgress(Data data);
/**
*
* @param e
* : exception
*/
public void onError(Exception e);
public void onCompleted();
}
二、定义了一个Downloader父类,为了适应不同的下载,比如使用Http进行下载;使用代理进行下载等。
所以这个类是一个接口类,定义了一些基本的操作方法。
package com.example.downloader;
package com.example.downloader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import android.util.Log;
public abstract class Downloader {
private static final String TAG = "Downloader";
protected URL sourceUrl;
private OutputStream outputStream;
private DownloadListener downloadListener = null;
private File outputFile;
private final int BUFFER_SIZE = 1240000;//1M
// ///////////////////////////
public abstract InputStream getInputStream() throws IOException;
public abstract int totalDownloadSize();
public abstract void download() throws IOException, InterruptedException;
// ///////////////////////////
Downloader(File destFile) throws FileNotFoundException,
MalformedURLException {
outputFile = destFile;
outputStream = new FileOutputStream(outputFile);
}
public void setProgressListener(DownloadListener downloadListener) {
this.downloadListener = downloadListener;
}
public File getFile() {
return outputFile;
}
protected void downloadFromStream() throws IOException,
InterruptedException {
Log.d(TAG, "Downloading from stream");
InputStream input = null;
try {
input = getInputStream();
throwExceptionIfInterrupted();
copyInputToOutputStream(getInputStream());
} finally {
try {
if (outputStream != null) {
outputStream.close();
}
if (input != null) {
input.close();
}
} catch (IOException ioe) {
// ignore
}
}
// Even if we have completely downloaded the file, we should probably
// respect
// the wishes of the user who wanted to cancel us.
throwExceptionIfInterrupted();
}
/**
* stop thread
*
* @throws InterruptedException
*/
private void throwExceptionIfInterrupted() throws InterruptedException {
if (Thread.interrupted()) {
Log.d(TAG, "Received interrupt, cancelling download");
throw new InterruptedException();
}
}
protected void copyInputToOutputStream(InputStream input)
throws IOException, InterruptedException {
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = 0;
int totalBytes = totalDownloadSize();
throwExceptionIfInterrupted();
sendProgress(bytesRead, totalBytes);
while (true) {
int count = input.read(buffer);
throwExceptionIfInterrupted();
bytesRead += count;
sendProgress(bytesRead, totalBytes);
if (count == -1) {
Log.d(TAG, "Finished downloading from stream");
break;
}
outputStream.write(buffer, 0, count);
}
outputStream.flush();
}
protected void sendProgress(int bytesRead, int totalBytes) {
if (downloadListener != null) {
DownloadListener.Data data = new DownloadListener.Data(bytesRead,
totalBytes);
downloadListener.onProgress(data);
}
}
}
三、写了一个HttpDownloader。继承自Downloader
package com.example.downloader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class HttpDownloader extends Downloader {
protected HttpURLConnection connection;
HttpDownloader(String source, File destFile) throws FileNotFoundException,
MalformedURLException {
super(destFile);
sourceUrl = new URL(source);
}
@Override
public void download() throws IOException, InterruptedException {
setupConnection();
downloadFromStream();
}
protected void setupConnection() throws IOException {
if (connection != null) {
return;
}
connection = (HttpURLConnection) sourceUrl.openConnection();
}
@Override
public InputStream getInputStream() throws IOException {
setupConnection();
return connection.getInputStream();
}
@Override
public int totalDownloadSize() {
return connection.getContentLength();
}
}
四、需要一个控制方法
package com.example.downloader;
import java.io.IOException;
import com.example.downloader.DownloadListener.Data;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
public class DownloadManager extends Handler {
private DownloadThread downloadThread;
private Downloader downloader;
private DownloadListener listener;
private static final int MSG_PROGRESS = 1;
private static final int MSG_DOWNLOAD_COMPLETE = 2;
private static final int MSG_DOWNLOAD_CANCELLED = 3;
private static final int MSG_ERROR = 4;
public DownloadManager(Downloader downloader, DownloadListener listener) {
this.downloader = downloader;
this.listener = listener;
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MSG_PROGRESS: {
DownloadListener.Data data = (Data) msg.obj;
if (listener != null) {
listener.onProgress(data);
}
break;
}
case MSG_DOWNLOAD_COMPLETE: {
if (listener != null) {
listener.onCompleted();
}
break;
}
case MSG_DOWNLOAD_CANCELLED: {
break;
}
case MSG_ERROR: {
Exception e = (Exception) msg.obj;
if (listener != null) {
listener.onError(e);
}
break;
}
default:
return;
}
}
public void download() {
downloadThread = new DownloadThread();
downloadThread.start();
}
private class DownloadThread extends Thread implements<strong><span style="color:#ff6666;"> DownloadListener </span></strong>{
private static final String TAG = "DownloadThread";
public void run() {
try {
downloader.setProgressListener(this);
downloader.download();
sendMessage(MSG_DOWNLOAD_COMPLETE);
} catch (InterruptedException e) {
sendMessage(MSG_DOWNLOAD_CANCELLED);
} catch (IOException e) {
Log.e(TAG, e.getMessage() + ": " + Log.getStackTraceString(e));
Message message = new Message();
message.what = MSG_ERROR;
message.obj = e;
sendMessage(message);
}
}
private void sendMessage(int messageType) {
Message message = new Message();
message.what = messageType;
DownloadManager.this.sendMessage(message);
}
private void sendMessage(Message msg){
DownloadManager.this.sendMessage(msg);
}
@Override
public void onProgress(Data data) {
// TODO
Message message = new Message();
message.what = MSG_PROGRESS;
message.obj =data;
DownloadManager.this.sendMessage(message);
}
@Override
public void onError(Exception e) {
// 在这里没有任何用,异常被捕获了
// do nothing
}
@Override
public void onCompleted() {
// do nothing
}
}
}
五、页面如何使用
<pre class="html" name="code">package com.example.downloader;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.view.Menu;
public class MainActivity extends Activity implements DownloadListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String dir = Environment.getExternalStorageDirectory()
.getAbsolutePath() + File.separator + "test.apk";
File f = new File(dir);
try {
Downloader downloader = new HttpDownloader(
"http://192.168.131.63/fengxing2.2.0.6.apk", f);
DownloadManager m = new DownloadManager(downloader, this);
m.download();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public void onProgress(Data data) {
System.out.println("======"+data);
}
@Override
public void onError(Exception e) {
System.out.println("======");
e.printStackTrace();
}
@Override
public void onCompleted() {
System.out.println("======下载完成");
}
}