首先上效果图
<1>关于图中数字进度条
GitHub https://github.com/daimajia/NumberProgressBar
下载下来之后, 解压打开文件夹
将NumberProgressBar copy到自己的项目中, 包名可以自己随便取
然后会发现很多res错误. 将下载下来的res资源(如下图) 添加到自己的项目中即可消除
这样我们就可以在布局文件中按照 自己的包路径 使用NumberProgressBar了
<2> 关于多线程下载
大致思路:先获取要下载文件的大小, 在本地新建相同大小的文件.
再考虑使用的下载线程数量, 每条线程下载一部分文件("Rang","bytes= " + 起始位置 + “-” + 终止位置)
通过RandomAccsess来对文件在相应的位置写入下载数据
具体需要考虑的是各个线程下载的字节不能有重叠的, 但必须连贯和源文件一致, 才不会出现文件错误
观察线程, <1>logcat打印观察 <2>Eclips的DDMS观察
<1>LogCat
可以看到文件总大小 7686916 字节. 使用6条线程下载 blocksize=7686916/6= 1281152.6666666666666666666666667
当blocksize不是整数时, 向上取整, 因为要保证源文件数据全部被保存下来
Thread_Num 0 从 0字节 到 1281153字节
Thread_Num 1 从 1281154字节 到 2562307字节 ....
每个Thread之间没有重合, 并且保证将原文件数据全部覆盖
<2>DDMS
可以看到Thread的状态. 是否还在native, 没有被销毁.
通过观察发现, Thread执行完Run函数之后就会自动被销毁!
<3> 草稿代码
之所以是草稿代码, 是因为并不是很完善.
下载完文件(mp3,apk,mp4,jpg)一切是正常. apk可安装, MP3无卡顿播放...
但是ProgressBar的数字显示可能不正确. 因为setMax(int). int范围是 -2^31~2^31-1
把文件大小设置为Max, 已经下载的设置为setProgress参数时. 2^31字节 = 2^31/1024/1024 = 2048M, 因此progress最大为2048M = 2G
ProgressBar计算进度时, 已经下载的*100/ Max = progress < 2048M
---> 已经下载的 < 20.48M
所以当我们下载数据超过20.48M时会出现进度条上出现负数.
但是, 这个显示数据不影响进度条的真正比例, 且不影响下载文件. 我也试图用两位小数再来乘以100表示比例, setMax=1来修改, 但是小数部分很难保存
因此这个部分还未解决.希望有人可以帮忙解决.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context="mirror.android.multithreaddownloader.MainActivity" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Download URL"/> <EditText android:id="@+id/ed_url" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="http://192.168.1.111:8080/MultiThreadDownloader/" /> <Button android:id="@+id/bt_download" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="DownLoad" /> <mirror.android.numberprogressbar.NumberProgressBar android:id="@+id/npb_rate" android:layout_width="wrap_content" android:layout_height="wrap_content" style="@style/NumberProgressBar_Default"/> </LinearLayout>
package mirror.android.multithreaddownloader; import mirror.android.numberprogressbar.NumberProgressBar; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.ProgressBar; public class MainActivity extends Activity { private Button bt_download; private EditText ed_url; private NumberProgressBar npb_rate; private Handler handler; private Message msg; private int FileSize; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bt_download = (Button)findViewById(R.id.bt_download); ed_url = (EditText)findViewById(R.id.ed_url); npb_rate = (NumberProgressBar)findViewById(R.id.npb_rate); bt_download.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { bt_download.setEnabled(false); ButtonThread bt = new ButtonThread(); bt.start(); } }); handler = new MyHandler(); } class ButtonThread extends Thread{ @Override public void run() { try { DownloadUtil downloadUtil = new DownloadUtil(ed_url.getText().toString(), 6); FileSize = downloadUtil.getFileSize(); downloadUtil.download(); int Size; while((Size = downloadUtil.getDownloadRate()) <= FileSize){ Message msg = handler.obtainMessage(); msg.obj = Size; handler.sendMessage(msg); Thread.sleep(500); if(Size == FileSize) break; } Log.d("BreakWhile","true"); } catch (Exception e) { e.printStackTrace(); } } } //ProgressBar update class MyHandler extends Handler{ @Override public void handleMessage(Message msg) { Integer rate = (Integer)msg.obj; if(rate == FileSize) bt_download.setEnabled(true); npb_rate.setMax(FileSize); npb_rate.setProgress(rate); } } }
package mirror.android.multithreaddownloader; import java.io.File; import java.io.InputStream; import java.io.RandomAccessFile; import java.math.BigDecimal; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.text.DecimalFormat; import android.os.Environment; import android.util.Log; public class DownloadUtil { private DownThread[] threads; private int fileSize; private File saveFile; private String path; private int threadNum; private int sumSize; public DownloadUtil(String path, int threadNum){ this.path = path; this.threadNum = threadNum; } public void download() throws Exception{ //create a randomacessflie String[] splitname = path.split("/"); String filename = splitname[splitname.length - 1]; //Log.d("Splitname",filename); saveFile = new File(Environment.getExternalStorageDirectory() + "/Download/" + filename); RandomAccessFile accessFile = new RandomAccessFile(saveFile, "rw"); accessFile.setLength(fileSize); accessFile.close(); //MultiThread download threads = new DownThread[threadNum]; int blockSize = ((fileSize%threadNum)==0)?(fileSize/threadNum):(fileSize/threadNum + 1); for (int i = 0; i < threadNum; i++) { int curretThreadStartPosition = i*(blockSize+1); threads[i] = new DownThread(curretThreadStartPosition, blockSize, accessFile); threads[i].setName("Thread_Num " + i); threads[i].start(); } } public int getFileSize(){ URL url; try { url = new URL(path); HttpURLConnection con = (HttpURLConnection)url.openConnection(); fileSize = con.getContentLength(); con.disconnect(); } catch (Exception e) { e.printStackTrace(); } Log.d("fileSize",String.valueOf(fileSize)); return fileSize; } public int getDownloadRate(){ sumSize = 0; for (int i = 0; i < threadNum; i++) { sumSize += threads[i].lengh; } //return ((sumSize*100)/fileSize); return sumSize; } private class DownThread extends Thread{ private int startPos; private int blockSize; private int lengh = 0; private RandomAccessFile accessFile; public DownThread(int startPos, int blockSize, RandomAccessFile accessFile){ this.startPos = startPos; this.blockSize = blockSize; this.accessFile = accessFile; } @Override public void run() { try { URL url = new URL(path); HttpURLConnection con = (HttpURLConnection)url.openConnection(); con.setConnectTimeout(5*1000); con.setRequestMethod("GET"); con.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*"); con.setRequestProperty("Accept-Language", "zh-CN"); con.setRequestProperty("Charset", "UTF-8"); con.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"); con.setRequestProperty("Connection", "Keep-Alive"); Log.d("ThreadInfo", this.getName() + " " + blockSize); Log.d("ThreadInfo", this.getName()+" startPosition "+ startPos + " endPosition "+ (startPos+blockSize)); //con.setRequestProperty("Range", "bytes=" + "起始位置" + "-" + "终止位置"); con.setRequestProperty("Range", "bytes=" + startPos + "-" + (startPos+blockSize)); byte[] buffer = new byte[1024]; accessFile = new RandomAccessFile(saveFile, "rwd"); accessFile.seek(startPos); InputStream in = con.getInputStream(); int hasRead = 0; while(lengh < blockSize && (hasRead = in.read(buffer))>0 ){ accessFile.write(buffer,0,hasRead); lengh += hasRead; } accessFile.close(); in.close(); Log.d(Thread.currentThread().getName().toString(),"complete"); } catch (Exception e) { e.printStackTrace(); } } } }
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>" <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <uses-permission android:name="android.permission.INTERNET"/>