Android mobile phone Development Network multithreading breakpoint Continuation Code

Source: Internet
Author: User
Tags file size int size message queue save file sqlite uuid file permissions

This example describes the implementation of a breakpoint continuation download via the HTTP protocol under the Android platform.

We are writing a Andorid HTTP protocol multithreaded breakpoint download application. Using a single thread to download HTTP files is a very simple thing for us to do. So what does a multithreaded breakpoint need? Where is the difficulty?

1. Multi-threaded Download,

2. Support Breakpoints.

The benefits of using Multithreading: Using multi-threaded downloads can increase the speed of file downloads. The process of downloading files by multiple threads is:

(1) First get the length of the download file, and then set the length of the local file.

Httpurlconnection.getcontentlength ()//Get the length of the download file

Randomaccessfile file = new Randomaccessfile ("QQWubiSetup.exe", "RWD");

File.setlength (filesize)//Set the length of the local file

(2) Calculate the data length and the download location of each thread according to the file length and number of threads.

For example, the length of the file is 6M and the number of threads is 3, so each thread downloads a data length of 2M, where each thread starts downloading as shown in the following figure.

such as 10M size, using 3 threads to download,

Thread download length of data (10%3 = 0?) 10/3:10/3+1), 1th, 2 thread download length is 4M, the third thread download length of 2M

Download start Location: Thread id* The length of data downloaded per thread =?

Download End Location: (thread id+1) * The length of data downloaded per thread -1=?

(3) Use the HTTP Range header field to specify where each thread starts downloading from the file, and where to download it.

For example: Specify to start the download from the 2M location of the file and download to the location (4m-1byte)

The code is as follows: Httpurlconnection.setrequestproperty ("Range", "bytes=2097152-4194303");

(4) Save the file and use the Randomaccessfile class to specify where each thread starts writing data from the local file.

The code is as follows Copy Code

Randomaccessfile threadfile = new Randomaccessfile ("QQWubiSetup.exe", "RWD");

Threadfile.seek (2097152);//write data from where the file is located

The program structure is shown in the following illustration:

String.xml code in File:

The code is as follows Copy Code

<?xml version= "1.0" encoding= "Utf-8"?>
<resources>
<string name= "Hello" >hello World, mainactivity!</string>
<string name= "app_name" >android network multithreading breakpoint Download </string>
<string name= "path" > Download path </string>
<string name= "Downloadbutton" > Download </string>
<string name= "Sdcarderror" >sdcard does not exist or write protection </string>
<string name= "Success" > Download complete </string>
<string name= "error" > download failed </string>
</resources>

Main.xml code in File:


Androidmanifest.xml code in File:

The code is as follows Copy Code

<?xml version= "1.0" encoding= "Utf-8"?>
<manifest xmlns:android= "http://schemas.android.com/apk/res/android" package= "Com.android.downloader" Android : versioncode= "1" android:versionname= "1.0" >
<USES-SDK android:minsdkversion= "8"/>

    <application android:icon= "@drawable/icon" android:label= "@string/app_name"
        <activity android:name= ". Mainactivity "
                   android:label= "@string/app_name"
             <intent-filter>
                 <action android:name= "Android.intent.action.MAIN"/>
                 <category android:name= " Android.intent.category.LAUNCHER "/>
            </intent-filter>
        </activity>

</application>

<!--Create and delete file permissions in SDcard-->
<uses-permission android:name= "Android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

<!--Write data permissions to SDcard-->
<uses-permission android:name= "Android.permission.WRITE_EXTERNAL_STORAGE"/>

<!--access to Internet permissions-->
<uses-permission android:name= "Android.permission.INTERNET"/>


</manifest>


Code in Mainactivity:

The code is as follows Copy Code

Package com.android.downloader;
Import Java.io.File;

Import Com.android.network.DownloadProgressListener;
Import Com.android.network.FileDownloader;

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.widget.Button;
Import Android.widget.EditText;
Import Android.widget.ProgressBar;
Import Android.widget.TextView;
Import Android.widget.Toast;

public class Mainactivity extends activity {
Private EditText Downloadpathtext;
Private TextView Resultview;
Private ProgressBar ProgressBar;

/**
* When handler is created, it is associated with the message queue of the current thread that created it, which is used to send messages to Message Queuing
* Messages in Message Queuing are processed internally by the current thread
*/
Private Handler Handler = new Handler () {

@Override


public void Handlemessage (msg) {


Switch (msg.what) {


Case 1:


Progressbar.setprogress (Msg.getdata (). GETINT ("size"));


float num = (float) progressbar.getprogress ()/(float) progressbar.getmax ();


int result = (int) (NUM*100);


Resultview.settext (result+ "%");





if (Progressbar.getprogress () ==progressbar.getmax ()) {


Toast.maketext (Mainactivity.this, r.string.success, 1). Show ();


}


Break


Case-1:


Toast.maketext (Mainactivity.this, R.string.error, 1). Show ();


Break


}


}


};





/** called the activity is a. */


@Override


public void OnCreate (Bundle savedinstancestate) {


Super.oncreate (savedinstancestate);


Setcontentview (R.layout.main);





Downloadpathtext = (edittext) This.findviewbyid (R.id.path);


ProgressBar = (ProgressBar) This.findviewbyid (R.id.downloadbar);


Resultview = (TextView) This.findviewbyid (R.id.resultview);


Button button = (button) This.findviewbyid (R.id.button);





Button.setonclicklistener (New View.onclicklistener () {





@Override


public void OnClick (View v) {


TODO auto-generated Method Stub


String path = Downloadpathtext.gettext (). toString ();


System.out.println (Environment.getexternalstoragestate () + "------" +environment.media_mounted);





if (Environment.getexternalstoragestate (). Equals (environment.media_mounted)) {


Download (path, environment.getexternalstoragedirectory ());


}else{


Toast.maketext (Mainactivity.this, R.string.sdcarderror, 1). Show ();


}


}


});


}





/**


* Main thread (UI thread)


* The interface update for the display control is only the responsibility of the UI thread, and if the property value of the control is updated on a non-UI thread, the updated display interface will not be reflected on the screen


* @param path


* @param savedir


*/


private void Download (final String path, final File Savedir) {


New Thread (New Runnable () {


@Override


public void Run () {


Filedownloader loader = new Filedownloader (mainactivity.this, Path, Savedir, 3);


Progressbar.setmax (Loader.getfilesize ());//Set the maximum scale of the progress bar to the length of the file





try {


Loader.download (New Downloadprogresslistener () {


@Override


public void ondownloadsize (int size) {//Realtime know the length of the data that the file has been downloaded


msg = new Message ();


Msg.what = 1;


Msg.getdata (). Putint ("size", size);


Handler.sendmessage (msg);//Send Message


}


});


catch (Exception e) {


Handler.obtainmessage ( -1). Sendtotarget ();


}


}


). Start ();


}


}


Code in Dbopenhelper:

The code is as follows Copy Code

Package com.android.service;
Import Android.content.Context;
Import Android.database.sqlite.SQLiteDatabase;
Import Android.database.sqlite.SQLiteOpenHelper;

public class Dbopenhelper extends Sqliteopenhelper {
private static final String dbname = "down.db";
private static final int VERSION = 1;

Public Dbopenhelper {
Super (context, dbname, NULL, VERSION);
}

@Override
public void OnCreate (Sqlitedatabase db) {
Db.execsql ("CREATE TABLE IF not EXISTS filedownlog" (ID integer primary key autoincrement, Downpath varchar (MB), ThreadID Integer, downlength integer) ");
}

@Override
public void Onupgrade (sqlitedatabase db, int oldversion, int newversion) {
Db.execsql ("DROP TABLE IF EXISTS filedownlog");
OnCreate (DB);
}
}

Code in Fileservice:

The code is as follows Copy Code

Package com.android.service;
Import Java.util.HashMap;
Import Java.util.Map;

Import Android.content.Context;
Import Android.database.Cursor;
Import Android.database.sqlite.SQLiteDatabase;

public class Fileservice {
Private Dbopenhelper Openhelper;

Public Fileservice {


Openhelper = new Dbopenhelper (context);


}





/**


* Get the length of the file that each thread has already downloaded


* @param path


* @return


*/


Public Map&lt;integer, integer&gt; getData (String path) {


Sqlitedatabase db = Openhelper.getreadabledatabase ();


Cursor Cursor = Db.rawquery ("Select ThreadID, downlength from Filedownlog where downpath=?", New String[]{path});


Map&lt;integer, integer&gt; data = new Hashmap&lt;integer, integer&gt; ();





while (Cursor.movetonext ()) {


Data.put (cursor.getint (0), Cursor.getint (1));


}





Cursor.close ();


Db.close ();


return data;


}





/**


* Save the file length that each thread has already downloaded


* @param path


* @param map


*/


public void Save (String path, Map&lt;integer, integer&gt; Map) {//int ThreadID, int position


Sqlitedatabase db = Openhelper.getwritabledatabase ();


Db.begintransaction ();





try{


For (Map.entry&lt;integer, integer&gt; entry:map.entrySet ()) {


Db.execsql ("INSERT into Filedownlog (Downpath, ThreadID, Downlength) VALUES (?,?,?)",


New Object[]{path, Entry.getkey (), Entry.getvalue ()});


}


Db.settransactionsuccessful ();


}finally{


Db.endtransaction ();


}





Db.close ();


}





/**


* Real-time update of each thread has been downloaded file length


* @param path


* @param map


*/


public void Update (String path, Map&lt;integer, integer&gt; Map) {


Sqlitedatabase db = Openhelper.getwritabledatabase ();


Db.begintransaction ();





try{


For (Map.entry&lt;integer, integer&gt; entry:map.entrySet ()) {


Db.execsql ("Update filedownlog set downlength=?") where downpath=? And threadid=? ",


New Object[]{entry.getvalue (), Path, Entry.getkey ()});


}





Db.settransactionsuccessful ();


}finally{


Db.endtransaction ();


}





Db.close ();


}





/**


* Delete the corresponding download record when the file download is complete


* @param path


*/


public void Delete (String path) {


Sqlitedatabase db = Openhelper.getwritabledatabase ();


Db.execsql ("Delete from Filedownlog where downpath=?", New Object[]{path});


Db.close ();


}


}


Code in Downloadprogresslistener:

Package com.android.network;
Public interface Downloadprogresslistener {
public void ondownloadsize (int size);
}


Code in Filedownloader:

The code is as follows Copy Code

Package com.android.network;
Import Java.io.File;
Import Java.io.RandomAccessFile;
Import java.net.HttpURLConnection;
Import Java.net.URL;
Import Java.util.LinkedHashMap;
Import Java.util.Map;
Import Java.util.UUID;
Import Java.util.concurrent.ConcurrentHashMap;
Import Java.util.regex.Matcher;
Import Java.util.regex.Pattern;

Import Com.android.service.FileService;

Import Android.content.Context;
Import Android.util.Log;

public class Filedownloader {


private static final String TAG = "Filedownloader";


private context;


Private Fileservice Fileservice;





/* Downloaded file length * *


private int downloadsize = 0;





/* Original File length * *


private int fileSize = 0;





/* Thread Count * *


Private downloadthread[] threads;





/* Local Save File * *


Private File SaveFile;





/* Cache the length of each thread download * *


Private Map&lt;integer, integer&gt; data = new Concurrenthashmap&lt;integer, integer&gt; ();





* * The length of each thread download


private int block;





/* Download Path/*


Private String DownloadURL;





/**


* Get the number of threads


*/


public int getthreadsize () {


return threads.length;


}





/**


* Get File size


* @return


*/


public int GetFileSize () {


return fileSize;


}





/**


* Cumulative Downloaded size


* @param size


*/


protected synchronized void append (int size) {


Downloadsize + = size;


}





/**


* Update the location where the specified thread was last downloaded


* @param threadId Thread ID


* @param pos Final Download location


*/


protected synchronized void Update (int threadId, int pos) {


This.data.put (ThreadId, POS);


This.fileService.update (This.downloadurl, this.data);


}





/**


* Build File Downloader


* @param downloadurl Download path


* @param filesavedir File Save directory


* @param threadnum Download Thread count


*/


Public Filedownloader (context context, String DownloadURL, File filesavedir, int threadnum) {


try {


This.context = context;


This.downloadurl = DownloadURL;


Fileservice = new Fileservice (this.context);


URL url = new URL (this.downloadurl);


if (!filesavedir.exists ()) filesavedir.mkdirs ();


This.threads = new Downloadthread[threadnum];





HttpURLConnection conn = (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; MSIE 8.0; Windows NT 5.2; trident/4.0. NET CLR 1.1.4322;. NET CLR 2.0.50727;. NET CLR 3.0.04506.30;. NET CLR 3.0.4506.2152) ");


Conn.setrequestproperty ("Connection", "keep-alive");


Conn.connect ();


Printresponseheader (conn);





if (Conn.getresponsecode () ==200) {


This.filesize = Conn.getcontentlength ()//Get file size based on response


if (this.filesize &lt;= 0) throw new runtimeexception ("Unkown file Size");





String filename = getfilename (conn);//Get file name


This.savefile = new File (filesavedir, filename);//Build Save files


Map&lt;integer, integer&gt; logdata = Fileservice.getdata (DownloadURL);//Get download record





if (Logdata.size () &gt;0) {//If there is a download record


For (Map.entry&lt;integer, integer&gt; entry:logdata.entrySet ())


Data.put (Entry.getkey (), Entry.getvalue ())//The length of data that each thread has downloaded


}





if (This.data.size () ==this.threads.length) {//below calculates the length of the data that has been downloaded by the thread


for (int i = 0; i &lt; this.threads.length; i++) {


This.downloadsize + + this.data.get (i+1);


}





Print ("Already downloaded length" + this.downloadsize);


}





Calculate the length of data downloaded per thread


This.block = (this.filesize% this.threads.length) ==0? This.filesize/this.threads.length:this.filesize/this.threads.length + 1;


}else{


throw new RuntimeException ("Server no Response");


}


catch (Exception e) {


Print (e.tostring ());


throw new RuntimeException ("Don ' t connection this url");


}


}





/**


* Get filename


* @param Conn


* @return


*/


Private String GetFileName (HttpURLConnection conn) {


String filename = this.downloadUrl.substring (this.downloadUrl.lastIndexOf ('/') + 1);





if (Filename==null | | "". Equals (Filename.trim ()) {//If no file name is obtained


for (int i = 0;; i++) {


String mine = Conn.getheaderfield (i);





if (mine = null) break;





if ("Content-disposition". Equals (Conn.getheaderfieldkey (i). toLowerCase ())) {


Matcher m = Pattern.compile (". *filename= (. *)"). Matcher (Mine.tolowercase ());


if (M.find ()) return M.group (1);


}


}





filename = uuid.randomuuid () + ". tmp";//default fetch a filename


}





return filename;


}





/**


* Start downloading files


* @param listener to monitor the number of downloads, if you do not need to know the number of real-time downloads, you can set to NULL


* @return downloaded file size


* @throws Exception


*/


public int Download (Downloadprogresslistener listener) throws exception{


try {


Randomaccessfile randout = new Randomaccessfile (this.savefile, "RW");


if (this.filesize&gt;0) randout.setlength (this.filesize);


Randout.close ();


URL url = new URL (this.downloadurl);





if (This.data.size ()!= this.threads.length) {


This.data.clear ();





for (int i = 0; i &lt; this.threads.length; i++) {


This.data.put (i+1, 0);//Initialize the data length of 0 that each thread has already downloaded


}


}





for (int i = 0; i &lt; this.threads.length i++) {//Open thread for download


int downlength = This.data.get (i+1);





if (Downlength &lt; This.block &amp;&amp; this.downloadsize&lt;this.filesize) {//Determine if the thread has finished downloading or continue downloading


This.threads[i] = new Downloadthread (this, URL, This.savefile, This.block, This.data.get (i+1), i+1);


This.threads[i].setpriority (7);


This.threads[i].start ();


}else{


This.threads[i] = null;


}


}





This.fileService.save (This.downloadurl, this.data);


Boolean notfinish = true;//Download not complete





while (notfinish) {//loop to determine whether all threads complete the download


Thread.Sleep (900);


Notfinish = false;//assumes full thread download complete





for (int i = 0; i &lt; this.threads.length; i++) {


if (This.threads[i]!= null &amp;&amp;!this.threads[i].isfinish ()) {//If the thread is found to have not finished downloading


Notfinish = true;//Set flag is not complete for download





if (this.threads[i].getdownlength () = = 1) {//If download fails, download again


This.threads[i] = new Downloadthread (this, URL, This.savefile, This.block, This.data.get (i+1), i+1);


This.threads[i].setpriority (7);


This.threads[i].start ();


}


}


}





if (listener!=null) listener.ondownloadsize (this.downloadsize);//notify currently downloaded data length


}





Fileservice.delete (This.downloadurl);


catch (Exception e) {


Print (e.tostring ());


throw new Exception ("File download Fail");


}


return this.downloadsize;


}





/**


* Get HTTP response header field


* @param http


* @return


*/


public static map&lt;string, string&gt; Gethttpresponseheader (httpurlconnection http) {


Map&lt;string, string&gt; header = new linkedhashmap&lt;string, string&gt; ();





for (int i = 0;; i++) {


String mine = Http.getheaderfield (i);


if (mine = null) break;


Header.put (Http.getheaderfieldkey (i), mine);


}





return header;


}





/**


* Print HTTP header fields


* @param http


*/


public static void Printresponseheader (HttpURLConnection http) {


Map&lt;string, string&gt; Header = Gethttpresponseheader (HTTP);





For (map.entry&lt;string, string&gt; entry:header.entrySet ()) {


String key = Entry.getkey ()!=null? Entry.getkey () + ":": "";


Print (key+ entry.getvalue ());


}


}

private static void print (String msg) {
LOG.I (TAG, msg);
}
}


Code in Downloadthread:

The code is as follows Copy Code

Package com.android.network;
Import Java.io.File;
Import Java.io.InputStream;
Import Java.io.RandomAccessFile;
Import java.net.HttpURLConnection;
Import Java.net.URL;

Import Android.util.Log;

public class Downloadthread extends Thread {


private static final String TAG = "Downloadthread";


Private File SaveFile;


Private URL Downurl;


private int block;





/* Download Start position * *


private int threadId =-1;


private int downlength;


Private Boolean finish = false;


Private Filedownloader Downloader;





Public Downloadthread (Filedownloader Downloader, URL Downurl, File savefile, int block, int downlength, int threadId) {


This.downurl = Downurl;


This.savefile = SaveFile;


This.block = Block;


This.downloader = Downloader;


This.threadid = threadId;


This.downlength = Downlength;


}





@Override


public void Run () {


if (Downlength &lt; block) {//Not downloaded complete


try {


HttpURLConnection http = (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");


int startpos = block * (threadId-1) + downlength;//start position


int endpos = block * threadid-1;//End position


Http.setrequestproperty ("Range", "bytes=" + startpos + "-" + endpos);/Set the scope of getting Entity data


Http.setrequestproperty ("User-agent", "mozilla/4.0" (compatible; MSIE 8.0; Windows NT 5.2; trident/4.0. NET CLR 1.1.4322;. NET CLR 2.0.50727;. NET CLR 3.0.04506.30;. NET CLR 3.0.4506.2152) ");


Http.setrequestproperty ("Connection", "keep-alive");





InputStream instream = Http.getinputstream ();


byte[] buffer = new byte[1024];


int offset = 0;


Print ("Thread" + this.threadid + "Start download from position" + startpos);


Randomaccessfile threadfile = new Randomaccessfile (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.append (offset);


}





Threadfile.close ();


Instream.close ();


Print ("Thread" + This.threadid + "Download Finish");


This.finish = true;


catch (Exception e) {


This.downlength =-1;


Print ("Thread" + this.threadid+ ":" + E);


}


}


}





private static void print (String msg) {


LOG.I (TAG, msg);


}





/**


* Whether the download is complete


* @return


*/


public Boolean isfinish () {


return finish;


}





/**


* Content size that has already been downloaded


* @return If the return value is-1, the download failed


*/


Public long getdownlength () {


return downlength;


}


}


Look at the effect on the phone

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.