Android实现断点下载的方法
程序员文章站
2024-02-26 16:32:04
最近做的项目中需要实现断点下载,即用户一次下载可以分多次进行,下载过程可以中断,在目前大多数的带离线缓存的软件都是需要实现这一功能。本文阐述了通过sqlite3简单实现了一...
最近做的项目中需要实现断点下载,即用户一次下载可以分多次进行,下载过程可以中断,在目前大多数的带离线缓存的软件都是需要实现这一功能。本文阐述了通过sqlite3简单实现了一个具有断点下载功能的demo。言归正传,开始正文。
设计
数据库表存储元数据
dbhelper.java
用于业务存储的dao
dao.java
抽象下载信息的bean
loadinfo.java
呈现下载信息view
mainactivity.java
存储下载信息bean
downloadinfo.java
封装好的下载类
downloader.java
代码结构
具体实现
下载信息类:downloadinfo.java
这里的代码很简单,就是一个用来保存下载信息的类,很简单,没啥好说的,具体看注释
package entity; public class downloadinfo { private int threadid;//线程id private int startpos;//下载起始位置 private int endpos;//下载结束位置 private int completesize;//下载完成量 private string url;//资源url public downloadinfo(int tid,int sp, int ep,int csize,string address){ threadid=tid; startpos=sp; endpos=ep; completesize = csize; url=address; } /** * @return the threadid */ public int getthreadid() { return threadid; } /** * @param threadid the threadid to set */ public void setthreadid(int threadid) { this.threadid = threadid; } /** * @return the startpos */ public int getstartpos() { return startpos; } /** * @param startpos the startpos to set */ public void setstartpos(int startpos) { this.startpos = startpos; } /** * @return the endpos */ public int getendpos() { return endpos; } /** * @param endpos the endpos to set */ public void setendpos(int endpos) { this.endpos = endpos; } /** * @return the completesize */ public int getcompletesize() { return completesize; } /** * @param completesize the completesize to set */ public void setcompletesize(int completesize) { this.completesize = completesize; } /** * @return the url */ public string geturl() { return url; } /** * @param url the url to set */ public void seturl(string url) { this.url = url; } @override public string tostring() { // todo auto-generated method stub return "threadid:"+threadid+",startpos:"+startpos+",endpos:"+endpos+",completesize:"+completesize+",url:"+url; } }
数据库 dbhelper.java
package db; import android.content.context; import android.database.sqlite.sqlitedatabase; import android.database.sqlite.sqliteopenhelper; public class dbhelper extends sqliteopenhelper { string sql ="create table download_info (id integer primary key autoincrement," + "thread_id integer," + "start_pos integer," + "end_pos integer," + "complete_size integer," + "url char)"; public dbhelper(context context) { // todo auto-generated constructor stub super(context, "download.db", null, 1); } @override public void oncreate(sqlitedatabase db) { // todo auto-generated method stub db.execsql(sql); } @override public void onupgrade(sqlitedatabase db, int oldversion, int newversion) { // todo auto-generated method stub } }
数据库业务管理类 dao.java
package db; import java.util.arraylist; import java.util.list; import android.content.context; import android.database.cursor; import android.database.sqlite.sqlitedatabase; import entity.downloadinfo; public class dao { private dbhelper dbhelper; public dao(context context){ dbhelper = new dbhelper(context); } public boolean isnewtask(string url){ sqlitedatabase db = dbhelper.getreadabledatabase(); string sql = "select count(*) from download_info where url=?"; cursor cursor = db.rawquery(sql, new string[]{url}); cursor.movetofirst(); int count = cursor.getint(0); cursor.close(); return count == 0; } public void saveinfo(list<downloadinfo> infos){ sqlitedatabase db = dbhelper.getwritabledatabase(); string sql = "insert into download_info(thread_id,start_pos,end_pos,complete_size,url) values(?,?,?,?,?)"; object[] bindargs= null; for(downloadinfo info:infos){ bindargs=new object[]{info.getthreadid(),info.getstartpos(),info.getendpos(),info.getcompletesize(),info.geturl()}; db.execsql(sql, bindargs); } } public list<downloadinfo> getinfo(string url){ sqlitedatabase db = dbhelper.getreadabledatabase(); list<downloadinfo> infos = new arraylist<downloadinfo>(); string sql ="select thread_id,start_pos,end_pos,complete_size,url from download_info where url=?"; cursor cursor=db.rawquery(sql, new string[]{url}); while(cursor.movetonext()){ downloadinfo info = new downloadinfo(cursor.getint(0), cursor.getint(1), cursor.getint(2), cursor.getint(3),cursor.getstring(4)); infos.add(info); } cursor.close(); return infos; } public void deleteinfo(string url){ sqlitedatabase db = dbhelper.getwritabledatabase(); db.delete("download_info", "url=?", new string[]{url}); db.close(); } public void updateinfo(int completesize,int threadid,string url){ sqlitedatabase db = dbhelper.getwritabledatabase(); string sql ="update download_info set complete_size=? where thread_id=? and url=?"; db.execsql(sql, new object[]{completesize,threadid,url}); } public void closedb(){ dbhelper.close(); } }
当前状态保存类 loadinfo.java
package entity; public class loadinfo { private int filesize; private int completesize; private string url; public loadinfo(int fs,int csize,string address){ filesize=fs; completesize = csize; url=address; } /** * @return the filesize */ public int getfilesize() { return filesize; } /** * @param filesize the filesize to set */ public void setfilesize(int filesize) { this.filesize = filesize; } /** * @return the completesize */ public int getcompletesize() { return completesize; } /** * @param completesize the completesize to set */ public void setcompletesize(int completesize) { this.completesize = completesize; } /** * @return the url */ public string geturl() { return url; } /** * @param url the url to set */ public void seturl(string url) { this.url = url; } }
下载助手类:downloader.java
package com.winton.downloadmanager; import java.io.file; import java.io.ioexception; import java.io.inputstream; import java.io.randomaccessfile; import java.net.httpurlconnection; import java.net.malformedurlexception; import java.net.url; import java.util.arraylist; import java.util.list; import android.content.context; import android.os.handler; import android.os.message; import db.dao; import entity.downloadinfo; import entity.loadinfo; public class downloader { private string url; private string localpath; private int threadcount; private handler mhanler; private dao dao; private int filesize; private list<downloadinfo> infos; private final static int init = 1; private final static int downloading =2; private final static int pause =3; private int state = init; public downloader(string address,string lpath,int thcount,context context,handler handler){ url =address; localpath = lpath; threadcount = thcount; mhanler = handler; dao = new dao(context); } public boolean isdownloading(){ return state == downloading; } public loadinfo getdownloadinfos(){ if(isfirstdownload(url)){ init(); int range = filesize/threadcount; infos = new arraylist<downloadinfo>(); for(int i=0;i<=threadcount-1;i++){ downloadinfo info = new downloadinfo(i, i*range, (i+1)*range-1, 0, url); infos.add(info); } dao.saveinfo(infos); return new loadinfo(filesize, 0, url); }else{ infos = dao.getinfo(url); int size = 0; int completesize =0; for(downloadinfo info:infos){ completesize += info.getcompletesize(); size += info.getendpos()-info.getstartpos()+1; } return new loadinfo(size, completesize, url); } } public boolean isfirstdownload(string url){ return dao.isnewtask(url); } public void init(){ try { url murl = new url(this.url); httpurlconnection connection = (httpurlconnection)murl.openconnection(); connection.setconnecttimeout(5000); connection.setrequestmethod("get"); filesize = connection.getcontentlength(); file file = new file(localpath); if(!file.exists()){ file.createnewfile(); } randomaccessfile accessfile = new randomaccessfile(file, "rwd"); accessfile.setlength(filesize); accessfile.close(); connection.disconnect(); } catch (malformedurlexception e) { // todo auto-generated catch block e.printstacktrace(); } catch (ioexception e) { // todo auto-generated catch block e.printstacktrace(); } } public void download(){ if(infos != null){ if(state ==downloading){ return; } state = downloading; for(downloadinfo info:infos){ new downloadthread(info.getthreadid(), info.getstartpos(), info.getendpos(), info.getcompletesize(), info.geturl()).start(); } } } class downloadthread extends thread{ private int threadid; private int startpos; private int endpos; private int completesize; private string url; public downloadthread(int tid,int sp,int ep,int csize,string address) { // todo auto-generated constructor stub threadid=tid; startpos=sp; endpos = ep; completesize = csize; url = address; } @override public void run() { // todo auto-generated method stub httpurlconnection connection = null; randomaccessfile randomaccessfile = null; inputstream is = null; try { url murl = new url(url); connection = (httpurlconnection)murl.openconnection(); connection.setconnecttimeout(5000); connection.setrequestmethod("get"); connection.setrequestproperty("range", "bytes="+(startpos+completesize)+"-"+endpos); randomaccessfile = new randomaccessfile(localpath, "rwd"); randomaccessfile.seek(startpos+completesize); is=connection.getinputstream(); byte[] buffer = new byte[4096]; int length =-1; while((length=is.read(buffer)) != -1){ randomaccessfile.write(buffer, 0, length); completesize +=length; dao.updateinfo(threadid, completesize, url); message msg = message.obtain(); msg.what=1; msg.obj=url; msg.arg1=length; mhanler.sendmessage(msg); if(state==pause){ return; } } } catch (malformedurlexception e) { // todo auto-generated catch block e.printstacktrace(); } catch (ioexception e) { // todo auto-generated catch block e.printstacktrace(); }finally{ try { is.close(); randomaccessfile.close(); connection.disconnect(); dao.closedb(); } catch (ioexception e) { // todo auto-generated catch block e.printstacktrace(); } } } } public void delete(string url){ dao.deleteinfo(url); } public void reset(){ state=init; } public void pause(){ state=pause; } }
view呈现类:mainactivity.java
package com.winton.downloadmanager; import android.app.activity; import android.os.bundle; import android.os.environment; import android.os.handler; import android.os.message; import android.view.view; import android.view.view.onclicklistener; import android.widget.button; import android.widget.progressbar; import android.widget.textview; import android.widget.toast; import entity.loadinfo; public class mainactivity extends activity implements onclicklistener{ private textview name; private progressbar process; private button start,stop; private downloader downloader; //处理下载进度ui的 handler handler = new handler(){ public void handlemessage(android.os.message msg) { if(msg.what==1){ name.settext((string)msg.obj); int lenght = msg.arg1; process.incrementprogressby(lenght); } if(msg.what==2){ int max =msg.arg1; process.setmax(max); toast.maketext(getapplicationcontext(), max+"", 1).show(); } }; }; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); name=(textview)findviewbyid(r.id.tv_name); process=(progressbar)findviewbyid(r.id.pb_download); start = (button)findviewbyid(r.id.bt_start); stop = (button)findviewbyid(r.id.bt_stop); start.setonclicklistener(this); stop.setonclicklistener(this); downloader=new downloader("http://img4.duitang.com/uploads/item/201206/11/20120611174542_5krmj.jpeg", environment.getexternalstoragedirectory().getpath()+"/test1.jpg", 1, getapplicationcontext(), handler); } @override public void onclick(view v) { // todo auto-generated method stub if(v==start){ new thread(new runnable() { @override public void run() { // todo auto-generated method stub loadinfo loadinfo = downloader.getdownloadinfos(); message msg =handler.obtainmessage(); msg.what=2; msg.arg1=loadinfo.getfilesize(); handler.sendmessage(msg); downloader.download(); } }).start(); return; } if(v==stop){ downloader.pause(); return; } } }
运行效果
总体比较简单,基本实现了 断点下载的功能,而且开启了多个线程去实现分块下载,对初学的同学有一定的帮助。
这些代码我也是从网上各种搜集而来,自己亲自动手敲了一遍,并做了一些小的改动,对整个断点下载的过程有了一个深刻的认识。因此平时要多敲代码,善于总结,必能有所收获。
上一篇: 深入理解Java高级特性——注解