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

 更新时间:2016年03月24日 15:25:53   作者:Never-say-Never  
这篇文章主要介绍了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 ? fileSize / threadNum 
: fileSize / threadNum + 1; 
Log.d(TAG, "fileSize:" + fileSize + " blockSize:"); 
File file = new File(filePath); 
for (int i = 0; i < threads.length; i++) { 
// 启动线程,分别下载每个线程需要下载的部分 
threads[i] = new FileDownloadThread(url, file, blockSize, 
(i + 1)); 
threads[i].setName("Thread:" + i); 
threads[i].start(); 
} 
boolean isfinished = false; 
int downloadedAllSize = 0; 
while (!isfinished) { 
isfinished = true; 
// 当前所有线程下载总量 
downloadedAllSize = 0; 
for (int i = 0; i < threads.length; i++) { 
downloadedAllSize += threads[i].getDownloadLength(); 
if (!threads[i].isCompleted()) { 
isfinished = false; 
} 
} 
// 通知handler去更新视图组件 
Message msg = new Message(); 
msg.getData().putInt("size", downloadedAllSize); 
mHandler.sendMessage(msg); 
// Log.d(TAG, "current downloadSize:" + downloadedAllSize); 
Thread.sleep(1000);// 休息1秒后再读取下载进度 
} 
Log.d(TAG, " all of downloadSize:" + downloadedAllSize); 
} catch (MalformedURLException e) { 
e.printStackTrace(); 
} catch (IOException e) { 
e.printStackTrace(); 
} catch (InterruptedException e) { 
e.printStackTrace(); 
} 
} 
} 
}

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实现网络多线程文件下载小编就给大家介绍这么多,希望对大家有所帮助!同时也非常感谢大家一直以来对脚本之家网站的支持!

相关文章

  • Android自定义SwipeLayout仿QQ侧滑条目

    Android自定义SwipeLayout仿QQ侧滑条目

    这篇文章主要为大家详细介绍了Android自定义SwipeLayout仿QQ侧滑条目,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-08-08
  • Android开发之自带下载器DownloadManager的使用示例代码

    Android开发之自带下载器DownloadManager的使用示例代码

    本篇文章主要介绍了Android开发之自带下载器DownloadManager的使用示例代码,Android自带的DownloadManager是一个很好的下载文件的工具,有兴趣的可以了解一下。
    2017-03-03
  • Android短信验证码(用的Mob短信验证)

    Android短信验证码(用的Mob短信验证)

    这篇文章主要为大家详细介绍了Android短信验证码,使用Mob短信验证,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05
  • android textview设置字体的行距和字间距

    android textview设置字体的行距和字间距

    这篇文章主要介绍了android textview设置字体的行距和字间距的方法,非常简单实用,有需要的小伙伴可以参考下
    2016-05-05
  • Android多线程断点续传下载实现代码

    Android多线程断点续传下载实现代码

    这篇文章主要介绍了Android多线程断点续传下载实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • Android中监听软键盘显示状态实现代码

    Android中监听软键盘显示状态实现代码

    这篇文章主要介绍了Android中监听软键盘显示状态实现代码,本文直接给出核心实现代码,需要的朋友可以参考下
    2015-04-04
  • UI 开源组件Flutter图表范围选择器使用详解

    UI 开源组件Flutter图表范围选择器使用详解

    这篇文章主要为大家介绍了UI 开源组件Flutter图表范围选择器使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • 自定义搜索功能Android实现

    自定义搜索功能Android实现

    这篇文章主要为大家详细介绍了自定义搜索功能,由Android代码实现,感兴趣的小伙伴们可以参考一下
    2016-05-05
  • Android开发之AlertDialog实现弹出对话框

    Android开发之AlertDialog实现弹出对话框

    这篇文章主要为大家详细介绍了Android开发之AlertDialog实现弹出对话框,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-09-09
  • Kotlin数据容器深入讲解

    Kotlin数据容器深入讲解

    Kotlin的数据容器分为数组和集合。其中集合分为集合Set、队列List、映射Map等三种集合,每种又包括只读和可变两种类型,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-09-09

最新评论