断点续传和多线程下载(一)

仿迅雷实现下载工具,支持断点续传多线程下载以及简单界面展示 这篇博客介绍下在公司实习时选做的个项目,虽然实习期间没有全部完成,但觉得这个项目做好的话挺有意思的,就在之后几天赶着完善了这个项目。 博客最后给出代码地址 这个项目是仿照迅雷,实现下载工具。 (开发平台: Ubuntu 16) 功能:能由个基于HTTP协议的网络资源链接,把该链接对应的资源下载到本地,并且显示下载时的状态(进度条、下载百分比、已下载时间、当前下载速度、自己又在后面... 阅读详情

最近在学习Android中的 断点续传 和 多线程下载 这方面的知识,一边从公司中的项目开始学习,一边也从网上查阅这方面的知识,然后发现公司中的项目代码关于这块都封装的比较好,比较大,不太容易看懂,所以准备参照网上的Demo,自己仿照着写一个。

首先我觉得学习一个比较复杂的知识点,不应当在一个demo里把这个所有复杂的知识点全都实现出来,这样对于初学者来说比较困难,从而不能完全的掌握知识点,所以我觉得应该先对这个知识点有个整体的把握,然后把这些东西给分成一个模块一个模块的,然后把这些局部的知识一个一个实现出来,这是比较简单的,最后将这些局部的知识再组装成一个整体的功能,这样一个比较复杂的功能就比较好实习出来了,就拿断点续床和多线程下载来说,一般涉及到下载的项目,都会用到这部分的知识,所以按照我的思路就是,如果之前没有接触过这块,那么先做一个简单的下载功能,能够下载,然后在UI界面上进行进度更新,然后在加上断点续传的功能,这块没问题了之后加上多线程下载。具体里面涉及到哪些东西和别的知识点,在写的过程中,去查阅。

我分以下几步来慢慢实现这个功能:

1、简单的下载一个APK,实现断点续传,能够开始和暂停

2、加上多线程下载功能,将一个下载的内容分给几个线程来下载

3、加入ListView,显示多个应用,点击下载,然后切换到下载界面,进行进度更新


这篇我先做一第一部分,简单实现一个断点续传功能。关于断点续传,其实就是在用HttpURLConnection得到输入流,一点一点往文件里写入数据的时候,把这个完成度给记录下来,放在数据库中,下次再下载的时候,先从数据库中得到之前下载的完成度,然后根据这个完成度,在设置HttpURLConnection的请求头里的属性时,从得到的断点处开始请求数据到文件的长度,然后再去从输入流中读取数据到文件里,这样就基本实现了断点续传的功能了。

下面贴一下部分代码:

布局很简单,一个按钮和进度条



main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.shulf.downloadtest.MainActivity" >

    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="24dp"
        android:layout_marginTop="18dp"
        android:text="新浪微博"
        android:textSize="20sp" />

    <Button
        android:id="@+id/startBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignTop="@+id/name"
        android:text="开始" />

    <TextView
        android:id="@+id/progressText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/name"
        android:layout_below="@+id/startBtn" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/progressText"
        android:layout_marginTop="15dp" />

</RelativeLayout>

MainActivity.java

package com.shulf.downloadtest;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity implements OnClickListener {

	public final static String URL = "http://gdown.baidu.com/data/wisegame/f58a940533df3890/weibo_1477.apk";
	private Button startBtn;
	private TextView progressText;
	private ProgressBar progressBar;
	private MyHander handler;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		startBtn = (Button) findViewById(R.id.startBtn);
		startBtn.setOnClickListener(this);

		progressText = (TextView) findViewById(R.id.progressText);
		progressBar = (ProgressBar) findViewById(R.id.progressBar);

		handler = new MyHander(progressText, progressBar);
	}

	@Override
	public void onClick(View view) {

		if (startBtn.getText().equals("开始") || startBtn.getText().equals("继续")) {
			startBtn.setText("暂停");
			download();
		} else {
			startBtn.setText("继续");
			pause();
		}
	}

	public void download() {
		DownloadAsyncTask task = new DownloadAsyncTask(
				this.getApplicationContext(), handler);
		task.execute(URL);
	}

	public void pause() {
		DownloadTask task = DownloadManager.getInstance(
				this.getApplicationContext()).getTask(URL);
		task.STATE = 2;
	}

	/**
	 * 负责接收DownTask下载线程中的传来的消息,更新UI线程中的进度条和下载进度
	 * @author Administrator
	 *
	 */
	
	private class MyHander extends Handler {
		private TextView tv;
		private ProgressBar pb;

		public MyHander(TextView tv, ProgressBar pb) {
			this.tv = tv;
			this.pb = pb;
		}

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			if (msg.what == 1) {
				tv.setText(msg.arg1 * 100 / msg.arg2 + "%");
				pb.setMax(msg.arg2);
				pb.setProgress(msg.arg1);
			}
		}
	}
}

DownloadAsyncTask.java
package com.shulf.downloadtest;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;

/**
 * 下载之前的准备,从DB中得到下载实体,或者创建下载实体
 * @author Administrator
 *
 */

public class DownloadAsyncTask extends AsyncTask<String, Void, DownloadInfo> {

	private Context context;
	private Handler handler;
	private DownloadManager dm;

	public DownloadAsyncTask(Context context, Handler handler) {
		this.handler = handler;
		this.context = context;
		dm = DownloadManager.getInstance(context);
	}

	@Override
	protected DownloadInfo doInBackground(String... args) {
		DownloadInfo info = DownloadDao.getInstance(context).getDownloadInof(
				args[0]);
		if (info == null) {
			int fileSize = 0;
			try {
				URL url = new URL(args[0]);
				HttpURLConnection conn = (HttpURLConnection) url
						.openConnection();
				conn.setReadTimeout(5000);
				conn.setRequestMethod("GET");
				fileSize = conn.getContentLength();

			} catch (MalformedURLException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			info = new DownloadInfo();
			info.completeSize = 0;
			info.fileSize = fileSize;
			info.url = args[0];
			DownloadDao.getInstance(context).saveDownloadInfo(info);

			return info;
		}
		return info;
	}

	@Override
	protected void onPostExecute(DownloadInfo info) {
		super.onPostExecute(info);

		DownloadTask task = new DownloadTask(context, info, handler);
		task.STATE=1;
		task.start();
		return;

	}

}

DownloadTask.java

package com.shulf.downloadtest;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;

import android.content.Context;
import android.os.Handler;
import android.os.Message;

/**
 * 下载线程类,得到下载实体中的信息,开启线程进行下载
 * @author Administrator
 *
 */

public class DownloadTask extends Thread {

	private String urlStr;
	private Handler handler;
	private int fileSize;
	private Context context;
	private DownloadInfo info;
	public static int STATE = 1;// 1表示未下载状态,2表示暂停状态

	public DownloadTask(Context context, DownloadInfo info, Handler handler) {
		this.info = info;
		this.context = context;
		this.urlStr = info.url;
		this.fileSize = info.fileSize;
		this.handler = handler;
	}

	@Override
	public void run() {
		HttpURLConnection conn = null;
		InputStream in = null;
		RandomAccessFile randomAccessFile = null;
		File saveFile = new File("/mnt/sdcard/", "test.apk");
		int completeSize = info.completeSize;
		try {
			URL url = new URL(urlStr);
			conn = (HttpURLConnection) url.openConnection();
			conn.setConnectTimeout(5000);
			conn.setRequestMethod("GET");
			/**设置请求头的属性,从断点处开始到文件的长度*/
			conn.setRequestProperty("Range", "bytes=" + completeSize + "-"
					+ fileSize);

			randomAccessFile = new RandomAccessFile(saveFile, "rwd");
			randomAccessFile.seek(completeSize);

			in = conn.getInputStream();
			byte[] buffer = new byte[4096];
			int length = -1;
			while ((length = in.read(buffer)) != -1) {
				randomAccessFile.write(buffer, 0, length);
				completeSize += length;
				DownloadDao.getInstance(context).updateComplete(urlStr,
						completeSize);
				Message msg = Message.obtain();
				msg.what = 1;
				msg.arg1 = completeSize;
				msg.arg2 = fileSize;
				handler.sendMessage(msg);
				if (STATE == 2) {
					return;
				}

			}
		} catch (ProtocolException e) {
			e.printStackTrace();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try{
				if (in != null) {
					in.close();
				}
				if (randomAccessFile != null) {
					randomAccessFile.close();
				}
			}catch(Exception e){
				e.printStackTrace();
			}

		}
	}

}

DownloadDao.java

package com.shulf.downloadtest;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

/**
 * 数据访问类
 * @author Administrator
 *
 */

public class DownloadDao {
	private static DownloadDao dao;
	private Context context;

	private DownloadDao(Context context) {
		this.context = context;
	}

	public static DownloadDao getInstance(Context context) {
		if (dao == null) {
			dao = new DownloadDao(context);
		}
		return dao;
	}

	public SQLiteDatabase getConnection() {
		SQLiteDatabase db = null;
		db = new DBHelper(context).getReadableDatabase();
		return db;
	}

	/**
	 * 得到下载的完成进度
	 * @param url
	 * @return
	 */
	public synchronized int getComplete(String url) {
		int complete = 0;
		SQLiteDatabase db = getConnection();
		String sql = "select compele_size from download_info where url = ?";
		Cursor cursor = null;
		try {
			cursor = db.rawQuery(sql, new String[] { url });
			while (cursor.moveToNext()) {
				complete = cursor.getInt(0);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (db != null) {
				db.close();
			}
			if (cursor != null) {
				cursor.close();
			}
		}
		return complete;
	}

	/**
	 *更新表中的下载进度
	 * @param url
	 * @param complete
	 */
	public synchronized void updateComplete(String url, int complete) {
		SQLiteDatabase db = getConnection();
		try {
			String sql = "update download_info set compelete_size = ? where url = ?";
			Object[] bindArgs = { complete, url };
			db.execSQL(sql, bindArgs);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (db != null) {
				db.close();
			}
		}
	}

	/**
	 * 从数据库中得到下载实体信息,然后转换成实体返回
	 * @param url
	 * @return
	 */
	public synchronized DownloadInfo getDownloadInof(String url) {
		SQLiteDatabase db = getConnection();
		Cursor cursor = null;
		DownloadInfo info = null;
		String sql = "select * from download_info where url = ?";
		try {
			cursor = db.rawQuery(sql, new String[] { url });
			while (cursor.moveToNext()) {
				info = new DownloadInfo();
				info.completeSize = cursor.getInt(5);
				info.fileSize = cursor.getInt(6);
				info.url = cursor.getString(2);
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (db != null) {
				db.close();
			}
			if (cursor != null) {
				cursor.close();
			}
		}
		return info;
	}

	/**
	 * 保存下载实体的信息
	 * @param info
	 */
	public synchronized void saveDownloadInfo(DownloadInfo info) {
		SQLiteDatabase db = getConnection();
		try {
			String sql = "insert into download_info(thread_id,url,start_pos,end_pos,compelete_size,file_size) values (?,?,?,?,?,?)";
			Object[] bindArgs = { 0, info.url, 0, 0, info.completeSize,
					info.fileSize };
			db.execSQL(sql, bindArgs);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (db != null) {
				db.close();
			}
		}
	}

}

DownloadInfo.java

package com.shulf.downloadtest;

/**
 * 下载内容的实体
 * @author Administrator
 *
 */

public class DownloadInfo {

	public int fileSize;

	public int completeSize;

	public String url;
	
	public DownloadInfo() {
	}

	public DownloadInfo(int fileSize, int completeSize, String url) {
		super();
		this.fileSize = fileSize;
		this.completeSize = completeSize;
		this.url = url;
	}

	public int getFileSize() {
		return fileSize;
	}

	public void setFileSize(int fileSize) {
		this.fileSize = fileSize;
	}

	public int getCompleteSize() {
		return completeSize;
	}

	public void setCompleteSize(int completeSize) {
		this.completeSize = completeSize;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	@Override
	public String toString() {
		return "DownloadInfo [fileSize=" + fileSize + ", completeSize="
				+ completeSize + ", url=" + url + "]";
	}

}

DBHelper.java

package com.shulf.downloadtest;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DBHelper extends SQLiteOpenHelper {

	public DBHelper(Context context) {
		super(context, "download.db", null, 1);
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL("create table download_info (_id integer PRIMARY KEY AUTOINCREMENT, thread_id integer,"
				+ "url char,start_pos integer , end_pos integer, compelete_size integer,file_size integer)");
	}

	@Override
	public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {

	}

}

这部分简单的实现了断线续传的功能,能够下载暂停了,实现的比较粗糙,很多细节的东西都还没有注意

掌握高效多线程下载断点续传技术 在互联网中,文件下载是指将远程服务器上的数据复制到本地存储设备的过程。传统的单线程下载方式是指在段时间内,下载任务只使用个连接(线程)来下载文件,即以顺序的方式个接个地下载文件的不同部分。 阅读详情

相关推荐

Http多线程下载断点续传分析

NULL 博文链接:https://chanshui.iteye.com/blog/724930

1DM+ v17.1 修改版 — 多线程下载管理工具(高效稳定)

1DM+款适用于安卓设备的下载管理工具,支持多线程下载,可以加快下载速度。此修改版由 Balatan 制作,无需 root 或 Lucky Patcher,禁用不必要的权限功能,优化图形资源,去除了广告,禁用了 Google Play 商店检查,支持多种 CPU 架构屏幕 DPI,操作简单易懂。🏷标签:#1DM+ #v17.1 #下载管理 #多线程下载 #高效稳定 #修改版 #安卓工具。链接:https://pan.quark.cn/s/ebdade42ef96。

python学习 1402

HTTP断点续传多线程下载

HTTP断点续传多线程下载,支持HTTP协议的断点续传多通道下载

Android 断点续传进阶之多线程下载

推荐资源站:https://zhimalier.com/ 今天继续下载的风骚走位内容---多线程多文件断点续传 Android 断点续传基础之单线程下载:http://blog.csdn.net/qq_27489007/article/details/53897653 效果图: 文件关系: 所需内容 多文件下载列表的显示 启动多个线程分段下载 使用通知栏显示进度条 使用其他方...

空名先生 3652

断点续传多线程下载()

概述    在当今的网络时代,下载软件是使用最为频繁的软件之。几年来,下载技术也在不停地发展。最原始的下载功能仅仅是个“下载”过程,即从WEB服务器上连续地读取文件。其最大的问题是,由于网络的不稳定性,旦连接断开使得下载过程中断,就不得不全部从头再来次。    随后,“断点续传”的概念就出来了,顾名思义,就是如果下载中断,在重新建立连接后,跳过已经下载的部分,而只下载还没有下载的部分。   

2013

重游java系列 线程二(多线程下载断点续传

本篇文章以你文件下载中的多线程下载以及断点续传为问题出发点,主要回顾多线程在实际开发中的应用具体实现。 多线程下载的关键点在于对下载任务进行切分,即计算每个任务线程对应的实际文件中的起始点终止点。在每个线程中采用数据流方式对远程文件进行连接,这里有个知识点,即http头Rander参数,详见[url]http://guoba6688-sina-com.iteye.c...

ilikeyoudo的专栏 169

爬虫中多线程断点续传的实现

怎么获取下载链接构造Request 请求不是本文的重点,本文重点是获取下载链接后怎么提高下载速度以及断电续传功能的实现思路。 获取下载链接后可以从Response 中获得所需文档的大小,即contentlength 的值,而Request 中range 属性可以设置下载的范围,知道了这个原理,多线程断点续传就不难实现了。笔者的段实现多线程下载的代码如下: /// <summary> /// with parm from and to control the st

退休码农的自留地 428

android线程池断点续传,Android多线程下载断点续传

今天我们来接触多线程下载,当然也包括断点续传,我们可以看到很多下载器,当开通会员的时候下载东西的速度就变得快了许多,这是为什么呢?这就是跟今天讲的多线程有关系了,其实就是多开了几个线程下载罢了。当然真正的多线程下载要比这个复杂,要考虑很多问题。效果图如下:这里下载的是本地服务器上的文件,也可以下载网络上的些文件。先来看看多线程下载的原理吧:通常服务器同时与多个用户连接,用户之间共享带宽。...

weixin_35278163的博客 321

多线程下载实践

多线程下载的意义(网上搜的资料自己理解的差不多拿来用下 忘了出处了暂不注明): 所谓断点续传下载过程可以中途暂停,然后再从停止点继续下载过程,并最终获得完整的文件,断点续传需要服务器支持。 多线程下载也需要服务器支持断点续传功能,此时文件可以分成几个部分,并且同时进行下载,以提高下载速度。也就是说,对于同下载文档,设置不同的下载起点,然后起开始下载。 ...

zhuyufufu的专栏 134

axel:多线程下载工具

定位:基于http,ftp协议的多线程下载工具,支持断点续传。axel github代码:axelubuntu16.04下载: apt-get install axel 使用方法:1.man axel 2.axel -h axel -o target url 改目标文件名称 axel -s xx url 指定最大下载速度 axel -n x url 指定最大连

zhuziyu1157817544 1218

多线程下载支持断点续传

使用HttpURLConnection写的多线程下载工具,支持断点续传

python 多线程下载

#-------------------------------------------------------------------------------# Name:        down# Purpose:     ## Author:      lqefn## Created:     15-09-2010# Copyright:   (c) lqefn 2010# Licence:     #--------------------------------------------------

lqefn的专栏 704

多线程断点下载原理解析

原理: 1、将个任务等分成几个小部分 2、开多个线程每个负责个,记录每个线程其开始位置结束位置 3、分别写入同个文件,也是按起开始位置结束位置写入。同时在下载时创建临时文件记录这次下载到哪儿 4、下载前看是否有上次记录,有则接着从后面下载 public class MainActivity extends AppCompatActivity { @BindView(R...

Mr_Leixiansheng的博客 586

文件下载新革命:3倍提速的多线程下载工具实战指南

还在为Gofile平台文件下载速度慢而烦恼吗?传统浏览器下载方式不仅效率低下,还会因为网络波动导致下载中断。今天要介绍的gofile-downloader专为解决这些问题而生,通过智能多线程技术断点续传功能,让文件下载变得轻松高效。这款Python工具能够从gofile.io平台快速下载文件,支持密码保护内容批量处理,真正实现稳定可靠的下载体验。 ## 问题发现:传统下载方式的效率瓶颈 在

gitblog_00655的博客 801

okhttp java_基于OkHttp实现Java多线程下载方案分析与实现

说明这篇文章主要是为了演示OkHttp多线程的简单运用,利用多线程HTTP断点续传的特性实现多线程下载。总体分为三种实现发难,并且实现了这三种方案。这里先说明,这只是个Demo程序,为了快速实现功能,所以没有太多设计思想在里面,很多异常情况也没有考虑。所以,不喜勿喷,你如果觉得应该怎样,而不是我这样,那你自己去实现好了。当然,如果对我的方案,或实现上面有更好的想法,欢迎讨论。多线程下载方案分...

weixin_34254848的博客 1098

[转]Android实现网络多线程断点续传下载

本文转自:http://www.cnblogs.com/hanyonglu/archive/2012/02/20/2358801.html 本示例介绍在Android平台下通过HTTP协议实现断点续传下载。 我们编写的是Andorid的HTTP协议多线程断点下载应用程序。直接使用单线程下载HTTP文件对我们来说是件非常简单的事。那么,多线程断点需要什么功能? 1.多线程下载, 2.支持断...

weixin_33958366的博客 80

如何选择高效Android下载库?RxDownload多线程下载工具的核心优势解析

在移动应用开发中,个高效可靠的下载库能显著提升用户体验。RxDownload作为基于RxJavaKotlin打造的多线程下载工具,凭借其独特的设计理念强大功能,成为Android开发者的理想选择。本文将深入分析RxDownload与其他下载库的差异,帮助你理解为什么它能在众多下载解决方案中脱颖而出。 ## 🚀 RxDownload的核心竞争力 RxDownload最显著的优势在于其基于

gitblog_00886的博客 353

多线程下载工具之 Axel

Axel 是 Linux 平台下的款 HTTP/FTP 的下载工具,支持多线程以及断点续传,对于些有速度限制的服务器上下载东西时,Axel 的速度就明显比 wget 要快些Fedora 23+ 安装dnf-yintsallaxelCentOS 7.x 安装CentOS 的 yum 中本来是不包含 axel 这个软件包的,如果要使用需要先添加 epel 拓展yum...

weixin_34326429的博客 162
上一篇: Android开发中常用的一些adb命令
下一篇: JAR文件转可执行的exe文件
Alone_SLFly
博客等级 码龄12年 2粉丝 9原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值