Android 下载线程的编写

来源:互联网 发布:网络电视如何看地方台 编辑:程序博客网 时间:2024/05/17 18:48

在我们的开发中,碰到文件下载的情况已经是很多见了吧?平时在开发大项目时,我们可能会用一些开源的库,比如afinal、Xutils等等,不过对于一些菜鸟的学习来说,自己去写一个下载的工具类,这对于自身的知识巩固那还是有很多好处的。好了废话不多说,现在我们直接来写一个文件下载工具类(不支持断点续传)。

下面我们来分析一下怎么去写。

首先,我们得去发送http请求去加载我们的inputStream吧,其次我们还得去利用BufferedInputStream、BufferedOutputStream等去写文件吧,还有md5校验吧。好了这里边还有很多细节问题,我们来一一查看,直接上代码。

// 获取文件的inputStreamprivate 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());

好了,下载的线程算是完事了,我们的额超时线程还没完呢,等等,别着急马上。它主要是干啥呢,就是检测下载线程的运行状态,来吧那就。

@Overridepublic 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();

当然咯,下面就是下载链接了 点击打开链接 下载。

2 0