热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android实现网络多线程文件下载

这篇文章主要介绍了Android实现网络多线程文件下载的相关资料,需要的朋友可以参考下

实现原理

(1)首先获得下载文件的长度,然后设置本地文件的长度。

(2)根据文件长度和线程数计算每条线程下载的数据长度和下载位置。

如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如下图所示:

(网上找的图)

例如10M大小,使用3个线程来下载,

线程下载的数据长度 (10%3 == 0 ? 10/3:10/3+1) ,第1,2个线程下载长度是4M,第三个线程下载长度为2M

下载开始位置:线程id*每条线程下载的数据长度 = ?

下载结束位置:(线程id+1)*每条线程下载的数据长度-1=?

之前练习时的一个demo,不多说了,直接上代码吧,有关断点续传,需要使用数据库,不再加了,网上有很多成熟的项目可以直接用。

实例

MainApp:

package com.amos.app; 
import java.io.File; 
import java.io.IOException; 
import java.net.MalformedURLException; 
import java.net.URL; 
import java.net.URLConnection; 
import com.amos.download.R; 
import android.annotation.SuppressLint; 
import android.app.Activity; 
import android.os.Bundle; 
import android.os.Environment; 
import android.os.Handler; 
import android.os.Message; 
import android.util.Log; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.ProgressBar; 
import android.widget.TextView; 
import android.widget.Toast; 
/** 
* @author yangxiaolong 
* @2014-5-6 
*/ 
public class MainApp extends Activity implements OnClickListener { 
private static final String TAG = MainApp.class.getSimpleName(); 
/** 显示下载进度TextView */ 
private TextView mMessageView; 
/** 显示下载进度ProgressBar */ 
private ProgressBar mProgressbar; 
@Override 
protected void onCreate(Bundle savedInstanceState) { 
super.onCreate(savedInstanceState); 
setContentView(R.layout.progress_activity); 
findViewById(R.id.download_btn).setOnClickListener(this); 
mMessageView = (TextView) findViewById(R.id.download_message); 
mProgressbar = (ProgressBar) findViewById(R.id.download_progress); 
} 
@Override 
public void onClick(View v) { 
if (v.getId() == R.id.download_btn) { 
doDownload(); 
} 
} 
/** 
* 使用Handler更新UI界面信息 
*/ 
@SuppressLint("HandlerLeak") 
Handler mHandler = new Handler() { 
@Override 
public void handleMessage(Message msg) { 
mProgressbar.setProgress(msg.getData().getInt("size")); 
float temp = (float) mProgressbar.getProgress() 
/ (float) mProgressbar.getMax(); 
int progress = (int) (temp * 100); 
if (progress == 100) { 
Toast.makeText(MainApp.this, "下载完成!", Toast.LENGTH_LONG).show(); 
} 
mMessageView.setText("下载进度:" + progress + " %"); 
} 
}; 
/** 
* 下载准备工作,获取SD卡路径、开启线程 
*/ 
private void doDownload() { 
// 获取SD卡路径 
String path = Environment.getExternalStorageDirectory() 
+ "/amosdownload/"; 
File file = new File(path); 
// 如果SD卡目录不存在创建 
if (!file.exists()) { 
file.mkdir(); 
} 
// 设置progressBar初始化 
mProgressbar.setProgress(0); 
// 简单起见,我先把URL和文件名称写死,其实这些都可以通过HttpHeader获取到 
String downloadUrl = "http://gdown.baidu.com/data/wisegame/91319a5a1dfae322/baidu_16785426.apk"; 
String fileName = "baidu_16785426.apk"; 
int threadNum = 5; 
String filepath = path + fileName; 
Log.d(TAG, "download file path:" + filepath); 
downloadTask task = new downloadTask(downloadUrl, threadNum, filepath); 
task.start(); 
} 
/** 
* 多线程文件下载 
* 
* @author yangxiaolong 
* @2014-8-7 
*/ 
class downloadTask extends Thread { 
private String downloadUrl;// 下载链接地址 
private int threadNum;// 开启的线程数 
private String filePath;// 保存文件路径地址 
private int blockSize;// 每一个线程的下载量 
public downloadTask(String downloadUrl, int threadNum, String fileptah) { 
this.downloadUrl = downloadUrl; 
this.threadNum = threadNum; 
this.filePath = fileptah; 
} 
@Override 
public void run() { 
FileDownloadThread[] threads = new FileDownloadThread[threadNum]; 
try { 
URL url = new URL(downloadUrl); 
Log.d(TAG, "download file http path:" + downloadUrl); 
URLConnection cOnn= url.openConnection(); 
// 读取下载文件总大小 
int fileSize = conn.getContentLength(); 
if (fileSize <= 0) { 
System.out.println("读取文件失败"); 
return; 
} 
// 设置ProgressBar最大的长度为文件Size 
mProgressbar.setMax(fileSize); 
// 计算每条线程下载的数据长度 
blockSize = (fileSize % threadNum) == 0 &#63; fileSize / threadNum 
: fileSize / threadNum + 1; 
Log.d(TAG, "fileSize:" + fileSize + " blockSize:"); 
File file = new File(filePath); 
for (int i = 0; i 

FileDownloadThread:

package com.amos.app; 
import java.io.BufferedInputStream; 
import java.io.File; 
import java.io.IOException; 
import java.io.RandomAccessFile; 
import java.net.URL; 
import java.net.URLConnection; 
import android.util.Log; 
/** 
* 文件下载类 
* 
* @author yangxiaolong 
* @2014-5-6 
*/ 
public class FileDownloadThread extends Thread { 
private static final String TAG = FileDownloadThread.class.getSimpleName(); 
/** 当前下载是否完成 */ 
private boolean isCompleted = false; 
/** 当前下载文件长度 */ 
private int downloadLength = 0; 
/** 文件保存路径 */ 
private File file; 
/** 文件下载路径 */ 
private URL downloadUrl; 
/** 当前下载线程ID */ 
private int threadId; 
/** 线程下载数据长度 */ 
private int blockSize; 
/** 
* 
* @param url:文件下载地址 
* @param file:文件保存路径 
* @param blocksize:下载数据长度 
* @param threadId:线程ID 
*/ 
public FileDownloadThread(URL downloadUrl, File file, int blocksize, 
int threadId) { 
this.downloadUrl = downloadUrl; 
this.file = file; 
this.threadId = threadId; 
this.blockSize = blocksize; 
} 
@Override 
public void run() { 
BufferedInputStream bis = null; 
RandomAccessFile raf = null; 
try { 
URLConnection cOnn= downloadUrl.openConnection(); 
conn.setAllowUserInteraction(true); 
int startPos = blockSize * (threadId - 1);//开始位置 
int endPos = blockSize * threadId - 1;//结束位置 
//设置当前线程下载的起点、终点 
conn.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos); 
System.out.println(Thread.currentThread().getName() + " bytes=" 
+ startPos + "-" + endPos); 
byte[] buffer = new byte[1024]; 
bis = new BufferedInputStream(conn.getInputStream()); 
raf = new RandomAccessFile(file, "rwd"); 
raf.seek(startPos); 
int len; 
while ((len = bis.read(buffer, 0, 1024)) != -1) { 
raf.write(buffer, 0, len); 
downloadLength += len; 
} 
isCompleted = true; 
Log.d(TAG, "current thread task has finished,all size:" 
+ downloadLength); 
} catch (IOException e) { 
e.printStackTrace(); 
} finally { 
if (bis != null) { 
try { 
bis.close(); 
} catch (IOException e) { 
e.printStackTrace(); 
} 
} 
if (raf != null) { 
try { 
raf.close(); 
} catch (IOException e) { 
e.printStackTrace(); 
} 
} 
} 
} 
/** 
* 线程文件是否下载完毕 
*/ 
public boolean isCompleted() { 
return isCompleted; 
} 
/** 
* 线程下载文件长度 
*/ 
public int getDownloadLength() { 
return downloadLength; 
} 
}

效果图:


Log控制台:

可以看到文件总大小、我们创建的5个线程每个负责下载的区间

SD卡:

关于Android实现网络多线程文件下载小编就给大家介绍这么多,希望对大家有所帮助!同时也非常感谢大家一直以来对网站的支持!


推荐阅读
  • 本文分享了作者在不同版本的Ubuntu系统上尝试编译VLC for Android的经历,包括遇到的技术难题及解决方案,旨在帮助开发者避免常见的编译问题。 ... [详细]
  • 在 Django 模型中,ForeignKey 的 on_delete 参数定义了当关联对象被删除时,当前模型实例的行为。本文详细解释了 on_delete 的各个选项及其具体作用。 ... [详细]
  • Android Studio: 修改应用包名的详细步骤
    本文详细介绍了在Android Studio中如何修改应用的包名,包括具体的操作步骤和注意事项。这对于需要更改包名以适应不同需求的开发者非常有用。 ... [详细]
  • 本文介绍了一个使用mii-tool工具检查网络接口状态的Bash脚本,并将结果记录到日志文件中。 ... [详细]
  • 本文探讨了如何在Windows程序中实现高精度的定时控制,特别是针对需要精确控制发包频率的应用场景,如“小兵以太网测试仪”。 ... [详细]
  • 本文将介绍如何使用原生JavaScript实现一个连连看小游戏中的一个关键功能:当用户点击列表项时,获取该列表项的索引。 ... [详细]
  • 在使用 Python 编程时,如果在一个函数中引用了未声明为全局变量的外部变量,可能会遇到 UnboundLocalError。本文将通过一个具体的例子来解释如何解决这个问题。 ... [详细]
  • 本文详细介绍了进程、线程和协程的概念及其之间的区别与联系。进程是在内存中运行的独立实体,具有独立的地址空间和资源;线程是操作系统调度的基本单位,属于进程内部;协程则是用户态下的轻量级调度单元,性能更高。 ... [详细]
  • Python 日志记录模块详解
    日志记录机制是软件开发中不可或缺的一部分,它帮助开发者追踪和调试程序运行时的各种异常。Python 提供了内置的 logging 模块,使我们在代码中记录和管理日志信息变得更加方便。本文将详细介绍如何使用 Python 的 logging 模块。 ... [详细]
  • 题目描述:计算从起点到终点的最小能量消耗。如果下一个单元格的风向与当前单元格相同,则消耗为0,否则为1。共有8个可能的方向。 ... [详细]
  • 本文介绍如何通过参数化查询来防止SQL注入攻击,确保数据库的安全性。示例代码展示了在C#中使用参数化查询添加学生信息的方法。 ... [详细]
  • 树莓派4B:安装基础操作系统指南
    本文将详细介绍如何为树莓派4B安装基础操作系统,包括所需材料、镜像下载、镜像烧录以及更换国内源等步骤。 ... [详细]
  • 本文介绍了GitHub上的一些Python开源项目,特别是IM(即时通讯)技术的应用。通过Sealtalk项目,探讨了如何利用开源SDK提升开发效率。 ... [详细]
  • 本文探讨了如何在游戏启动画面中移除广告,特别是在游戏数据加载期间(大约5-6秒)广告会短暂显示的问题。通过调整XML布局和代码逻辑,可以实现广告的延迟加载或完全移除。 ... [详细]
  • 腾讯云移动推送TPNS(Tencent Push Notification Service)为APP开发者和运营人员提供了一站式、高效、稳定的推送解决方案,帮助提升用户活跃度和运营效率。 ... [详细]
author-avatar
不语皓月
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有