The multi‑thread resumable download loader can occupy more resources on the server side when multiple threads are concurrently downloaded, thus accelerating the download speed. During the download process, the number of data copies of each thread is recorded, if the download is interrupted, for example, when there is no signal disconnection or insufficient power, you need to use the resumable data transfer function. The download will continue from the recorded location at the next startup, this prevents repeated downloads. Here, the database is used to record the download progress.
Resumable upload
1. resumable download needs to record the download progress of each thread during the download process
2. Read the database before each download starts. check whether there are any unfinished records. If yes, continue to download the database. If no, create a new record and insert it to the database.
3. Update the download progress in the database after each write to the file
4. After the download is complete, delete the download records in the database.
Handler transfers data
This is mainly used to record the percentage. Each part of data downloaded notifies the main thread to record the time.
1. The view created in the main thread can only be modified in the main thread. Other threads can only communicate with the main thread and change the view data in the main thread.
2. handler can be used to handle this requirement.
Create a handler in the main thread and override the handlemessage () method.
The new thread uses handler to send a message. The main thread can receive the message and execute the handlemessage () method.
Dynamically generate new view
Multi-task download
1. Create an XML file and configure the view to be generated.
2. Obtain the system service layoutinflater to generate a new view.
Layoutinflater Inflater = (layoutinflater) getsystemservice (Layout_inflater_service);
3. Use the inflate (INT resource, viewgroup root) method to generate a new view
4. Call addview of a container on the current page to add the newly created view.
Example
Progress bar style download. xml
<? XML version = "1.0" encoding = "UTF-8"?> <Linearlayout xmlns: Android = "http://schemas.android.com/apk/res/android" Android: layout_width = "fill_parent" Android: layout_height = "wrap_content"> <linearlayout Android: Orientation = "vertical" Android: layout_width = "fill_parent" Android: layout_height = "wrap_content" Android: layout_weight = "1"> <! -- The progress bar style is a circular progress bar by default. The style attribute must be configured for the horizontal progress bar ,? Android: ATTR/progressbarstylehorizontal --> <progressbarandroid: layout_width = "fill_parent" Android: layout_height = "20dp" style = "? Android: ATTR/progressbarstylehorizontal "/> <textviewandroid: layout_width =" wrap_content "Android: layout_height =" wrap_content "Android: layout_gravity =" center "Android: TEXT = "0%"/> </linearlayout> <buttonandroid: layout_width = "40dp" Android: layout_height = "40dp" Android: onclick = "pause" Android: TEXT = "|"/> </linearlayout>
Top style main. xml
<? XML version = "1.0" encoding = "UTF-8"?> <Linearlayout xmlns: Android = "http://schemas.android.com/apk/res/android" Android: Orientation = "vertical" Android: layout_width = "fill_parent" Android: layout_height = "fill_parent" Android: id = "@ + ID/root"> <textview Android: layout_width = "fill_parent" Android: layout_height = "wrap_content" Android: TEXT = "Enter the download path"/> <linearlayout Android: layout_width = "fill_parent" Android: layout_height = "wrap_content" Android: layout_marginbottom = "30dp"> <edittextandroid: id = "@ + ID/path" Android: layout_width = "fill_parent" Android: layout_height = "wrap_content" Android: singleline = "true" Android: layout_weight = "1"/> <buttonandroid: layout_width = "wrap_content" Android: layout_height = "wrap_content" Android: text = "Download" Android: onclick = "Download"/> </linearlayout>
Mainactivity. Java
Public class mainactivity extends activity {private layoutinflater Inflater; private linearlayout rootlinearlayout; private edittext pathedittext; @ overridepublic void oncreate (bundle savedinstancestate) {super. oncreate (savedinstancestate); setcontentview (R. layout. main); // dynamically generate a new view and obtain the system service layoutinflater, which is used to generate a new viewinflater = (layoutinflater) getsystemservice (layout_inflater_service); rootlinearlayout = (linearlayout) find. id. root); pathedittext = (edittext) findviewbyid (R. id. path); // After the form is created, check whether there are unfinished tasks in the database. If yes, create a progress bar and other components, continue to download list <string> List = new infodao (this ). queryundone (); For (string path: List) createdownload (PATH);}/*** Download button * @ Param view */Public void download (view) {string Path = "http: // 192.168.1.199: 8080/14 _ Web/" + pathedittext. gettext (). tostring (); createdownload (PATH);}/*** dynamically generate a new view * initialize form data * @ Param path */private void createdownload (string path) {// obtain the system service layoutinflater to generate a new viewlayoutinflater Inflater = (layoutinflater) getsystemservice (layout_inflater_service); linearlayout = (linearlayout) Inflater. inflate (R. layout. download, null); linearlayout childlinearlayout = (linearlayout) linearlayout. getchildat (0); progressbar = (progressbar) childlinearlayout. getchildat (0); textview = (textview) childlinearlayout. getchildat (1); button = (button) linearlayout. getchildat (1); try {button. setonclicklistener (New mylistener (progressbar, textview, PATH); // call addview of a container on the current page and add the newly created view to rootlinearlayout. addview (linearlayout);} catch (exception e) {e. printstacktrace () ;}} private final class mylistener implements onclicklistener {private progressbar; private textview; private int filelen; private downloader; private string name; /*** download ** @ Param progressbar // progress bar * @ Param textview // percentage * @ Param path // download file path */Public mylistener (progressbar, textview, string path) {This. progressbar = progressbar; this. textview = textview; name = path. substring (path. lastindexof ("/") + 1); downloader = new downloader (getapplicationcontext (), Handler); try {Downloader. download (path, 3);} catch (exception e) {e. printstacktrace (); toast. maketext (getapplicationcontext (), "an exception occurred during download", 0 ). show (); throw new runtimeexception (e) ;}// handler transfers data private handler = new handler () {@ overridepublic void handlemessage (Message MSG) {Switch (MSG. what) {Case 0: // get the file size filelen = MSG. getdata (). getint ("filelen"); // set the maximum progress bar scale: setmax () progressbar. setmax (filelen); break; Case 1: // get the total amount of the current download int done = MSG. getdata (). getint ("done"); // percentage of the current progress textview. settext (name + "\ t" + done * 100/filelen + "%"); // progress bar: setprogress () progressbar. setprogress (done); If (done = filelen) {toast. maketext (getapplicationcontext (), name + "downloaded", 0 ). show (); // exit the progress bar rootlinearlayout after the download is complete. removeview (View) progressbar. getparent (). getparent ();} break ;}};/*** pause and continue download */Public void onclick (view v) {button pausebutton = (button) V; if ("| ". equals (pausebutton. gettext () {Downloader. pause (); pausebutton. settext ("");} else {Downloader. resume (); pausebutton. settext ("| ");}}}}
Downloader. Java
Public class downloader {private int done; private infodao Dao; private int filelen; private handler; private Boolean ispause; Public downloader (context, Handler handler) {Dao = new infodao (context); this. handler = handler;}/*** multithreading download * @ Param path download path * @ Param thcount: How many threads need to be enabled * @ throws exception */Public void download (string path, int thcount) throws exception {URL url = new URL (Pat H); httpurlconnection conn = (httpurlconnection) URL. openconnection (); // set the timeout value Conn. setconnecttimeout (3000); If (Conn. getresponsecode () = 200) {filelen = Conn. getcontentlength (); string name = path. substring (path. lastindexof ("/") + 1); file = new file (environment. getexternalstoragedirectory (), name); randomaccessfile RAF = new randomaccessfile (file, "RWS"); RAF. setlength (filelen); RAF. close (); // hand The ler sends a message. The main thread receives the message and obtains the length of the data. Message MSG = new message (); MSG. what = 0; MSG. getdata (). putint ("filelen", filelen); handler. sendmessage (MSG); // calculate the number of bytes downloaded by each thread. Int partlen = (filelen + thcount-1)/thcount; For (INT I = 0; I <thcount; I ++) New downloadthread (URL, file, partlen, I ). start () ;}else {Throw new illegalargumentexception ("404 path:" + path) ;}} private final class downloadthread extends thread {private URL; private file; private int partlen; private int ID; Public downloadthread (URL, file, int partlen, int ID) {This. url = URL; this. file = file; this. partlen = partlen; this. id = ID;}/*** write operation */Public void run () {// determine whether the previous unfinished task has been completed. info = Dao. query (URL. tostring (), ID); If (info! = NULL) {// If yes, read the current thread's downloaded done + = info. getdone ();} else {// If NO, a new record is created and saved to Info = new info (URL. tostring (), ID, 0); Dao. insert (Info);} int start = ID * partlen + info. getdone (); // start position + = downloaded int end = (ID + 1) * partlen-1; try {httpurlconnection conn = (httpurlconnection) URL. openconnection (); Conn. setreadtimeout (3000); // get the data at the specified position. If the range is beyond the data range on the server, the value of the range parameter is Conn. setrequestproperty ("range", "B Ytes = "+ start +"-"+ end); randomaccessfile RAF = new randomaccessfile (file," RWS "); RAF. seek (start); // starts to read and write data. inputstream in = Conn. getinputstream (); byte [] Buf = new byte [1024*10]; int Len; while (LEN = in. read (BUF ))! =-1) {If (ispause) {// use the thread lock to lock the thread synchronized (DAO) {try {Dao. wait ();} catch (interruptedexception e) {e. printstacktrace () ;}} Raf. write (BUF, 0, Len); done + = Len; info. setdone (info. getdone () + Len); // record the data volume downloaded by each thread Dao. update (Info); // The New thread uses handler to send messages. The main thread receives the message MSG = new message (); MSG. what = 1; MSG. getdata (). putint ("done", done); handler. sendmessage (MSG);} In. close (); RAF. close (); // Delete the download record Dao. deleteall (info. getpath (), filelen);} catch (ioexception e) {e. printstacktrace () ;}}// pause public void pause () {ispause = true;} // continue to download public void resume () {ispause = false; // recover all threads synchronized (DAO) {Dao. policyall ();}}}
Dao:
Dbopenhelper:
public class DBOpenHelper extends SQLiteOpenHelper {public DBOpenHelper(Context context) {super(context, "download.db", null, 1);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL("CREATE TABLE info(path VARCHAR(1024), thid INTEGER, done INTEGER, PRIMARY KEY(path, thid))");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}}
Infodao:
public class InfoDao {private DBOpenHelper helper;public InfoDao(Context context) {helper = new DBOpenHelper(context);}public void insert(Info info) {SQLiteDatabase db = helper.getWritableDatabase();db.execSQL("INSERT INTO info(path, thid, done) VALUES(?, ?, ?)", new Object[] { info.getPath(), info.getThid(), info.getDone() });}public void delete(String path, int thid) {SQLiteDatabase db = helper.getWritableDatabase();db.execSQL("DELETE FROM info WHERE path=? AND thid=?", new Object[] { path, thid });}public void update(Info info) {SQLiteDatabase db = helper.getWritableDatabase();db.execSQL("UPDATE info SET done=? WHERE path=? AND thid=?", new Object[] { info.getDone(), info.getPath(), info.getThid() });}public Info query(String path, int thid) {SQLiteDatabase db = helper.getWritableDatabase();Cursor c = db.rawQuery("SELECT path, thid, done FROM info WHERE path=? AND thid=?", new String[] { path, String.valueOf(thid) });Info info = null;if (c.moveToNext())info = new Info(c.getString(0), c.getInt(1), c.getInt(2));c.close();return info;}public void deleteAll(String path, int len) {SQLiteDatabase db = helper.getWritableDatabase();Cursor c = db.rawQuery("SELECT SUM(done) FROM info WHERE path=?", new String[] { path });if (c.moveToNext()) {int result = c.getInt(0);if (result == len)db.execSQL("DELETE FROM info WHERE path=? ", new Object[] { path });}}public List<String> queryUndone() {SQLiteDatabase db = helper.getWritableDatabase();Cursor c = db.rawQuery("SELECT DISTINCT path FROM info", null);List<String> pathList = new ArrayList<String>();while (c.moveToNext())pathList.add(c.getString(0));c.close();return pathList;}}