Android實(shí)現(xiàn)多線程下載文件的方法
本文實(shí)例講述了Android實(shí)現(xiàn)多線程下載文件的方法。分享給大家供大家參考。具體如下:
多線程下載大概思路就是通過Range 屬性實(shí)現(xiàn)文件分段,然后用RandomAccessFile 來讀寫文件,最終合并為一個文件
首先看下效果圖:

創(chuàng)建工程 ThreadDemo
首先布局文件 threaddemo.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="下載地址" /> <TextView android:id="@+id/downloadurl" android:layout_width="fill_parent" android:layout_height="wrap_content" android:lines="5" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="線程數(shù)" /> <EditText android:id="@+id/downloadnum" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <ProgressBar android:id="@+id/downloadProgressBar" android:layout_width="fill_parent" style="?android:attr/progressBarStyleHorizontal" android:layout_height="wrap_content" /> <TextView android:id="@+id/downloadinfo" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="下載進(jìn)度 0" /> <Button android:id="@+id/downloadbutton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="開始下載" /> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="下載地址" /> <TextViewandroid:id="@+id/downloadurl"android:layout_width="fill_parent" android:layout_height="wrap_content" android:lines="5"/> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="線程數(shù)" /> <EditTextandroid:id="@+id/downloadnum"android:layout_width="fill_parent" android:layout_height="wrap_content" /> <ProgressBarandroid:id="@+id/downloadProgressBar"android:layout_width="fill_parent" style="?android:attr/progressBarStyleHorizontal" android:layout_height="wrap_content" /> <TextViewandroid:id="@+id/downloadinfo"android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="下載進(jìn)度 0"/> <Buttonandroid:id="@+id/downloadbutton"android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="開始下載"/> </LinearLayout>
主界面 Acitivity
public class ThreadDownloadDemo extends Activity {
private TextView downloadurl;
private EditText downloadnum;
private Button downloadbutton;
private ProgressBar downloadProgressBar;
private TextView downloadinfo;
private int downloadedSize = 0;
private int fileSize = 0;
private long downloadtime;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.threaddemo);
downloadurl = (TextView) findViewById(R.id.downloadurl);
downloadurl.setText("http://file16.top100.cn/201105110911/AA5CC27CBE34DEB50A194581D1300881/Special_323149/%E8%8D%B7%E5%A1%98%E6%9C%88%E8%89%B2.mp3");
downloadnum = (EditText) findViewById(R.id.downloadnum);
downloadinfo = (TextView) findViewById(R.id.downloadinfo);
downloadbutton = (Button) findViewById(R.id.downloadbutton);
downloadProgressBar = (ProgressBar) findViewById(R.id.downloadProgressBar);
downloadProgressBar.setVisibility(View.VISIBLE);
downloadProgressBar.setMax(100);
downloadProgressBar.setProgress(0);
downloadbutton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
download();
downloadtime = SystemClock.currentThreadTimeMillis();
}
});
}
private void download() {
// 獲取SD卡目錄
String dowloadDir = Environment.getExternalStorageDirectory()
+ "/threaddemodownload/";
File file = new File(dowloadDir);
//創(chuàng)建下載目錄
if (!file.exists()) {
file.mkdirs();
}
//讀取下載線程數(shù),如果為空,則單線程下載
int downloadTN = Integer.valueOf("".equals(downloadnum.getText()
.toString()) ? "1" : downloadnum.getText().toString());
String fileName = "hetang.mp3";
//開始下載前把下載按鈕設(shè)置為不可用
downloadbutton.setClickable(false);
//進(jìn)度條設(shè)為0
downloadProgressBar.setProgress(0);
//啟動文件下載線程
new downloadTask("http://file16.top100.cn/201105110911/AA5CC27CBE34DEB50A194581D1300881/Special_323149/%E8%8D%B7%E5%A1%98%E6%9C%88%E8%89%B2.mp3", Integer
.valueOf(downloadTN), dowloadDir + fileName).start();
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
//當(dāng)收到更新視圖消息時,計(jì)算已完成下載百分比,同時更新進(jìn)度條信息
int progress = (Double.valueOf((downloadedSize * 1.0 / fileSize * 100))).intValue();
if (progress == 100) {
downloadbutton.setClickable(true);
downloadinfo.setText("下載完成!");
Dialog mdialog = new AlertDialog.Builder(ThreadDownloadDemo.this)
.setTitle("提示信息")
.setMessage("下載完成,總用時為:"+(SystemClock.currentThreadTimeMillis()-downloadtime)+"毫秒")
.setNegativeButton("確定", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.create();
mdialog.show();
} else {
downloadinfo.setText("當(dāng)前進(jìn)度:" + progress + "%");
}
downloadProgressBar.setProgress(progress);
}
};
public class downloadTask extends Thread {
private int blockSize, downloadSizeMore;
private int threadNum = 5;
String urlStr, threadNo, fileName;
public downloadTask(String urlStr, int threadNum, String fileName) {
this.urlStr = urlStr;
this.threadNum = threadNum;
this.fileName = fileName;
}
@Override
public void run() {
FileDownloadThread[] fds = new FileDownloadThread[threadNum];
try {
URL url = new URL(urlStr);
URLConnection conn = url.openConnection();
//防止返回-1
InputStream in = conn.getInputStream();
//獲取下載文件的總大小
fileSize = conn.getContentLength();
Log.i("bb", "======================fileSize:"+fileSize);
//計(jì)算每個線程要下載的數(shù)據(jù)量
blockSize = fileSize / threadNum;
// 解決整除后百分比計(jì)算誤差
downloadSizeMore = (fileSize % threadNum);
File file = new File(fileName);
for (int i = 0; i < threadNum; i++) {
Log.i("bb", "======================i:"+i);
//啟動線程,分別下載自己需要下載的部分
FileDownloadThread fdt = new FileDownloadThread(url, file, i * blockSize, (i + 1) * blockSize - 1);
fdt.setName("Thread" + i);
fdt.start();
fds[i] = fdt;
}
boolean finished = false;
while (!finished) {
// 先把整除的余數(shù)搞定
downloadedSize = downloadSizeMore;
finished = true;
for (int i = 0; i < fds.length; i++) {
downloadedSize += fds[i].getDownloadSize();
if (!fds[i].isFinished()) {
finished = false;
}
}
handler.sendEmptyMessage(0);
//線程暫停一秒
sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class ThreadDownloadDemo extends Activity {
private TextView downloadurl;
private EditText downloadnum;
private Button downloadbutton;
private ProgressBar downloadProgressBar;
private TextView downloadinfo;
private int downloadedSize = 0;
private int fileSize = 0;
private long downloadtime;
@Overridepublic void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.threaddemo);
downloadurl = (TextView) findViewById(R.id.downloadurl);
downloadurl.setText("http://file16.top100.cn/201105110911/AA5CC27CBE34DEB50A194581D1300881/Special_323149/%E8%8D%B7%E5%A1%98%E6%9C%88%E8%89%B2.mp3");
downloadnum = (EditText) findViewById(R.id.downloadnum);
downloadinfo = (TextView) findViewById(R.id.downloadinfo);
downloadbutton = (Button) findViewById(R.id.downloadbutton);
downloadProgressBar = (ProgressBar) findViewById(R.id.downloadProgressBar);
downloadProgressBar.setVisibility(View.VISIBLE);
downloadProgressBar.setMax(100);
downloadProgressBar.setProgress(0);
downloadbutton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {download();
downloadtime = SystemClock.currentThreadTimeMillis();
}
}
);
}
private void download() {// 獲取SD卡目錄
String dowloadDir = Environment.getExternalStorageDirectory()+ "/threaddemodownload/";
File file = new File(dowloadDir);
//創(chuàng)建下載目錄
if (!file.exists()) {file.mkdirs();
}//讀取下載線程數(shù),如果為空,則單線程下載
int downloadTN = Integer.valueOf("".equals(downloadnum.getText().toString()) ? "1" : downloadnum.getText().toString());
String fileName = "hetang.mp3";
//開始下載前把下載按鈕設(shè)置為不可用
downloadbutton.setClickable(false);
//進(jìn)度條設(shè)為0
downloadProgressBar.setProgress(0);
//啟動文件下載線程
new downloadTask("http://file16.top100.cn/201105110911/AA5CC27CBE34DEB50A194581D1300881/Special_323149/%E8%8D%B7%E5%A1%98%E6%9C%88%E8%89%B2.mp3", Integer.valueOf(downloadTN), dowloadDir + fileName).start();
}
Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {
//當(dāng)收到更新視圖消息時,計(jì)算已完成下載百分比,同時更新進(jìn)度條信息
int progress = (Double.valueOf((downloadedSize * 1.0 / fileSize * 100))).intValue();
if (progress == 100) {downloadbutton.setClickable(true);
downloadinfo.setText("下載完成!");
Dialog mdialog = new AlertDialog.Builder(ThreadDownloadDemo.this).setTitle("提示信息").setMessage("下載完成,總用時為:"+(SystemClock.currentThreadTimeMillis()-downloadtime)+"毫秒").setNegativeButton("確定", new DialogInterface.OnClickListener(){@Overridepublic void onClick(DialogInterface dialog, int which) {dialog.dismiss();
}}).create();
mdialog.show();
} else {
downloadinfo.setText("當(dāng)前進(jìn)度:" + progress + "%");
}
downloadProgressBar.setProgress(progress);
}
};
public class downloadTask extends Thread {private int blockSize, downloadSizeMore;
private int threadNum = 5;
String urlStr, threadNo, fileName;
public downloadTask(String urlStr, int threadNum, String fileName) {
this.urlStr = urlStr;
this.threadNum = threadNum;
this.fileName = fileName;
}
@Overridepublic void run() {
FileDownloadThread[] fds = new FileDownloadThread[threadNum];
try {URL url = new URL(urlStr);
URLConnection conn = url.openConnection();
//防止返回-1
InputStream in = conn.getInputStream();
//獲取下載文件的總大小
fileSize = conn.getContentLength();
Log.i("bb", "======================fileSize:"+fileSize);
//計(jì)算每個線程要下載的數(shù)據(jù)量
blockSize = fileSize / threadNum;
// 解決整除后百分比計(jì)算誤差
downloadSizeMore = (fileSize % threadNum);
File file = new File(fileName);
for (int i = 0; i < threadNum; i++) {
Log.i("bb", "======================i:"+i);
//啟動線程,分別下載自己需要下載的部分
FileDownloadThread fdt = new FileDownloadThread(url, file, i * blockSize, (i + 1) * blockSize - 1);
fdt.setName("Thread" + i);
fdt.start();
fds[i] = fdt;
}
boolean finished = false;
while (!finished) {
// 先把整除的余數(shù)搞定
downloadedSize = downloadSizeMore;
finished = true;
for (int i = 0; i < fds.length; i++) {
downloadedSize += fds[i].getDownloadSize();
if (!fds[i].isFinished()) {
finished = false;
}
}
handler.sendEmptyMessage(0);
//線程暫停一秒sleep(1000);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
}
這里啟動線程將文件分割為幾個部分,每一個部分再啟動一個線程去下載數(shù)據(jù)
下載文件的線程
public class FileDownloadThread extends Thread{
private static final int BUFFER_SIZE=1024;
private URL url;
private File file;
private int startPosition;
private int endPosition;
private int curPosition;
//標(biāo)識當(dāng)前線程是否下載完成
private boolean finished=false;
private int downloadSize=0;
public FileDownloadThread(URL url,File file,int startPosition,int endPosition){
this.url=url;
this.file=file;
this.startPosition=startPosition;
this.curPosition=startPosition;
this.endPosition=endPosition;
}
@Override
public void run() {
BufferedInputStream bis = null;
RandomAccessFile fos = null;
byte[] buf = new byte[BUFFER_SIZE];
URLConnection con = null;
try {
con = url.openConnection();
con.setAllowUserInteraction(true);
//設(shè)置當(dāng)前線程下載的起止點(diǎn)
con.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);
Log.i("bb", Thread.currentThread().getName()+" bytes=" + startPosition + "-" + endPosition);
//使用java中的RandomAccessFile 對文件進(jìn)行隨機(jī)讀寫操作
fos = new RandomAccessFile(file, "rw");
//設(shè)置寫文件的起始位置
fos.seek(startPosition);
bis = new BufferedInputStream(con.getInputStream());
//開始循環(huán)以流的形式讀寫文件
while (curPosition < endPosition) {
int len = bis.read(buf, 0, BUFFER_SIZE);
if (len == -1) {
break;
}
fos.write(buf, 0, len);
curPosition = curPosition + len;
if (curPosition > endPosition) {
downloadSize+=len - (curPosition - endPosition) + 1;
} else {
downloadSize+=len;
}
}
//下載完成設(shè)為true
this.finished = true;
bis.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public boolean isFinished(){
return finished;
}
public int getDownloadSize() {
return downloadSize;
}
}
public class FileDownloadThread extends Thread{
private static final int BUFFER_SIZE=1024;
private URL url;
private File file;
private int startPosition;
private int endPosition;
private int curPosition;//標(biāo)識當(dāng)前線程是否下載完成
private boolean finished=false;
private int downloadSize=0;
public FileDownloadThread(URL url,File file,int startPosition,int endPosition){
this.url=url;
this.file=file;
this.startPosition=startPosition;
this.curPosition=startPosition;
this.endPosition=endPosition;
}
@Overridepublic void run() {
BufferedInputStream bis = null;
RandomAccessFile fos = null;
byte[] buf = new byte[BUFFER_SIZE];
URLConnection con = null;
try {
con = url.openConnection();
con.setAllowUserInteraction(true);
//設(shè)置當(dāng)前線程下載的起止點(diǎn)
con.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);
Log.i("bb", Thread.currentThread().getName()+" bytes=" + startPosition + "-" + endPosition);
//使用java中的RandomAccessFile 對文件進(jìn)行隨機(jī)讀寫操作
fos = new RandomAccessFile(file, "rw");
//設(shè)置寫文件的起始位置
fos.seek(startPosition);
bis = new BufferedInputStream(con.getInputStream());
//開始循環(huán)以流的形式讀寫文件
while (curPosition < endPosition) {
int len = bis.read(buf, 0, BUFFER_SIZE);
if (len == -1) {
break;
}
fos.write(buf, 0, len);
curPosition = curPosition + len;
if (curPosition > endPosition) {
downloadSize+=len - (curPosition - endPosition) + 1;
} else {
downloadSize+=len;
}
}
//下載完成設(shè)為true
this.finished = true;
bis.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public boolean isFinished(){return finished;}
public int getDownloadSize() {return downloadSize;}
}
這里通過RandomAccessFile 的seek方法定位到相應(yīng)的位置 并實(shí)時記錄下載量
當(dāng)然這里需要聯(lián)網(wǎng)和訪問SD卡 所以要加上相應(yīng)的權(quán)限
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
這樣就OK了 下面可以看看斷點(diǎn)續(xù)傳的問題了。有待測試~~
希望本文所述對大家的Android程序設(shè)計(jì)有所幫助。
- android中多線程下載實(shí)例
- android實(shí)現(xiàn)多線程下載文件(支持暫停、取消、斷點(diǎn)續(xù)傳)
- Android版多線程下載 仿下載助手(最新)
- Android編程開發(fā)實(shí)現(xiàn)帶進(jìn)度條和百分比的多線程下載
- Android FTP 多線程斷點(diǎn)續(xù)傳下載\上傳的實(shí)例
- Android多線程+單線程+斷點(diǎn)續(xù)傳+進(jìn)度條顯示下載功能
- Android多線程斷點(diǎn)續(xù)傳下載功能實(shí)現(xiàn)代碼
- Android實(shí)現(xiàn)多線程斷點(diǎn)下載的方法
- Android實(shí)現(xiàn)多線程下載圖片的方法
- Android線程池控制并發(fā)數(shù)多線程下載
相關(guān)文章
Android Studio多工程引用同一個library項(xiàng)目配置的解決方法
大家在使用android studio的時候,會遇到多個項(xiàng)目引用相同的library這篇文章主要介紹了Android Studio多工程引用同一個library項(xiàng)目配置方法,需要的朋友可以參考下2018-03-03
屏蔽RecyclerView單邊滑動到頭陰影(fadingEdge)的方法
這篇文章主要給大家介紹了如何屏蔽RecyclerView單邊滑動到頭陰影(fadingEdge)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-04-04
android Launcher AppWidget添加步驟介紹
大家好,本篇文章主要講的是android Launcher AppWidget添加步驟介紹,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽2022-01-01
Android實(shí)現(xiàn)左側(cè)滑動菜單
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)左側(cè)滑動菜單,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
Android開發(fā)實(shí)現(xiàn)文件關(guān)聯(lián)方法介紹
這篇文章主要介紹了Android開發(fā)實(shí)現(xiàn)文件關(guān)聯(lián)方法介紹,具有一定參考價(jià)值,需要的朋友樂意了解下。2017-10-10
RecyclerView使用payload實(shí)現(xiàn)局部刷新
這篇文章主要為大家詳細(xì)介紹了RecyclerView使用payload實(shí)現(xiàn)局部刷新,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10
Android 深入探究自定義view之流式布局FlowLayout的使用
FlowLayout(int align, int hgap, int vgap)創(chuàng)建一個新的流布局管理器,它具有指定的對齊方式以及指定的水平和垂直間隙,意思就是說從左上角開始添加原件,依次往后排,第一行擠滿了就換一行接著排2021-11-11
Android自定義ViewGroup實(shí)現(xiàn)可滾動的橫向布局(2)
這篇文章主要介紹了Android自定義ViewGroup實(shí)現(xiàn)可滾動的橫向布局,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12

