首先要说的是。。。这代码绝对的有bug。。。只要处理稍微大点的文件,就会出错。。。
不过还是写出来吧。。。
1.两个文本框,一个用来输入下载的网络地址,一个用来输入保存的文件路径,一个下载按钮,一个进度条查看进度
2.主文件:
获取下载的网络地址和存放的文件路径还有线程数,调用DownUtil类的方法来实现下载操作。创建一个Timer类,每秒获取一次下载进度,并发消息给Handler,更新进度条。
3.DownUtil文件:
download方法负责按照下载地址来建立网络连接,得到文件的大小,按照程序设定的线程数,将文件分成几个块,每个块使用一个单独的线程下载。这里用到了RandomAccessFile类,这是一个可以再文件中随意设置读写位置的类,使用seek函数实现随意设置读写位置。计算每个块(即每个线程)的起始下载位置,块大小,每个块使用一个RandomAccessFile。启动下载线程,将这三个参数传递给线程。
DownloadThread线程:建立网络连接,找到要下载的文件后,根据传入的起始下载位置参数,跳转到自己这块要下载的位置(使用skip函数)开始下载。期间随时检查已下载的大小和文件是否已经到末尾。
但这里其实是有问题的,比如,我设定了1024byte为单位下载,如果块的大小不是1024的整数倍,而且下载的块不是最后一块,那么最后那个1024分组必然也会是满的,因为在最后那一轮循环时,没有读到文件尾部,也没有使得已下载的大小大于本块的大小。
byte[] buffer = new byte[1024];
这里用到了read函数,这个函数的返回值如下:
- 读取成功,读完buffer要求的字数后还未到达文件结尾,则返回值等于buffer要求的字节数
- 读取成功,读取的文件的长度小于buffer要求的字节数,返回值为文件实际的长度
- 读取时,已到达文件末尾,返回0
- 读取出错,返回-1
下面是错误的一些数据和图片,分析什么的。。。
文件大小为491052字节
两个线程:起始位置分别为0,245527,每块240个hasRead,最后那个790byte,下载的图片正确
| | | | | |
| | | | | |
| | | | | |
| 245528,368292 | | | | |
| 294633,392844 | | | | |
| 245529,327372, 409215 | | | | |
只有那么一阵子,模拟器无论分成几个线程,都能得出正确结果,甚至试了8线程。
模拟器,随机错误,图片拒不重复,MP3是从头开始的某部分一直不断重复,大致大小是1M,以1M左右大小,循环。
模拟器实现了多个线程并发执行,问题可能再分块上,经过System。out输出,分块的起始位置又没有问题。。。下载下来的文件有时比实际大小要大,有时又小。。。前几个线程,每个hasRead都是1024,这可能是因为直接对文件进行分块读取,只设置了读取开始位置,而没有设置读取终止位置,从而覆盖了正确的读取结果。但事实上,错误的图片显示出局部的重复性,就像是线程3的位置上却又是线程2的内容,就造成了图片局部重复。但不止这样,还有些位置会出现一片灰色。错误完全是随机的。有时,执行多次能得出不同的结果。。。并且这些错误都出现在下载大一些的文件中。其实也不大,才几百k。最后一个线程的最后一个hasRead应该小于1024,因为读到了文件末尾了,但是有时候会小于,有时候又等于。。。
也可能是几个线程共用了buffer???没有懂。。。
真机测试,下载结果一切正常,但是过程非常慢,看System。out可以发现,线程并没有并行,而是第一个线程的内容完全下载完成后,第二个线程开始工作。而且,不像虚拟机中一直显示1024(除了虚拟器的正确结果时,最后一个线程的最后一分组,那组理应小于1024),真机下载是,会循环出现1024和几个固定的,小于1024的数值。这可能是真机强制执行的操作。。。
真是,费流量啊这个测试。。。十几兆没了。。。
综上,这代码弄了一天,也没有明白它为什么会这样,但好在,至少知其然了,虽然还是不知其所以然。总比连这代码会出bug都不知道好,只能这么安慰自己了。
看来,问题肯定是出在并行操作上了,应该是共用了什么内存,缓冲区什么的吧。。。纠结。。。
以下图片分别为,2线程,3线程,4线程,5线程,6线程,8线程,和莫名正常了一阵子的n线程