Android进阶:实现多线程下载文件

简介:
 

多线程下载大概思路就是通过Range 属性实现文件分段,然后用RandomAccessFile 来读写文件,最终合并为一个文件

 

首先看下效果图

 

 

创建工程 ThreadDemo

 

首先布局文件 threaddemo.xml

 

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"  
  6.     >  
  7. <TextView    
  8.     android:layout_width="fill_parent"   
  9.     android:layout_height="wrap_content"   
  10.     android:text="下载地址"  
  11.     />  
  12. <TextView  
  13.     android:id="@+id/downloadurl"  
  14.     android:layout_width="fill_parent"   
  15.     android:layout_height="wrap_content"   
  16.     android:lines="5"  
  17.     />  
  18. <TextView    
  19.     android:layout_width="fill_parent"   
  20.     android:layout_height="wrap_content"   
  21.     android:text="线程数"  
  22.     />  
  23. <EditText  
  24.     android:id="@+id/downloadnum"  
  25.     android:layout_width="fill_parent"   
  26.     android:layout_height="wrap_content"   
  27.     />  
  28. <ProgressBar  
  29.     android:id="@+id/downloadProgressBar"  
  30.     android:layout_width="fill_parent"   
  31.     style="?android:attr/progressBarStyleHorizontal"  
  32.     android:layout_height="wrap_content"   
  33.     />  
  34. <TextView  
  35.     android:id="@+id/downloadinfo"  
  36.     android:layout_width="fill_parent"   
  37.     android:layout_height="wrap_content"   
  38.     android:text="下载进度 0"  
  39.     />  
  40. <Button  
  41.     android:id="@+id/downloadbutton"  
  42.     android:layout_width="wrap_content"   
  43.     android:layout_height="wrap_content"   
  44.     android:text="开始下载"  
  45.     />  
  46. </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="线程数" /><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="下载进度 0"/><Buttonandroid:id="@+id/downloadbutton"android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="开始下载"/></LinearLayout>

 

主界面 Acitivity

 

  1. public class ThreadDownloadDemo extends Activity {  
  2.    
  3.     private TextView downloadurl;  
  4.     private EditText downloadnum;  
  5.     private Button downloadbutton;  
  6.     private ProgressBar downloadProgressBar;  
  7.     private TextView downloadinfo;  
  8.     private int downloadedSize = 0;  
  9.     private int fileSize = 0;  
  10.       
  11.     private long downloadtime;  
  12.    
  13.     @Override  
  14.     public void onCreate(Bundle savedInstanceState) {  
  15.         super.onCreate(savedInstanceState);  
  16.         setContentView(R.layout.threaddemo);  
  17.    
  18.         downloadurl = (TextView) findViewById(R.id.downloadurl);  
  19.         downloadurl.setText("http://file16.top100.cn/201105110911/AA5CC27CBE34DEB50A194581D1300881/Special_323149/%E8%8D%B7%E5%A1%98%E6%9C%88%E8%89%B2.mp3");  
  20.         downloadnum = (EditText) findViewById(R.id.downloadnum);  
  21.         downloadinfo = (TextView) findViewById(R.id.downloadinfo);  
  22.         downloadbutton = (Button) findViewById(R.id.downloadbutton);  
  23.         downloadProgressBar = (ProgressBar) findViewById(R.id.downloadProgressBar);  
  24.         downloadProgressBar.setVisibility(View.VISIBLE);  
  25.         downloadProgressBar.setMax(100);  
  26.         downloadProgressBar.setProgress(0);  
  27.         downloadbutton.setOnClickListener(new OnClickListener() {  
  28.             public void onClick(View v) {  
  29.                 download();  
  30.                 downloadtime = SystemClock.currentThreadTimeMillis();  
  31.             }  
  32.         });  
  33.     }  
  34.    
  35.     private void download() {  
  36.         // 获取SD卡目录   
  37.         String dowloadDir = Environment.getExternalStorageDirectory()  
  38.                 + "/threaddemodownload/";  
  39.         File file = new File(dowloadDir);  
  40.         //创建下载目录   
  41.         if (!file.exists()) {  
  42.             file.mkdirs();  
  43.         }  
  44.           
  45.         //读取下载线程数,如果为空,则单线程下载   
  46.         int downloadTN = Integer.valueOf("".equals(downloadnum.getText()  
  47.                 .toString()) ? "1" : downloadnum.getText().toString());  
  48.         String fileName = "hetang.mp3";  
  49.         //开始下载前把下载按钮设置为不可用   
  50.         downloadbutton.setClickable(false);  
  51.         //进度条设为0   
  52.         downloadProgressBar.setProgress(0);  
  53.         //启动文件下载线程   
  54.         new downloadTask("http://file16.top100.cn/201105110911/AA5CC27CBE34DEB50A194581D1300881/Special_323149/%E8%8D%B7%E5%A1%98%E6%9C%88%E8%89%B2.mp3", Integer  
  55.                 .valueOf(downloadTN), dowloadDir + fileName).start();  
  56.     }  
  57.    
  58.     Handler handler = new Handler() {  
  59.         @Override  
  60.         public void handleMessage(Message msg) {  
  61.             //当收到更新视图消息时,计算已完成下载百分比,同时更新进度条信息   
  62.             int progress = (Double.valueOf((downloadedSize * 1.0 / fileSize * 100))).intValue();  
  63.             if (progress == 100) {  
  64.                 downloadbutton.setClickable(true);  
  65.                 downloadinfo.setText("下载完成!");  
  66.                 Dialog mdialog = new AlertDialog.Builder(ThreadDownloadDemo.this)  
  67.                     .setTitle("提示信息")  
  68.                     .setMessage("下载完成,总用时为:"+(SystemClock.currentThreadTimeMillis()-downloadtime)+"毫秒")  
  69.                     .setNegativeButton("确定"new DialogInterface.OnClickListener(){  
  70.                         @Override  
  71.                         public void onClick(DialogInterface dialog, int which) {  
  72.                             dialog.dismiss();  
  73.                         }  
  74.                     })  
  75.                     .create();  
  76.                 mdialog.show();  
  77.             } else {  
  78.                 downloadinfo.setText("当前进度:" + progress + "%");  
  79.             }  
  80.             downloadProgressBar.setProgress(progress);  
  81.         }  
  82.    
  83.     };  
  84.    
  85.       
  86.     public class downloadTask extends Thread {  
  87.         private int blockSize, downloadSizeMore;  
  88.         private int threadNum = 5;  
  89.         String urlStr, threadNo, fileName;  
  90.    
  91.         public downloadTask(String urlStr, int threadNum, String fileName) {  
  92.             this.urlStr = urlStr;  
  93.             this.threadNum = threadNum;  
  94.             this.fileName = fileName;  
  95.         }  
  96.    
  97.         @Override  
  98.         public void run() {  
  99.             FileDownloadThread[] fds = new FileDownloadThread[threadNum];  
  100.             try {  
  101.                 URL url = new URL(urlStr);  
  102.                 URLConnection conn = url.openConnection();  
  103.                 //防止返回-1   
  104.                 InputStream in = conn.getInputStream();  
  105.                 //获取下载文件的总大小   
  106.                 fileSize = conn.getContentLength();  
  107.                 Log.i("bb""======================fileSize:"+fileSize);  
  108.                 //计算每个线程要下载的数据量   
  109.                 blockSize = fileSize / threadNum;  
  110.                 // 解决整除后百分比计算误差   
  111.                 downloadSizeMore = (fileSize % threadNum);  
  112.                 File file = new File(fileName);  
  113.                 for (int i = 0; i < threadNum; i++) {  
  114.                     Log.i("bb""======================i:"+i);  
  115.                     //启动线程,分别下载自己需要下载的部分   
  116.                     FileDownloadThread fdt = new FileDownloadThread(url, file, i * blockSize, (i + 1) * blockSize - 1);  
  117.                     fdt.setName("Thread" + i);  
  118.                     fdt.start();  
  119.                     fds[i] = fdt;  
  120.                 }  
  121.                 boolean finished = false;  
  122.                 while (!finished) {  
  123.                     // 先把整除的余数搞定   
  124.                     downloadedSize = downloadSizeMore;  
  125.                     finished = true;  
  126.                     for (int i = 0; i < fds.length; i++) {  
  127.                         downloadedSize += fds[i].getDownloadSize();  
  128.                         if (!fds[i].isFinished()) {  
  129.                             finished = false;  
  130.                         }  
  131.                     }  
  132.                     handler.sendEmptyMessage(0);  
  133.                     //线程暂停一秒   
  134.                     sleep(1000);  
  135.                 }  
  136.             } catch (Exception e) {  
  137.                 e.printStackTrace();  
  138.             }  
  139.    
  140.         }  
  141.     }  
  142. }  
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);//创建下载目录if (!file.exists()) {file.mkdirs();}//读取下载线程数,如果为空,则单线程下载int downloadTN = Integer.valueOf("".equals(downloadnum.getText().toString()) ? "1" : downloadnum.getText().toString());String fileName = "hetang.mp3";//开始下载前把下载按钮设置为不可用downloadbutton.setClickable(false);//进度条设为0downloadProgressBar.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) {//当收到更新视图消息时,计算已完成下载百分比,同时更新进度条信息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("当前进度:" + 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();//防止返回-1InputStream in = conn.getInputStream();//获取下载文件的总大小fileSize = conn.getContentLength();Log.i("bb", "======================fileSize:"+fileSize);//计算每个线程要下载的数据量blockSize = fileSize / threadNum;// 解决整除后百分比计算误差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) {// 先把整除的余数搞定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();} }}}

 

这里启动线程将文件分割为几个部分,每一个部分再启动一个线程去下载数据

 

下载文件的线程

 

  1. public class FileDownloadThread extends Thread{  
  2.     private static final int BUFFER_SIZE=1024;  
  3.     private URL url;  
  4.     private File file;  
  5.     private int startPosition;  
  6.     private int endPosition;  
  7.     private int curPosition;  
  8.     //标识当前线程是否下载完成   
  9.     private boolean finished=false;  
  10.     private int downloadSize=0;  
  11.     public FileDownloadThread(URL url,File file,int startPosition,int endPosition){  
  12.         this.url=url;  
  13.         this.file=file;  
  14.         this.startPosition=startPosition;  
  15.         this.curPosition=startPosition;  
  16.         this.endPosition=endPosition;  
  17.     }  
  18.     @Override  
  19.     public void run() {  
  20.         BufferedInputStream bis = null;  
  21.         RandomAccessFile fos = null;                                                 
  22.         byte[] buf = new byte[BUFFER_SIZE];  
  23.         URLConnection con = null;  
  24.         try {  
  25.             con = url.openConnection();  
  26.             con.setAllowUserInteraction(true);  
  27.             //设置当前线程下载的起止点   
  28.             con.setRequestProperty("Range""bytes=" + startPosition + "-" + endPosition);  
  29.             Log.i("bb", Thread.currentThread().getName()+"  bytes=" + startPosition + "-" + endPosition);  
  30.             //使用java中的RandomAccessFile 对文件进行随机读写操作   
  31.             fos = new RandomAccessFile(file, "rw");  
  32.             //设置写文件的起始位置   
  33.             fos.seek(startPosition);  
  34.             bis = new BufferedInputStream(con.getInputStream());    
  35.             //开始循环以流的形式读写文件   
  36.             while (curPosition < endPosition) {  
  37.                 int len = bis.read(buf, 0, BUFFER_SIZE);                  
  38.                 if (len == -1) {  
  39.                     break;  
  40.                 }  
  41.                 fos.write(buf, 0, len);  
  42.                 curPosition = curPosition + len;  
  43.                 if (curPosition > endPosition) {  
  44.                     downloadSize+=len - (curPosition - endPosition) + 1;  
  45.                 } else {  
  46.                     downloadSize+=len;  
  47.                 }  
  48.             }  
  49.             //下载完成设为true   
  50.             this.finished = true;  
  51.             bis.close();  
  52.             fos.close();  
  53.         } catch (IOException e) {  
  54.             e.printStackTrace();  
  55.         }  
  56.     }  
  57.    
  58.     public boolean isFinished(){  
  59.         return finished;  
  60.     }  
  61.    
  62.     public int getDownloadSize() {  
  63.         return downloadSize;  
  64.     }  
  65. }  
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;//标识当前线程是否下载完成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); //设置当前线程下载的起止点 con.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition); Log.i("bb", Thread.currentThread().getName()+" bytes=" + startPosition + "-" + endPosition); //使用java中的RandomAccessFile 对文件进行随机读写操作 fos = new RandomAccessFile(file, "rw"); //设置写文件的起始位置 fos.seek(startPosition); bis = new BufferedInputStream(con.getInputStream()); //开始循环以流的形式读写文件 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; } } //下载完成设为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方法定位到相应的位置 并实时记录下载量

 

当然这里需要联网和访问SD卡 所以要加上相应的权限

 

<uses-permission android:name="android.permission.INTERNET" />  
  1.     <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了 下面可以看看断点续传的问题了。有待测试~~

相关文章
|
22天前
|
Java Android开发 C++
Android Studio JNI 使用模板:c/cpp源文件的集成编译,快速上手
本文提供了一个Android Studio中JNI使用的模板,包括创建C/C++源文件、编辑CMakeLists.txt、编写JNI接口代码、配置build.gradle以及编译生成.so库的详细步骤,以帮助开发者快速上手Android平台的JNI开发和编译过程。
68 1
|
7天前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
25 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
9天前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android应用开发中的多线程编程,涵盖基本概念、常见实现方式及最佳实践。主要内容包括主线程与工作线程的作用、多线程的多种实现方法(如 `Thread`、`HandlerThread`、`Executors` 和 Kotlin 协程),以及如何避免内存泄漏和合理使用线程池。通过有效的多线程管理,可以显著提升应用性能和用户体验。
29 10
|
7天前
|
API Android开发 iOS开发
安卓与iOS开发中的线程管理对比
【9月更文挑战第12天】在移动应用的世界中,安卓和iOS平台各自拥有庞大的用户群体。开发者们在这两个平台上构建应用时,线程管理是他们必须面对的关键挑战之一。本文将深入探讨两大平台在线程管理方面的异同,通过直观的代码示例,揭示它们各自的设计理念和实现方式,帮助读者更好地理解如何在安卓与iOS开发中高效地处理多线程任务。
|
9天前
|
Java Android开发 开发者
安卓应用开发中的线程管理优化技巧
【9月更文挑战第10天】在安卓开发的海洋里,线程管理犹如航行的风帆,掌握好它,能让应用乘风破浪,反之则可能遭遇性能的暗礁。本文将通过浅显易懂的语言和生动的比喻,带你探索如何优雅地处理安卓中的线程问题,从基础的线程创建到高级的线程池运用,让你的应用运行更加流畅。
|
14天前
|
安全 算法 Java
多线程写入同一个文件时,如何保证写入正常
【9月更文挑战第3天】多线程写入同一个文件时,如何保证写入正常
63 8
|
23天前
|
开发工具 git 索引
repo sync 更新源码 android-12.0.0_r34, fatal: 不能重置索引文件至版本 ‘v2.27^0‘。
本文描述了在更新AOSP 12源码时遇到的repo同步错误,并提供了通过手动git pull更新repo工具来解决这一问题的方法。
43 1
|
22天前
|
开发工具 uml git
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
本文分享了下载AOSP源码的方法,包括如何使用repo工具和处理常见的repo sync错误,以及配置Python环境以确保顺利同步特定版本的AOSP代码。
132 0
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
|
21天前
|
存储 监控 数据库
Android经典实战之OkDownload的文件分段下载及合成原理
本文介绍了 OkDownload,一个高效的 Android 下载引擎,支持多线程下载、断点续传等功能。文章详细描述了文件分段下载及合成原理,包括任务创建、断点续传、并行下载等步骤,并展示了如何通过多种机制保证下载的稳定性和完整性。
25 0
|
21天前
|
安全 网络安全 数据安全/隐私保护
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享安卓与iOS开发中的线程管理比较
【8月更文挑战第30天】本文将探讨网络安全与信息安全的重要性,并分享关于网络安全漏洞、加密技术和安全意识的知识。我们将了解常见的网络攻击类型和防御策略,以及如何通过加密技术和提高安全意识来保护个人和组织的信息安全。