Android/java http多线程断点下载(附源码)

先看下项目结构:

http多线程断点下载涉及到 数据库,多线程和http请求等几个模块,东西不是很多,想弄清楚也不是很困难,接下来我和大家分享下我的做法。

一、先看MainActivity.java

成员变量,主要是一些下载过程的变量和handler

private String path = ":8080/wanmei/yama.apk";private String sdcardPath;private int threadNum = 5;ProgressDialog dialog;// 下载的进度private int process;// 下载完成的百分比private int done;private int filelength;// 本次下载开始之前,已经完成的下载量private int completed;// 用线程池是为了能够优雅的中断线程下载ExecutorService pool;@SuppressLint("HandlerLeak")private Handler handler = new Handler() {public void handleMessage(android.os.Message msg) {process += msg.arg1;done = (int) ((1.0 * process / filelength) * 100);Log.i("process", "process" + done);dialog.setProgress(done);// 第一次没有显示dialog的时候显示dialogif (done == 100) {// 提示用户下载完成// 线程下载完成以后就删除在数据库的缓存数据DBService.getInstance(getApplicationContext()).delete(path);// 做一个延时的效果,可以让用户多看一会100%Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {dialog.dismiss();}}, 1000);}};};

download方法触发下载事件,先检查有没有sd卡,然后才开始开线程下载

public void download(View v) {completed = 0;process = 0;done = 0;pool = Executors.newFixedThreadPool(threadNum);initProgressDialog();new Thread() {public void run() {try {if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {sdcardPath = Environment.getExternalStorageDirectory().getAbsolutePath();} else {toast("没有内存卡");return;}download(path, threadNum);} catch (Exception e) {e.printStackTrace();}};}.start();}

在真正开始下载之前,我们得先做一次http请求,为的是获取下载文件的大小和文件名,好预先准备好本地文件的大小以及各个线程应该下载的区域。这个时候我们请求的信息在响应头里面都有,只需要请求head就行了,既缩短了响应时间,也能节省流量

public void download(String path, int threadsize) throws Exception {long startTime = System.currentTimeMillis();URL url = new URL(path);// HttpHead head = new HttpHead(path);HttpURLConnection conn = (HttpURLConnection) url.openConnection();// 这里只需要获取httphead,至请求头文件,不需要body,// 不仅能缩短响应时间,也能节省流量// conn.setRequestMethod("GET");conn.setRequestMethod("HEAD");conn.setConnectTimeout(5 * 1000);Map<String, List<String>> headerMap = conn.getHeaderFields();Iterator<String> iterator = headerMap.keySet().iterator();while (iterator.hasNext()) {String key = iterator.next();List<String> values = headerMap.get(key);System.out.println(key + ":" + values.toString());}filelength = conn.getContentLength();// 获取要下载的文件的长度long endTime = System.currentTimeMillis();Log.i("spend", "spend time = " + (endTime – startTime));String filename = getFilename(path);// 从路径中获取文件名称File File = new File(sdcardPath + "/download/");if (!File.exists()) {File.mkdirs();}File saveFile = new File(sdcardPath + "/download/" + filename);RandomAccessFile accessFile = new RandomAccessFile(saveFile, "rwd");accessFile.setLength(filelength);// 设置本地文件的长度和下载文件相同accessFile.close();// 计算每条线程下载的数据长度<strong>int block = filelength % threadsize == 0 ? filelength / threadsize: filelength / threadsize + 1;</strong>// 判断是不是第一次下载,不是就计算已经下载了多少if (!DBService.getInstance(getApplicationContext()).isHasInfors(path)) {for (int threadid = 0; threadid < threadNum; threadid++) {completed += DBService.getInstance(getApplicationContext()).getInfoByIdAndUrl(threadid, path);}} Message msg = handler.obtainMessage(); msg.arg1 = completed;handler.sendMessage(msg);for (int threadid = 0; threadid < threadsize; threadid++) {pool.execute(new DownloadThread(getApplicationContext(), path,saveFile, block, threadid, threadNum).setOnDownloadListener(this));}}DownloadThread.java

有两点:1、谷歌推荐httpurlconnection,我试了下下载速度确实比httpclient快

2、下载的时候用来缓存的byte数组,他的长度影响到下载速度的快慢

@Overridepublic void run() {Log.i("download", "线程id:" + threadid + "开始下载");// 计算开始位置公式:线程id*每条线程下载的数据长度+已下载完成的(断点续传)= ?// 计算结束位置公式:(线程id +1)*每条线程下载的数据长度-1 =?completed = DBService.getInstance(context).getInfoByIdAndUrl(threadid, url);int startposition = threadid * block+completed;int endposition = (threadid + 1) * block – 1;try {RandomAccessFile accessFile = new RandomAccessFile(saveFile, "rwd");accessFile.seek(startposition);// 设置从什么位置开始写入数据// 我测试的时候,用httpurlconnection下载速度比httpclient快了10倍不止HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();conn.setRequestMethod("GET");conn.setConnectTimeout(5 * 1000);conn.setRequestProperty("Accept-Language", "zh-CN");conn.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, */*");conn.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)");conn.setRequestProperty("Referer", url);conn.setRequestProperty("Connection", "Keep-Alive");conn.setRequestProperty("RANGE", "bytes=" + startposition + "-"+ endposition);// 设置获取实体数据的范围// HttpClient httpClient = new DefaultHttpClient();// HttpGet httpGet = new HttpGet(url);// httpGet.addHeader("Range",// "bytes="+startposition+"-"+endposition);// HttpResponse response = httpClient.execute(httpGet);InputStream inStream = conn.getInputStream();// 这里需要注意,数组的长度其实代表了每次下载的流的大小// 如果太小的话,例如1024,每次就都只会下载1024byte的内容,速度太慢了,// 对于下载十几兆的文件来说太难熬了,太小了相当于限速了// 但也不能太大,如果太大了,那么缓冲区中的数据会过大,从而造成oom// 为了不oom又能开最大的速度,这里可以获取应用可用内容,动态分配int freeMemory = ((int) Runtime.getRuntime().freeMemory());// 获取应用剩余可用内存byte[] buffer = new byte[freeMemory / threadNum];// 可用内存得平分给几个线程// byte[] buffer = new byte[1024];int len = 0;int total = 0;boolean isInterrupted=false;while ((len = inStream.read(buffer)) != -1) {accessFile.write(buffer, 0, len);total += len;Log.i("download", "线程id:" + threadid + "已下载" + total + "总共有" + block);// 实时更新进度listener.onDownload(threadid,len,total,url);//当线程被暗示需要中断以后,退出循环,终止下载操作<strong>if(Thread.interrupted()){isInterrupted=true;break;}</strong>}inStream.close();accessFile.close();if(isInterrupted){Log.i("download", "线程id:" + threadid + "下载停止");}else{Log.i("download", "线程id:" + threadid + "下载完成");}} catch (Exception e) {e.printStackTrace();}}我是在应用退到后台,就让停止下载的,不为什么,就是不想多写那个button,需要的可以自己写。我就想是一只草原中被牧童遗忘的羊,

Android/java http多线程断点下载(附源码)

相关文章:

你感兴趣的文章:

标签云: