Android中实现多线程下载的两种方式示例及浅析之一(无断点续传)
来源:互联网 发布:正版spss软件多少钱 编辑:程序博客网 时间:2024/06/05 17:49
Android里面下载一般应该有两种方式,一种是多线程实现,一种是异步。而实现多线程在java里可以有两种方式,一种是多个线程实现同一个Runnable,另外就是将一个文件长度分为n个部分,然后让n个线程去分别下载1部分。
[1]我阅读了网上的一个方法2的实现代码,然后自己实现了一个,基本思路就是实现一个单个线程类,在这个单个线程类就完成3个操作,
1是从URL连接读取流
2是将读取的字节数组写入到本地文件中
3是将当前已经读取的长度返回
然后开启一个线程,这个线程的操作是创建一个线程数组,每个元素是刚刚的那个单个线程类,在这个线程中分配每个线程的下载起点和下载终点,然后根据各个线程的下载进度之和来更新进度条。
demo如下:
package com.study.daniel.downloadtest;import java.io.BufferedInputStream;import java.io.File;import java.io.IOException;import java.io.RandomAccessFile;import java.net.MalformedURLException;import java.net.URL;import java.net.URLConnection;/** * Created by daniel on 15-8-1. */public class DownloadSingleThread extends Thread{//单个下载线程 /**文件下载URL*/ private String downloadURL; /**文件保存路径*/ private File file; /**单个线程下载文件大小*/ private int partSize; /**当前已经下载的长度*/ private int currentLength; /**线程id*/ private int threadID; /**当前线程是否下载完成*/ private boolean isCompleted=false; public DownloadSingleThread(String downloadURL,File file,int partSize,int threadID){ this.partSize=partSize; this.downloadURL=downloadURL; this.file=file; this.threadID=threadID; } public void run(){ BufferedInputStream bis=null;//读取下载文件流 RandomAccessFile raf=null;//写入文件流 try { //计算下载起点,终点 int startPos = partSize * threadID;//开始位置 int endPos = partSize * (threadID +1)-1;//结束位置 //建立连接 URL url=new URL(downloadURL); URLConnection connection=url.openConnection();//获得连接 //设置本次连接的下载起点和终点 connection.setRequestProperty("Range","bytes="+startPos+"-"+endPos); //创建输入流 bis=new BufferedInputStream(connection.getInputStream()); //创建文件写入流 raf=new RandomAccessFile(file,"rwd"); raf.seek(startPos); byte[] buffer=new byte[1024];//一次读取一k int len; while((len=bis.read(buffer,0,1024))!=-1){//本线程下载是否完成 raf.write(buffer,0,len);//读取并写入 currentLength+=len; } isCompleted=true;//当前线程下载完成 } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally {//检查并关闭IO流 if(bis!=null) try { bis.close(); } catch (IOException e) { e.printStackTrace(); } if(raf!=null) try { raf.close(); } catch (IOException e) { e.printStackTrace(); } } } public int getCurrentLength(){//返回当前下载长度 return currentLength; } public boolean isCompleted(){ return isCompleted; }}-------------------------------------------------------package com.study.daniel.downloadtest;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.os.Message;import android.support.v7.app.ActionBarActivity;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.ProgressBar;import android.widget.TextView;import android.widget.Toast;import java.io.File;import java.io.IOException;import java.net.MalformedURLException;import java.net.URL;import java.net.URLConnection;public class MainActivity extends ActionBarActivity implements View.OnClickListener { private TextView tv_downloaded; private ProgressBar progressBar; Handler handler = new Handler() { public void handleMessage(Message msg) { //更新进度条 progressBar.setProgress(msg.getData().getInt("size")); //算算当前进度比 float temp = (float) progressBar.getProgress() / (float) progressBar.getMax(); //显示进度百分比 tv_downloaded.setText((int) (temp * 100) + "%"); //下载完成 if (progressBar.getProgress() == progressBar.getMax()) Toast.makeText(MainActivity.this, "下载完成!", Toast.LENGTH_SHORT).show(); msg.arg1 = 0; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv_downloaded = (TextView) findViewById(R.id.tv_downloaded); progressBar = (ProgressBar) findViewById(R.id.progressbar); findViewById(R.id.bt_download).setOnClickListener(this); } public void onClick(View view) { if (view.getId() == R.id.bt_download) download(); } public void download() { //设置下载保存路径 String path = Environment.getExternalStorageDirectory() + "/newdownload/";//这是下载文件保存目录 File file = new File(path); if (!file.exists()) {//如果该保存目录不存在就创建 file.mkdir(); } //初始化进度条 progressBar.setProgress(0); //设置下载URL String downloadURL = "http://gdown.baidu.com/data/wisegame/91319a5a1dfae322/baidu_16785426.apk"; String fileName = "baidu_16785426.apk"; int threadNum = 5; String filePath = path + fileName; System.out.println("download file path:" + filePath); DownloadTask downloadTask = new DownloadTask(threadNum, downloadURL, filePath); downloadTask.start(); } private class DownloadTask extends Thread { private int threadNum;//开启线程数 private String downloadURL;//下载链接地址 private String savePath;//文件保存路径 private int partSize;//每一个线程的下载量,根据下载文件总长度计算得出 public DownloadTask(int threadNum, String downloadURL, String savePath) { this.threadNum = threadNum; this.downloadURL = downloadURL; this.savePath = savePath; } public void run() { try { URL url = new URL(downloadURL); URLConnection connection = url.openConnection(); int totalLength = connection.getContentLength();//总长度 if (totalLength <= 0) { System.out.println("读取文件失败"); return; } //根据下载文件长度设置进度条的最大值 progressBar.setMax(totalLength); //计算单个线程下载长度 partSize = (totalLength % threadNum == 0) ? totalLength / threadNum : totalLength / threadNum + 1; DownloadSingleThread[] threads = new DownloadSingleThread[threadNum]; File file = new File(savePath); for (int i = 0; i < threads.length; i++) {//创建并开启单个线程 DownloadSingleThread thread = new DownloadSingleThread(downloadURL, file, partSize, i); threads[i] = thread; threads[i].start(); } boolean isfinished = false; int totalDownloadSize = 0;//5个线程当前下载总量 while (!isfinished) { isfinished = true; totalDownloadSize = 0; for (int i = 0; i < threads.length; i++) { if (!threads[i].isCompleted())//只要有一个线程没有完成,就没有完成下载 isfinished = false; totalDownloadSize += threads[i].getCurrentLength();//累加 System.out.println("totalDownloadSize----------" + totalDownloadSize); } //更新视图 Message msg = handler.obtainMessage(0x11);//发送当前下载总量去更新视图 msg.getData().putInt("size", totalDownloadSize); msg.sendToTarget(); sleep(1000);//每隔1秒更新一次 } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
[2]方法1的代码是我参考方法2写的,自己实现。最后调很多遍不知为什么显示下载结束的Toast始终不消失,我以为是setRequestProperty()方法造成的下载没有结束引起,结果发现不是。后来终于发现是因为始终没有跳出while(!isfinished)循环,所以一直在更新handler,所以一直显示Toast,这个小问题搞了很久,逻辑上的,后来用了一点小技巧搞定。
demo如下:
package com.study.daniel.runnabletest;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.os.Message;import android.support.v7.app.ActionBarActivity;import android.view.View;import android.widget.ProgressBar;import android.widget.TextView;import android.widget.Toast;import java.io.BufferedInputStream;import java.io.File;import java.io.IOException;import java.io.RandomAccessFile;import java.net.MalformedURLException;import java.net.URL;import java.net.URLConnection;public class MainActivity extends ActionBarActivity implements View.OnClickListener{ /**本地保存目录*/ private String path= Environment.getExternalStorageDirectory() + "/RunnableDownload/"; private File directory; /**下载连接URL*/ private String downloadURL="http://gdown.baidu.com/data/wisegame/91319a5a1dfae322/baidu_16785426.apk"; private String fileName="baidu_16785426.apk"; /**下载文件总大小*/ private int fileSize; /**输入流读取连接*/ private BufferedInputStream bis; /**随机流保存文件*/ private RandomAccessFile raf; /**每次读入保存的字节数组*/ private byte[] buffer; /**下载文件*/ private File file; /**线程数组*/ private Thread[] threads; /**自定义线程类完成下载前的准备工作*/ PrepareThread prepareThread; /**记录已下载文件距离文件开头的位置,初始为0*/ private static int position=0; private boolean isfinished=false; private boolean runIsFinished=false; /**控件*/ private TextView tv_downloaded; private ProgressBar progressBar; Handler handler=new Handler(){ public void handleMessage(Message msg){ progressBar.setProgress(msg.getData().getInt("size")); float temp=(float)progressBar.getProgress() / (float)progressBar.getMax(); tv_downloaded.setText((int)(temp*100)+"%"); if(progressBar.getMax()==progressBar.getProgress()) {//下载完成 Toast.makeText(MainActivity.this, "下载完成", Toast.LENGTH_SHORT).show(); } } }; private Runnable runnable=new Runnable() { @Override public void run() { try { URL url=new URL(downloadURL); URLConnection connection=url.openConnection(); connection.setRequestProperty("Range","bytes="+0+"-"+fileSize); //输入流 bis=new BufferedInputStream(connection.getInputStream()); buffer=new byte[1024]; //写入流 raf=new RandomAccessFile(file,"rw"); raf.seek(0); int len; while((len=bis.read(buffer,0,1024))!=-1){ raf.write(buffer,0,len); position+=len;//为了更新进度条 } runIsFinished=true; } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }; public void onClick(View view){ if(view.getId()==R.id.bt_download) { //下载前的准备,文件及连接 prepareThread=new PrepareThread(); prepareThread.start(); } } protected void onCreate(Bundle savedInstance){ super.onCreate(savedInstance); setContentView(R.layout.activity_main); tv_downloaded=(TextView)findViewById(R.id.tv_downloaded); progressBar=(ProgressBar)findViewById(R.id.progressbar); findViewById(R.id.bt_download).setOnClickListener(this); } @Override protected void onStart() { super.onStart(); } @Override protected void onResume() { super.onResume(); } @Override protected void onDestroy() {//程序销毁时候销毁线程 super.onDestroy(); } /** * 自定义线程类,做初始化工作 */ private class PrepareThread extends Thread{ int temp=0; public void run(){ //设置本地下载文件保存目录 directory=new File(path); if(!directory.exists()){//如果目录不存在则创建 directory.mkdir(); } file=new File(path+fileName);//将下载文件保存为file //连接 try { URL url=new URL(downloadURL); URLConnection connection=url.openConnection(); fileSize=connection.getContentLength(); progressBar.setMax(fileSize); /**在这个线程里开启多线程,解决了执行先后的问题*/ threads=new Thread[3];//3个线程下载 for(int i=0;i<threads.length;i++){ Thread thread=new Thread(runnable);//所有线程都执行同一个任务 threads[i]=thread; thread.start(); } while(!isfinished){//更新视图 Message message=handler.obtainMessage(0x11); message.getData().putInt("size",position); message.sendToTarget(); sleep(1000); isfinished=false; if(temp==2 && runIsFinished){ isfinished=true; } if(runIsFinished ){ temp++; } } System.out.println("----------------------out of while(!isfinished)"); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } finally { /**既然使用runnable,那么所有线程使用的都是一个流,那就不妨在这个线程里面, * 等所有线程都结束再关闭流,就不存在问题了*/ if(bis!=null) try { bis.close(); } catch (IOException e) { e.printStackTrace(); } if(raf!=null) try { raf.close(); } catch (IOException e) { e.printStackTrace(); } } } }}
还遇到流的关闭的顺序问题,我原先把流的关闭放在Runnable的最后,但是造成流关闭多次的异常。我还没太搞清楚为什么。但是我将操作放到了那个准备线程中,就解决了。
运行结果如下,测试没有问题:
我将两个demo工程上传到csdn,大家下载参考吧!我没有实现的是断点续传。实现之后更新博客,传代码。
以下是下载地址:
Thread方法:http://download.csdn.net/detail/u012320459/8957715
Runnable方法:http://download.csdn.net/detail/u012320459/8957709
- Android中实现多线程下载的两种方式示例及浅析之一(无断点续传)
- Android中实现多线程下载的两种方式示例及浅析之二(Thread加断点续传)
- Android多线程断点续传下载的实现
- Android多线程断点续传下载的实现
- Android多线程下载及断点续传
- 以多线程、断点续传方式下载文件的实现
- 主题:以多线程、断点续传方式下载文件的实现
- 在android中实现多线程下载和断点续传
- Java实现多线程下载及断点续传
- Android 中实现多线程下载和断点续传的原理和代码
- android--http协议多线程断点续传下载的实现
- android--http协议多线程断点续传下载的实现
- 多线程断点续传及下载
- Android实现网络多线程断点续传下载(二)
- Android实现网络多线程断点续传下载
- Android实现网络多线程断点续传下载
- Android实现网络多线程断点续传下载
- Android实现网络多线程断点续传下载
- 财务审批流程都有哪些步骤呢?
- UPnP
- C# 控件开发常用Attribute总结
- android launcher 资料
- hdoj 2041 超级楼梯
- Android中实现多线程下载的两种方式示例及浅析之一(无断点续传)
- 站立会议旁听体验
- 数据结构 树
- 数据结构实验之求二叉树后序遍历和层次遍历
- C++、 C 中的结构体、联合和枚举 异同
- hdu 1312 Red and Black
- 再谈const
- poj3071--Football(概率计算)
- C 语言中的好基友 数组和指针之一