前言:你学过的每一样东西,你遭受的每一次苦难,都会在你一生中的某个时候派上用场。—— 菲茨杰拉德
转载请标明出处:http://blog.csdn.net/android_for_james/article/details/51476311
源码我放到了文章的末尾
先来看看软件运行效果:
一、我们先来谈谈什么是多线程下载
下载文件这个活动,通常我们会理解为像是我们在写作文,拿着笔从头写到结尾,当然这可以抽象成单线程下载的情况,而当这篇文章可以好多个人完成的时候,那我们可以分配这些人一人写一段,最终也写成一篇文章,当作文字数一定时,不难想象当然是多个人一起写更快一些。
因此我们可以知道多线程下载的原理就是:通常服务器同时与多个用户连接时,每个用户之间共享带宽。如果N个用户优先级相同那么每个用户连接到服务器上的实际带宽就是1/N,但是当我们启用多线程时我们可以得到的带宽就可以是x/N(x=1,2,3.....),当然这时下载的速度也就越快。
二、实现结构
1.多线程管理类(MultiThreadManager),它负责分配每个线程该从哪里下载,以及得到下载进度。
2.下载线程类(DownThreads),它负责实际的下载操作。
3.图形界面显示类(ViewInit),它负责显示图像界面。
4.监听器(OnClickListener),它负责监听按钮事件。
三、核心内容
实现多线程下载的基础核心是在写入文件时,可以从任意位置写起,即我们使用RandomAccessFile来配合多线程来实现任意位置读写文件,我们可以这样理解:
这是普通流来写入文件时从头开始写入的过程
这是使用RandomAccessFile任意位置写入文件的过程:
这样也就可以配合多线程从文件的多个位置下载并写入文件。
四、具体实现:
public class MultiThreadManager { private String urlPath; private String filePath; private int threadNum; private int fileSize; private DownThreads[] threads; public MultiThreadManager(String urlPath,String filePath,int threadNum) { this.urlPath=urlPath; this.filePath=filePath; this.threadNum=threadNum; threads=new DownThreads[threadNum]; } public void downLoad() throws Exception { URL url=new URL(urlPath); HttpURLConnection connection=(HttpURLConnection)url.openConnection(); connection.setRequestMethod("GET"); connection.setRequestProperty("Connection", "Keep-Alive"); fileSize=connection.getContentLength(); connection.disconnect(); //分配每个线程需要在下载的文件块大小 int currentPartSize=fileSize/threadNum+1; //要用RandomAccessFile来写入文件,因为它支持随机访问模式,即程序可以直接跳转至文件的任意位置来读写 RandomAccessFile raFile=new RandomAccessFile(filePath,"rw"); //记录要写入文件的大小 raFile.setLength(fileSize); raFile.close(); for(int i=0;i<threadNum;i++) { int startPosition=i*currentPartSize; RandomAccessFile currentPartFile=new RandomAccessFile(filePath,"rw"); //将文件记录指针跳转至我们需要分割的位置 currentPartFile.seek(startPosition); threads[i]=new DownThreads(startPosition,currentPartSize,currentPartFile,urlPath); threads[i].start(); } } //得到当前下载完成百分比,用于绘制进度条 public int getCompleteRate() { int sumSize=0; for(int i=0;i<threadNum;i++) { sumSize+=threads[i].hasDownSize; } return (int) ((sumSize*1.0/fileSize)*100); } }
public class DownThreads extends Thread{ //下载线程 public int hasDownSize; private int startPosition; private int currentPartSize; private RandomAccessFile currentPartFile; private String urlPath; public DownThreads(int startPosition,int currentPartSize,RandomAccessFile currentPartFile,String urlPath) { this.startPosition=startPosition; this.currentPartSize=currentPartSize; this.currentPartFile=currentPartFile; this.urlPath=urlPath; } public void run() { try { URL url=new URL(urlPath); HttpURLConnection connection=(HttpURLConnection)url.openConnection(); connection.setRequestMethod("GET"); InputStream in=connection.getInputStream(); in.skip(this.startPosition); //设置缓冲区大小为4是为了提高运行速度 byte[] buffer=new byte[4]; int hasRead; while(hasDownSize<currentPartSize&&((hasRead=in.read(buffer))!=-1)) { //写入文件操作 currentPartFile.write(buffer, 0, hasRead); hasDownSize+=hasRead; } currentPartFile.close(); in.close(); } catch (IOException e) { e.printStackTrace(); } } }3、 ViewInit
public class ViewInit extends JFrame{ JTextField urlInfoText; JTextField filePathText; JTextField threadNumInfoText; JButton button; MyCommandListener listener; public ViewInit(){ init(); setVisible(true); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public void init() { setLayout(new FlowLayout()); setBounds(300,300,700,150); urlInfoText=new JTextField(50); filePathText=new JTextField(50); threadNumInfoText=new JTextField(10); button=new JButton("确定"); add(new JLabel("网址:")); add(urlInfoText); add(new JLabel("储存路径:")); add(filePathText); add(new JLabel("想要启动线程数:")); add(threadNumInfoText); add(button); } public void setListener(MyCommandListener listener) { this.listener=listener; listener.setFilePath(filePathText); listener.setUrlInfo(urlInfoText); listener.setThreadNum(threadNumInfoText); button.addActionListener(listener); } }
public interface MyCommandListener extends ActionListener{ //设置资源路径,文件保存路径,线程数量 public void setUrlInfo(JTextField urlText); public void setFilePath(JTextField fileText); public void setThreadNum(JTextField threadNum); }5、 OnClickListener
public class OnClickListener implements MyCommandListener{ String urlPath=null; String filePath=null; int threadNum; JTextField urlText; JTextField filePathText; JTextField threadNumInfo; public void setUrlInfo(JTextField urlText) { this.urlText=urlText; } public void setFilePath(JTextField fileText) { this.filePathText=fileText; } public void setThreadNum(JTextField threadNum) { this.threadNumInfo=threadNum; } @Override public void actionPerformed(ActionEvent e) { urlPath=urlText.getText(); filePath=filePathText.getText(); //获取所需线程数 threadNum=Integer.parseInt(threadNumInfo.getText()); //开启多线程下载 MultiThreadManager mtDown=new MultiThreadManager(urlPath,filePath,threadNum); try { mtDown.downLoad(); } catch (Exception ex) { ex.printStackTrace(); } //创建进度对话框 final ProgressMonitor dialog=new ProgressMonitor(null,"等待任务完成","已完成:",0,100); //在新线程中处理耗时任务 new Thread(()-> { //绘制进度条 while(mtDown.getCompleteRate()<101) { dialog.setProgress(mtDown.getCompleteRate()); if(dialog.isCanceled()) { break; } try{ Thread.sleep(300); }catch(Exception ex) { ex.printStackTrace(); } } }).start(); } }
转载请标明出处:http://blog.csdn.net/android_for_james/article/details/51476311
源码下载地址:http://download.csdn.net/detail/android_for_james/9527888
如果对你有帮助,那就顶一下~~~
如果你喜欢我的文章欢迎关注我的博客:http://blog.csdn.net/android_for_james