Android 下载线程的编写
2015-11-25 08:53
591 查看
在我们的开发中,碰到文件下载的情况已经是很多见了吧?平时在开发大项目时,我们可能会用一些开源的库,比如afinal、Xutils等等,不过对于一些菜鸟的学习来说,自己去写一个下载的工具类,这对于自身的知识巩固那还是有很多好处的。好了废话不多说,现在我们直接来写一个文件下载工具类(不支持断点续传)。
下面我们来分析一下怎么去写。
首先,我们得去发送http请求去加载我们的inputStream吧,其次我们还得去利用BufferedInputStream、BufferedOutputStream等去写文件吧,还有md5校验吧。好了这里边还有很多细节问题,我们来一一查看,直接上代码。
根据上面的代码块我们来分析,HttpURLConnection去拿我们想要的数据,包括http头和inputStream,在http头里边有我们下载文件的长度,这个长度可以用来检测下载成功与否和下载进度的显示。这个方法我们返回的是一个inputStream。
在线程run方法里边有一个while循环,在循环里去获取inputStream,因为有可能我们获取到的inputStream为null,所以我们循环获取,最多获取三次。
下载完成之后会有一个md5码的校验,直接来代码。若返回为null校验失败,否则下载成功。
好了,下载的线程算是完事了,我们的额超时线程还没完呢,等等,别着急马上。它主要是干啥呢,就是检测下载线程的运行状态,来吧那就。
这是它的一个run方法,可以看到首先,做一个延时,等待下载线程的执行,因为我们的超时线程和下载线程必须同时执行!!随之来的就是检测,检测下载线程的isRunning,检测文件读写是是否卡顿,如果在12s之内下载一直是卡顿的,我们就直接将下载线程停止掉(当然,这个停止掉指的是我们不再使用它,并不能直接杀死他),这个12秒我们也可以自己在超时线程里边控制。如果 listener.statusTimeoutListener(process,name);则表示线程下载超时,停止下载。
使用方法:
当然咯,下面就是下载链接了 点击打开链接 下载。
下面我们来分析一下怎么去写。
首先,我们得去发送http请求去加载我们的inputStream吧,其次我们还得去利用BufferedInputStream、BufferedOutputStream等去写文件吧,还有md5校验吧。好了这里边还有很多细节问题,我们来一一查看,直接上代码。
// 获取文件的inputStream private InputStream getInputStream(String path)throws Exception { listener.statusListener(0,this.getFileName()); URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(10 * 1000); conn.setRequestMethod("GET"); if(length <= 0){ try { Map<String, List<String>> map = conn.getHeaderFields(); for(int i=0;i<map.size();i++){ if(map.containsKey("Content-Length")){ String temp = map.get("Content-Length").toString().replaceAll("\\]", "").replaceAll("\\[", ""); length = Long.parseLong(temp); break; } } } catch (Exception e) { length = 0; } } if(length <= 0){ listener.statusListener(-1,this.getFileName()); return null; } if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { return conn.getInputStream(); } return null; }
根据上面的代码块我们来分析,HttpURLConnection去拿我们想要的数据,包括http头和inputStream,在http头里边有我们下载文件的长度,这个长度可以用来检测下载成功与否和下载进度的显示。这个方法我们返回的是一个inputStream。
在线程run方法里边有一个while循环,在循环里去获取inputStream,因为有可能我们获取到的inputStream为null,所以我们循环获取,最多获取三次。
while (isRunning && errorCount < 3) { threadStartRun = true; if(errorCount >= 2){ listener.statusListener( -1,this.getFileName()); break; } try { inputStream = getInputStream(src); if(inputStream == null){ errorCount++; Thread.sleep(3000); continue; } } catch (Exception e) { e.printStackTrace(); errorCount++; continue; } if(inputStream !=null){ break; } }拿到inputStream之后当然是去读写文件到本地.
/** * 将InputStream保存为本地文件 * */ private String saveFile(InputStream is) throws IOException { threadStartWrite = true; File myCaptureFile = new File(this.getFileName()); if (myCaptureFile.exists()) { myCaptureFile.delete(); } myCaptureFile.createNewFile(); FileOutputStream out = null; try { out = new FileOutputStream(myCaptureFile); BufferedInputStream bufis = new BufferedInputStream(is); BufferedOutputStream bufos = new BufferedOutputStream(out); byte[] b = new byte[1024*16]; int len; long count = 0; float process = 0; while (isRunning && (len = bufis.read(b)) != -1) { bufos.write(b, 0, len); count += len; process =(float)count/length*100; if(!isRunning){ break; } getListener().statusListener((int)process,myCaptureFile.getAbsolutePath()); downprocess = (int)process; N = (int) count; } out.flush(); bufis.close(); bufos.close(); if(!isRunning){ return null; } String tempMd5 = getFileMD5(myCaptureFile); if(!(getMd5().equals(tempMd5))){ N = -1; downprocess = -1; return null; } return myCaptureFile.getAbsolutePath(); } catch (Exception e) { getListener().statusListener(-1,myCaptureFile.getAbsolutePath()); N = -1; downprocess = -1; return null; } }从上面代码块我们可以看到,获取到inputStream之后利用BufferedInputStream等去写文件,当然还有一个重要方法就是getListener().statusListener((int)process, myCaptureFile. getAbsolutePath());通知调用者当前下载的进度。
下载完成之后会有一个md5码的校验,直接来代码。若返回为null校验失败,否则下载成功。
private String getFileMD5(File file) { if (!file.isFile()) { return null; } MessageDigest digest = null; FileInputStream in = null; byte buffer[] = new byte[1024]; int len; try { digest = MessageDigest.getInstance("MD5"); in = new FileInputStream(file); while ((len = in.read(buffer, 0, 1024)) != -1) { digest.update(buffer, 0, len); } in.close(); } catch (Exception e) { e.printStackTrace(); return null; } BigInteger bigInt = new BigInteger(1, digest.digest()); String strMd5 = bigInt.toString(16); if (strMd5.length() < 32) { for (int i = 0; i < 32 - strMd5.length(); i++) { strMd5 = "0" + strMd5; } } return strMd5; }当然下载完成之后我们还得通知调用者下载完成吧。
getListener().statusListener(100,myCaptureFile.getAbsolutePath());
好了,下载的线程算是完事了,我们的额超时线程还没完呢,等等,别着急马上。它主要是干啥呢,就是检测下载线程的运行状态,来吧那就。
@Override public void run() { super.run(); name = downloadTask.getFileName(); try { while(!downloadTask.isThreadStartRun()&& downloadTask.isRunning()){ //开始启动下载的时候 ,超时线程也跟着启动。 Thread.sleep(500); } if(downloadTask.isRunning()){ isWhile = true; } Thread.sleep(2000); while(downloadTask != null && downloadTask.isRunning()) { isWhile = true; Thread.sleep(3000); if(c != downloadTask.getN() && downloadTask.isThreadStartWrite() && c!=downloadTask.getLength()) { c = downloadTask.getN(); t=0; process = downloadTask.getDownprocess(); } else { if(process<100)t++; } process = downloadTask.getDownprocess(); if(process > 100){ break; } if(t >= TIMEOUT_COUNT) { downloadTask.setRunning(false); downloadTask.interrupt(); downloadTask = null; break; } if(process <= -1){ break; } } if(t >= TIMEOUT_COUNT && downloadTask == null && process<100) { listener.statusTimeoutListener(-1,name); return; } if(!isWhile){ if(downloadTask != null){ downloadTask.setRunning(false); downloadTask.interrupt(); downloadTask = null; } listener.statusTimeoutListener(-1,name); } } catch (Exception e) { if(process <100){ listener.statusTimeoutListener(process,name); } }
这是它的一个run方法,可以看到首先,做一个延时,等待下载线程的执行,因为我们的超时线程和下载线程必须同时执行!!随之来的就是检测,检测下载线程的isRunning,检测文件读写是是否卡顿,如果在12s之内下载一直是卡顿的,我们就直接将下载线程停止掉(当然,这个停止掉指的是我们不再使用它,并不能直接杀死他),这个12秒我们也可以自己在超时线程里边控制。如果 listener.statusTimeoutListener(process,name);则表示线程下载超时,停止下载。
使用方法:
DownloadTask downloadTask = new DownloadTask("下载地址", "文件md5", 122222, "文件保存地址", new DownloadTask.DownLoadTaskStatusListener() <span style="white-space:pre"> </span>{ public void statusListener(int process, String fileName) { // 这里边是下载进度的回调 } }); downloadTask.start(); DownloadTimeout downloadTimeout = new DownloadTimeout(downloadTask, new DownLoadTaskStatusTimeoutListener() { public void statusTimeoutListener(int process, String fileName) { // 这里边是下载失败,超时的回调 } }); downloadTimeout.start();
当然咯,下面就是下载链接了 点击打开链接 下载。
相关文章推荐
- Android中图片的三级缓存
- Android and MJPEG
- Android开发实现距离位置变化(类似滴滴打车附件的车辆)
- 10 条提升 Android 性能的建议
- Android-存储方式(持久化数据的方式)
- 让Android应用保持灵敏响应:AsyncTask
- Android之如何解决右上角不显示3个点的菜单
- Android之原生控件列表
- Android之常见数据类型
- Android之常用代码片段收集
- Android之常用头文件列表
- Android之Widget认识
- no resource identifer found in 'android' package
- 今天起记录我的android 开发历程
- Android 属性动画(Property Animation)
- Android5.0+(CollapsingToolbarLayout)
- 免费的android室内地图接口分享
- Android与JavaScript之间的相互调用
- Android 获得ImageView中Image的绘制大小
- Android之类定义的认识