F-droid 源码片段(二)下载模块整理

这篇文章把F-droid的下载功能经过修改单独拿出来,而且做了一个demo。

希望能对自己后续起到借鉴作用。各位童鞋也可以去进行下载。

其实主要的思想有2个

1、使用接口进行回调

2、线程直接调用回调,由于无法知道主线程是否进行UI操作,所以把线程的回调进行了包装,使用Handler来发消息。保证不会崩溃。

项目下载地址:

http://download.csdn.net/download/leehu1987/7979253

一、定义一个接口,用于页面下载状态的监听;

<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("======下载完成");

	}

}

尚未完成的功能:

1、断点下载(需要数据库)

2、如果下载完成了,下次下载应该是不需要下载了。

时间: 2024-07-31 02:08:39

F-droid 源码片段(二)下载模块整理的相关文章

Android4.4.2源码分析之WiFi模块(二)

接着上一篇继续对WiFi源码的分析 Android4.4.2源码分析之WiFi模块(一) onResume方法中 6>,首先是调用WiFiEnabler的resume方法对switch进行管理 接下来注册广播 getActivity().registerReceiver(mReceiver, mFilter); 广播监听的action如下 //wifi状态改变的action mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); //W

如何分析SpringBoot源码模块及结构?--SpringBoot源码(二)

注:该源码分析对应SpringBoot版本为2.1.0.RELEASE 1 前言 本篇接如何搭建自己的SpringBoot源码调试环境?--SpringBoot源码(一). 前面搭建好了自己本地的SpringBoot源码调试环境后,此时我们不要急着下手进入到具体的源码调试细节中,刚开始阅读源码,此时我们一定要对项目结构等有一个整体的认识,然后再进行源码分析调试.推荐阅读下笔者之前写的的分析开源项目源码,我们该如何入手分析?一文,干货满满哦. 2 SpringBoot源码模块一览 我们先来对Spr

可视化工具gephi源码探秘(二)---导入netbeans

在上篇<可视化工具gephi源码探秘(一)>中主要介绍了如何将gephi的源码导入myeclipse中遇到的一些问题,此篇接着上篇而来,主要讲解当下通过myeclipse导入gephi源码的可行性不高以及熟悉netbeans,并把原本基于netbeans平台开发的gephi源码导入进netbeans后启动正常运行的过程,其中有遇到的不少问题和相应的解决方法. 前日工作梗概(还是沿着想把源码导入myeclipse的思路): 经过从各大子模块的pom.xml中筛选出符合条件的jar包写入项目下的p

可视化工具gephi源码探秘(二)

在上篇<可视化工具gephi源码探秘(一)>中主要介绍了如何将gephi的源码导入myeclipse中遇到的一些问题,此篇接着上篇而来,主要讲解当下通过myeclipse导入gephi源码的可行性不高以及熟悉netbeans,并把原本基于netbeans平台开发的gephi源码导入进netbeans后启动正常运行的过程,其中有遇到的不少问题和相应的解决方法. 前日工作梗概(还是沿着想把源码导入myeclipse的思路): 经过从各大子模块的pom.xml中筛选出符合条件的jar包写入项目下的p

Android4.4.2源码分析之WiFi模块(一)

由对Androidsetting的源码分析之WiFi模块的界面fragment为WiFisettings.java,关于setting模块的源码分析可以参考 Android系统源码剖析(一)---Settings 已经写了几篇关于Android源码的,源码代码量太大,所以如果想分析某个模块可能不知如何下手,说一下思路 1,分析源码英文阅读能力要够,想要分析某个模块一般找模块对应的英文,就是模块 2,找到之后首先查看清单配置文件Androidmani.fest,找到程序主界面activity 3,

netty 源码分析二

以服务端启动,接收客户端连接整个过程为例分析, 简略分为 五个过程: 1.NioServerSocketChannel 管道生成, 2.NioServerSocketChannel 管道完成初始化, 3.NioServerSocketChannel注册至Selector选择器, 4.NioServerSocketChannel管道绑定到指定端口,启动服务 5.NioServerSocketChannel接受客户端的连接,进行相应IO操作 Ps:netty内部过程远比这复杂,简略记录下方便以后回忆

Spring 源码解析之HandlerAdapter源码解析(二)

Spring 源码解析之HandlerAdapter源码解析(二) 前言 看这篇之前需要有Spring 源码解析之HandlerMapping源码解析(一)这篇的基础,这篇主要是把请求流程中的调用controller流程单独拿出来了 解决上篇文章遗留的问题 getHandler(processedRequest) 这个方法是如何查找到对应处理的HandlerExecutionChain和HandlerMapping的,比如说静态资源的处理和请求的处理肯定是不同的HandlerMapping ge

Android xUtils3源码解析之数据库模块

xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3源码解析之图片模块 三. Android xUtils3源码解析之注解模块 四. Android xUtils3源码解析之数据库模块 配置数据库 DbManager.DaoConfig daoConfig = new DbManager.DaoConfig() .setDbName("test.db") .setDbVersion(1) .setDbOpenListe

Android xUtils3源码解析之注解模块

xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3源码解析之图片模块 三. Android xUtils3源码解析之注解模块 四. Android xUtils3源码解析之数据库模块 初始化 public class BaseActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { su

[Android]Fragment源码分析(二) 状态

我们上一讲,抛出来一个问题,就是当Activity的onCreateView的时候,是如何构造Fragment中的View参数.要回答这个问题我们先要了解Fragment的状态,这是Fragment管理中非常重要的一环.我们先来看一下FragmentActivity提供的一些核心回调: @Override protected void onCreate(Bundle savedInstanceState) { mFragments.attachActivity(this, mContainer,