android多线程断点下载——网络编
很多时候我们需要在Android设备上下载远程服务器上的文件进安装,前两天晚上我看到一个视频,写了两个晚上,大概理解一下。 直接通过Android提供的Http类访问远程服务器,这里AndroidHttpClient是SDK 2.2中新出的方法, 下载断点文件。 下面让我们看一下图先。 让我们看一下代码的实现方法。 packagecom.smart.db; importjava.util.HashMap; importjava.util.Map; importandroid.content.Context; importandroid.database.Cursor; importandroid.database.sqlite.SQLiteDatabase; /** *业务bean * */ publicclassFileService{ privateDBOpenHelperopenHelper; publicFileService(Contextcontext){ openHelper=newDBOpenHelper(context); } /** *获取每条线程已经下载的文件长度 *@parampath *@return */ publicMap<Integer,Integer>getData(Stringpath){ SQLiteDatabasedb=openHelper.getReadableDatabase(); Cursorcursor=db.rawQuery("selectthreadid,downlengthfromSmartFileDownlogwheredownpath=?",newString[]{path}); Map<Integer,Integer>data=newHashMap<Integer,Integer>(); while(cursor.moveToNext()){ data.put(cursor.getInt(0),cursor.getInt(1)); } cursor.close(); db.close(); returndata; } /** *保存每条线程已经下载的文件长度 *@parampath *@parammap */ publicvoidsave(Stringpath,Map<Integer,Integer>map){//intthreadid,intposition SQLiteDatabasedb=openHelper.getWritableDatabase(); db.beginTransaction(); try{ for(Map.Entry<Integer,Integer>entry:map.entrySet()){ db.execSQL("insertintoSmartFileDownlog(downpath,threadid,downlength)values(?,?,?)", newObject[]{path,entry.getKey(),entry.getValue()}); } db.setTransactionSuccessful(); }finally{ db.endTransaction(); } db.close(); } /** *实时更新每条线程已经下载的文件长度 *@parampath *@parammap */ publicvoidupdate(Stringpath,Map<Integer,Integer>map){ SQLiteDatabasedb=openHelper.getWritableDatabase(); db.beginTransaction(); try{ for(Map.Entry<Integer,Integer>entry:map.entrySet()){ db.execSQL("updateSmartFileDownlogsetdownlength=?wheredownpath=?andthreadid=?", newObject[]{entry.getValue(),path,entry.getKey()}); } db.setTransactionSuccessful(); }finally{ db.endTransaction(); } db.close(); } /** *当文件下载完成后,删除对应的下载记录 *@parampath */ publicvoiddelete(Stringpath){ SQLiteDatabasedb=openHelper.getWritableDatabase(); db.execSQL("deletefromSmartFileDownlogwheredownpath=?",newObject[]{path}); db.close(); } } packagecom.smart.impl; importjava.io.File; importjava.io.RandomAccessFile; importjava.net.HttpURLConnection; importjava.net.URL; importjava.util.LinkedHashMap; importjava.util.Map; importjava.util.UUID; importjava.util.concurrent.ConcurrentHashMap; importjava.util.regex.Matcher; importjava.util.regex.Pattern; importandroid.content.Context; importandroid.util.Log; importcom.smart.db.FileService; /** *文件下载器 *@authorlihuoming@sohu.com */ publicclassSmartFileDownloader{ privatestaticfinalStringTAG="SmartFileDownloader"; privateContextcontext; privateFileServicefileService; /*已下载文件长度*/ privateintdownloadSize=0; /*原始文件长度*/ privateintfileSize=0; /*线程数*/ privateSmartDownloadThread[]threads; /*本地保存文件*/ privateFilesaveFile; /*缓存各线程下载的长度*/ privateMap<Integer,Integer>data=newConcurrentHashMap<Integer,Integer>(); /*每条线程下载的长度*/ privateintblock; /*下载路径*/ privateStringdownloadUrl; /** *获取线程数 */ publicintgetThreadSize(){ returnthreads.length; } /** *获取文件大小 *@return */ publicintgetFileSize(){ returnfileSize; } /** *累计已下载大小 *@paramsize */ protectedsynchronizedvoidappend(intsize){ downloadSize+=size; } /** *更新指定线程最后下载的位置 *@paramthreadId线程id *@parampos最后下载的位置 */ protectedvoidupdate(intthreadId,intpos){ this.data.put(threadId,pos); } /** *保存记录文件 */ protectedsynchronizedvoidsaveLogFile(){ this.fileService.update(this.downloadUrl,this.data); } /** *构建文件下载器 *@paramdownloadUrl下载路径 *@paramfileSaveDir文件保存目录 *@paramthreadNum下载线程数 */ publicSmartFileDownloader(Contextcontext,StringdownloadUrl,FilefileSaveDir,intthreadNum){ try{ this.context=context; this.downloadUrl=downloadUrl; fileService=newFileService(this.context); URLurl=newURL(this.downloadUrl); if(!fileSaveDir.exists())fileSaveDir.mkdirs(); this.threads=newSmartDownloadThread[threadNum]; HttpURLConnectionconn=(HttpURLConnection)url.openConnection(); conn.setConnectTimeout(5*1000); conn.setRequestMethod("GET"); 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("Accept-Language","zh-CN"); conn.setRequestProperty("Referer",downloadUrl); conn.setRequestProperty("Charset","UTF-8"); conn.setRequestProperty("User-Agent","Mozilla/4.0(compatible;MSIE8.0;WindowsNT5.2;Trident/4.0;.NETCLR1.1.4322;.NETCLR2.0.50727;.NETCLR3.0.04506.30;.NETCLR3.0.4506.2152;.NETCLR3.5.30729)"); conn.setRequestProperty("Connection","Keep-Alive"); conn.connect(); printResponseHeader(conn); if(conn.getResponseCode()==200){ this.fileSize=conn.getContentLength();//根据响应获取文件大小 if(this.fileSize<=0)thrownewRuntimeException("Unkownfilesize"); Stringfilename=getFileName(conn); this.saveFile=newFile(fileSaveDir,filename);/*保存文件*/ Map<Integer,Integer>logdata=fileService.getData(downloadUrl); if(logdata.size()>0){ for(Map.Entry<Integer,Integer>entry:logdata.entrySet()) data.put(entry.getKey(),entry.getValue()); } this.block=(this.fileSize%this.threads.length)==0?this.fileSize/this.threads.length:this.fileSize/this.threads.length+1; if(this.data.size()==this.threads.length){ for(inti=0;i<this.threads.length;i++){ this.downloadSize+=this.data.get(i+1); } print("已经下载的长度"+this.downloadSize); } }else{ thrownewRuntimeException("servernoresponse"); } }catch(Exceptione){ print(e.toString()); thrownewRuntimeException("don'tconnectionthisurl"); } } /** *获取文件名 */ privateStringgetFileName(HttpURLConnectionconn){ Stringfilename=this.downloadUrl.substring(this.downloadUrl.lastIndexOf('/')+1); if(filename==null||"".equals(filename.trim())){//如果获取不到文件名称 for(inti=0;;i++){ Stringmine=conn.getHeaderField(i); if(mine==null)break; if("content-disposition".equals(conn.getHeaderFieldKey(i).toLowerCase())){ Matcherm=Pattern.compile(".*filename=(.*)").matcher(mine.toLowerCase()); if(m.find())returnm.group(1); } } filename=UUID.randomUUID()+".tmp";//默认取一个文件名 } returnfilename; } /** *开始下载文件 *@paramlistener监听下载数量的变化,如果不需要了解实时下载的数量,可以设置为null *@return已下载文件大小 *@throwsException */ publicintdownload(SmartDownloadProgressListenerlistener)throwsException{ try{ RandomAccessFilerandOut=newRandomAccessFile(this.saveFile,"rw"); if(this.fileSize>0)randOut.setLength(this.fileSize); randOut.close(); URLurl=newURL(this.downloadUrl); if(this.data.size()!=this.threads.length){ this.data.clear();//清除数据 for(inti=0;i<this.threads.length;i++){ this.data.put(i+1,0); } } for(inti=0;i<this.threads.length;i++){ intdownLength=this.data.get(i+1); if(downLength<this.block&&this.downloadSize<this.fileSize){//该线程未完成下载时,继续下载 this.threads=newSmartDownloadThread(this,url,this.saveFile,this.block,this.data.get(i+1),i+1); this.threads.setPriority(7); this.threads.start(); }else{ this.threads=null; } } this.fileService.save(this.downloadUrl,this.data); booleannotFinish=true;//下载未完成 while(notFinish){//循环判断是否下载完毕 Thread.sleep(900); notFinish=false;//假定下载完成 for(inti=0;i<this.threads.length;i++){ if(this.threads!=null&&!this.threads.isFinish()){ notFinish=true;//下载没有完成 if(this.threads.getDownLength()==-1){//如果下载失败,再重新下载 this.threads=newSmartDownloadThread(this,url,this.saveFile,this.block,this.data.get(i+1),i+1); this.threads.setPriority(7); this.threads.start(); } } } if(listener!=null)listener.onDownloadSize(this.downloadSize); } fileService.delete(this.downloadUrl); }catch(Exceptione){ print(e.toString()); thrownewException("filedownloadfail"); } returnthis.downloadSize; } /** *获取Http响应头字段 *@paramhttp *@return */ publicstaticMap<String,String>getHttpResponseHeader(HttpURLConnectionhttp){ Map<String,String>header=newLinkedHashMap<String,String>(); for(inti=0;;i++){ Stringmine=http.getHeaderField(i); if(mine==null)break; header.put(http.getHeaderFieldKey(i),mine); } returnheader; } /** *打印Http头字段 *@paramhttp */ publicstaticvoidprintResponseHeader(HttpURLConnectionhttp){ Map<String,String>header=getHttpResponseHeader(http); for(Map.Entry<String,String>entry:header.entrySet()){ Stringkey=entry.getKey()!=null?entry.getKey()+":":""; print(key+entry.getValue()); } } //打印日志 privatestaticvoidprint(Stringmsg){ Log.i(TAG,msg); } } packagecom.smart.impl; importjava.io.File; importjava.io.InputStream; importjava.io.RandomAccessFile; importjava.net.HttpURLConnection; importjava.net.URL; importandroid.util.Log; publicclassSmartDownloadThreadextendsThread{ privatestaticfinalStringTAG="SmartDownloadThread"; privateFilesaveFile; privateURLdownUrl; privateintblock; /**下载开始位置*/ privateintthreadId=-1; privateintdownLength; privatebooleanfinish=false; privateSmartFileDownloaderdownloader; publicSmartDownloadThread(SmartFileDownloaderdownloader,URLdownUrl,FilesaveFile,intblock,intdownLength,intthreadId){ this.downUrl=downUrl; this.saveFile=saveFile; this.block=block; this.downloader=downloader; this.threadId=threadId; this.downLength=downLength; } @Override publicvoidrun(){ if(downLength<block){//未下载完成 try{ HttpURLConnectionhttp=(HttpURLConnection)downUrl.openConnection(); http.setConnectTimeout(5*1000); http.setRequestMethod("GET"); http.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,*/*"); http.setRequestProperty("Accept-Language","zh-CN"); http.setRequestProperty("Referer",downUrl.toString()); http.setRequestProperty("Charset","UTF-8"); intstartPos=block*(threadId-1)+downLength;//开始位置 intendPos=block*threadId-1;//结束位置 http.setRequestProperty("Range","bytes="+startPos+"-"+endPos);//设置获取实体数据的范围 http.setRequestProperty("User-Agent","Mozilla/4.0(compatible;MSIE8.0;WindowsNT5.2;Trident/4.0;.NETCLR1.1.4322;.NETCLR2.0.50727;.NETCLR3.0.04506.30;.NETCLR3.0.4506.2152;.NETCLR3.5.30729)"); http.setRequestProperty("Connection","Keep-Alive"); InputStreaminStream=http.getInputStream(); byte[]buffer=newbyte[1024]; intoffset=0; print("Thread"+this.threadId+"startdownloadfromposition"+startPos); RandomAccessFilethreadfile=newRandomAccessFile(this.saveFile,"rwd"); threadfile.seek(startPos); while((offset=inStream.read(buffer,0,1024))!=-1){ threadfile.write(buffer,0,offset); downLength+=offset; downloader.update(this.threadId,downLength); downloader.saveLogFile(); downloader.append(offset); } threadfile.close(); inStream.close(); print("Thread"+this.threadId+"downloadfinish"); this.finish=true; }catch(Exceptione){ this.downLength=-1; print("Thread"+this.threadId+":"+e); } } } privatestaticvoidprint(Stringmsg){ Log.i(TAG,msg); } /** *下载是否完成 *@return */ publicbooleanisFinish(){ returnfinish; } /** *已经下载的内容大小 *@return如果返回值为-1,代表下载失败 */ publiclonggetDownLength(){ returndownLength; } } packagecom.smart.activoty.download; importjava.io.File; importandroid.app.Activity; importandroid.os.Bundle; importandroid.os.Environment; importandroid.os.Handler; importandroid.os.Message; importandroid.view.View; importandroid.widget.Button; importandroid.widget.EditText; importandroid.widget.ProgressBar; importandroid.widget.TextView; importandroid.widget.Toast; importcom.smart.impl.SmartDownloadProgressListener; importcom.smart.impl.SmartFileDownloader; publicclassSmartDownloadActivityextendsActivity{ privateProgressBardownloadbar; privateEditTextpathText; privateTextViewresultView; privateHandlerhandler=newHandler(){ @Override//信息 publicvoidhandleMessage(Messagemsg){ switch(msg.what){ case1: intsize=msg.getData().getInt("size"); downloadbar.setProgress(size); floatresult=(float)downloadbar.getProgress()/(float)downloadbar.getMax(); intp=(int)(result*100); resultView.setText(p+"%"); if(downloadbar.getProgress()==downloadbar.getMax()) Toast.makeText(SmartDownloadActivity.this,R.string.success,1).show(); break; case-1: Toast.makeText(SmartDownloadActivity.this,R.string.error,1).show(); break; } } }; @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); Buttonbutton=(Button)this.findViewById(R.id.button); downloadbar=(ProgressBar)this.findViewById(R.id.downloadbar); pathText=(EditText)this.findViewById(R.id.path); resultView=(TextView)this.findViewById(R.id.result); button.setOnClickListener(newView.OnClickListener(){ @Override publicvoidonClick(Viewv){ Stringpath=pathText.getText().toString(); if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ Filedir=Environment.getExternalStorageDirectory();//文件保存目录 download(path,dir); }else{ Toast.makeText(SmartDownloadActivity.this,R.string.sdcarderror,1).show(); } } }); } //对于UI控件的更新只能由主线程(UI线程)负责,如果在非UI线程更新UI控件,更新的结果不会反映在屏幕上,某些控件还会出错 privatevoiddownload(finalStringpath,finalFiledir){ newThread(newRunnable(){ @Override publicvoidrun(){ try{ SmartFileDownloaderloader=newSmartFileDownloader(SmartDownloadActivity.this,path,dir,3); intlength=loader.getFileSize();//获取文件的长度 downloadbar.setMax(length); loader.download(newSmartDownloadProgressListener(){ @Override publicvoidonDownloadSize(intsize){//可以实时得到文件下载的长度 Messagemsg=newMessage(); msg.what=1; msg.getData().putInt("size",size); handler.sendMessage(msg); }}); }catch(Exceptione){ Messagemsg=newMessage();//信息提示 msg.what=-1; msg.getData().putString("error","下载失败");//如果下载错误,显示提示失败! handler.sendMessage(msg); } } }).start();//开始 } } 附件:http://down.51cto.com/data/2357675 本文转自 llb988 51CTO博客,原文链接:http://blog.51cto.com/llb988/510035,如需转载请自行联系原作者