(转自:http://blog.csdn.net/mad1989/article/details/38421465)

实现原理

(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;
} }

补充:.xml文件:

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical" > <Button
android:id="@+id/download_btn"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="50dp"
android:text="Download"
android:textSize="18sp" /> <ProgressBar
android:id="@+id/download_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
android:layout_marginTop="20dp"
android:indeterminate="false"
android:max="100" /> <TextView
android:id="@+id/download_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:maxLines="1"
android:text="50%"
android:textSize="18dip" /> </LinearLayout>

效果图:

Android 实现网络多线程APK文件下载的更多相关文章

  1. Android实现网络多线程断点续传下载(转)

    本示例介绍在Android平台下通过HTTP协议实现断点续传下载. 我们编写的是Andorid的HTTP协议多线程断点下载应用程序.直接使用单线程下载HTTP文件对我们来说是一件非常简单的事.那么,多 ...

  2. Android实现网络多线程断点续传下载

    本示例介绍在Android平台下通过HTTP协议实现断点续传下载. 我们编写的是Andorid的HTTP协议多线程断点下载应用程序.直接使用单线程下载HTTP文件对我们来说是一件非常简单的事.那么,多 ...

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

    实现原理 (1)首先获得下载文件的长度,然后设置本地文件的长度. (2)根据文件长度和线程数计算每条线程下载的数据长度和下载位置. 如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M ...

  4. Android网络多线程断点续传下载

    本示例介绍在Android平台下通过HTTP协议实现断点续传下载. 我们编写的是Andorid的HTTP协议多线程断点下载应用程序.直接使用单线程下载HTTP文件对我们来说是一件非常简单的事.那么,多 ...

  5. 基于Retrofit+RxJava的Android分层网络请求框架

    目前已经有不少Android客户端在使用Retrofit+RxJava实现网络请求了,相比于xUtils,Volley等网络访问框架,其具有网络访问效率高(基于OkHttp).内存占用少.代码量小以及 ...

  6. Android okHttp网络请求之文件上传下载

    前言: 前面介绍了基于okHttp的get.post基本使用(http://www.cnblogs.com/whoislcj/p/5526431.html),今天来实现一下基于okHttp的文件上传. ...

  7. 七、Android学习第六天——SQLite与文件下载(转)

    (转自:http://wenku.baidu.com/view/af39b3164431b90d6c85c72f.html) 七.Android学习第六天——SQLite与文件下载 SQLite SQ ...

  8. 网络--三种网络通讯方式及Android的网络通讯机制

    Android平台有三种网络接口可以使用,他们分别是:java.net.*(标准Java接口).Org.apache接口和Android.net.*(Android网络接口).下面分别介绍这些接口的功 ...

  9. Android实现网络访问

    Android实现网络访问 开发工具:Andorid Studio 1.3 运行环境:Android 4.4 KitKat 工程内容 1) 熟练使用HttpURLConnection访问WebServ ...

随机推荐

  1. js作用域和变量提升

    Function declarations and variable declarations are always moved (“hoisted”) invisibly to the top of ...

  2. .net 将List序列化成Json字符串

    将List类型转化为Json,是我们平常开发时最常见的了.在使用中,有很多种方法,也可以使用. 第一种 第三方组件:Newtonsoft.Json.dll //转化成Json Newtonsoft.J ...

  3. 如何设置 Panorama 控件的只读 SelectedIndex 属性?

    在 OnNavigatedTo() 方法中设置: panoramaControl.DefaultItem = panoramaControl.Items[indexToSet];

  4. 我的angularjs源码学习之旅2——依赖注入

    依赖注入起源于实现控制反转的典型框架Spring框架,用来削减计算机程序的耦合问题.简单来说,在定义方法的时候,方法所依赖的对象就被隐性的注入到该方法中,在方法中可以直接使用,而不需要在执行该函数的时 ...

  5. window.open

    window.open 的三个参数 第一个参数:url 第二个参数:对应新打开标签或者窗口的window.name属性,如果为填默认为 "_blank" 第三个参数:如果有则打开的 ...

  6. 一台主机上安装多个Tomcat

    1. 下载解压版的Tomcat,并解压两次,分别命名为Tomcat_Server_01和Tomcat_Server_02: 2. 进入Tomcat_Server_01\bin目录,编辑service文 ...

  7. String和Date、Timestamp之间的转换

    一.String与Date(java.util.Date)互转 1.1 String -> Date String dateStr = "2010/05/04 12:34:23&quo ...

  8. php实现设计模式之 工厂方法模式

    <?php /* * *工厂方法模式,创建型模式 定义一个用于创建对象的工厂接口,让子类决定实例化哪一个类.Factory Method使用一个类的实例化延迟到其子类 * * 四个角色: * 工 ...

  9. WebApi系列~FromUri参数自动解析成实体的要求

    回到目录 关于webapi我之前写了一些文章,大家可以根据目录去浏览,今天要说的是个怪问题,也是被我忽略的一个问题,当你的Url参数需要被Api自动解析成实体的属性,实事上是要有条件的,不是所以属性都 ...

  10. Apache Lucene(全文检索引擎)—搜索

    目录 返回目录:http://www.cnblogs.com/hanyinglong/p/5464604.html 本项目Demo已上传GitHub,欢迎大家fork下载学习:https://gith ...