Android Implementation Network Multithreading breakpoint Download Instance _android

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

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?

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 ()//Gets 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.
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:

 <?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 not present or write protection </string>

  <string name= "Success" > Download complete </string>

  < String name= "error" > download failed </string>

</resources>

Main.xml code in File:

 &lt;?xml version= "1.0" encoding= "Utf-8"?&gt; &lt;linearlayout xmlns:android= "http://schemas.android.com/apk/res/" Android "android:orientation=" vertical "android:layout_width=" fill_parent "android:layout_height=" Fill_parent " &gt; &lt;!--download path--&gt; &lt;textview android:layout_width= "Fill_parent android:layout_height=" Wrap_conten T "android:text=" @string/path "/&gt; &lt;edittext android:id=" @+id/path "android:text=" Http://www.winra

  R.com.cn/download/wrar380sc.exe "android:layout_width=" fill_parent "android:layout_height=" Wrap_content "&gt; &lt;/EditText&gt; &lt;!--download button--&gt; &lt;button android:layout_width= "Wrap_content" Android:layout_heig ht= "Wrap_content" android:text= "@string/downloadbutton" android:id= "@+id/button"/&gt; &lt;!--progress bar--&gt; & Lt ProgressBar android:layout_width= "fill_parent" android:layout_height= "20dip" style= "ANDROID:ATTR/PROGRESSB"? Arstylehorizontal "android:id="@+id/downloadbar"/&gt; &lt;textview android:layout_width= "fill_parent" android:layout_height= "Wrap_content"

 android:gravity= "center" android:id= "@+id/resultview"/&gt; &lt;/LinearLayout&gt;

Androidmanifest.xml file code:

 &lt;?xml version= "1.0" encoding= "Utf-8" &gt;&lt;manifest xmlns:android= "http://schemas.android.com/apk/res/" Android "package=" Com.android.downloader "android:versioncode=" 1 "android:versionname=" 1.0 "&gt; &lt;uses-sdk Andro id:minsdkversion= "8"/&gt; &lt;application android:icon= "@drawable/icon" android:label= "@string/app_name" &gt; &lt;a Ctivity android:name= ". Mainactivity "android:label=" @string/app_name "&gt; &lt;intent-filter&gt; &lt;action android:name=" a Ndroid.intent.action.MAIN "/&gt; &lt;category android:name=" Android.intent.category.LAUNCHER "/&gt; &lt;/int Ent-filter&gt; &lt;/activity&gt; &lt;/application&gt; &lt;!--Create and delete file permissions in SDcard--&gt; &lt;uses-permission a Ndroid:name= "Android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/&gt; &lt;!--Write data permissions to SDcard--&gt; &lt;uses-permission an Droid:name= "Android.permission.WRITE_EXTERNAL_STORAGE"/&gt; &lt;!--access Internet permissions--&gt; &lt;uses-permission Android : Name= "Android. permission.

 INTERNET "/&gt; &lt;/manifest&gt;

 mainactivity 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 queues * Messages in message queues are processed internally by the current thread/private Handler Handler = new Han        

        Dler () {@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). Sho

        W ();

      } 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 (). To

        String ();

        

        System.out.println (Environment.getexternalstoragestate () + "------" +environment.media_mounted); if (Environment.getexternalstoragestate (). Equals (environment.media_mounted)) {Download (path, environment.getexte

        Rnalstoragedirectory ());

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

  }

      }

    }); /** * main thread (UI thread) * updates to the interface of the display control are only handled by 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 * @p      

      Aram Savedir */private void Download (final String path, final File savedir) {new Thread (new Runnable () { @Override public void Run () {Filedownloader loader = new Filedownloader (Mainactivity.this, Pat

        H, 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 ondownloads

              ize (int size) {//Live to know that the file has been downloaded data length message 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:

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 inte GER primary Key AutoIncrement, Downpath varchar (m), 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:

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 (St

    Ring 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 length of the file 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.GETW

    Ritabledatabase ();

    

    Db.begintransaction (); try{for (Map.entry&lt;integer, integer&gt; entry:map.entrySet ()) {db.execsql (' insert INTO Filedownlog ' (do

      Wnpath, ThreadID, Downlength) VALUES (?,?,?) ", New Object[]{path, Entry.getkey (), Entry.getvalue ()});

    } db.settransactionsuccessful ();

    }finally{db.endtransaction ();

  } db.close (); /** * Real-time update of the file length per thread has been downloaded * @param path * @param map/public void update (String path, map&lt;i

    Nteger, 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 Dow Nlength=? where downpath=? And threadid=? ", New Object[]{entry.getvalue (), Path, Entry.getkey ()});

    } db.settransactionsuccessful ();

    }finally{db.endtransaction ();

  } db.close (); /** * When the file download is complete, delete the corresponding download record * @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:

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 Number * * 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 Download size * @param size */protected synchronized void append (int size) {downloadsize = Siz

  E /** * Update the location where the specified thread was last downloaded * @param threadId thread ID * @param POS last downloaded location * * * protected synchronized void UPDA

    Te (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 number * *  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 (This.file
        Size &lt;= 0) throw new runtimeexception ("Unkown file Size"); String filename = getfilename (conn);//Get file name This.savefile = new file (filesavedir, filename);//Build Save File 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.getv

          Alue ())//The length of the data that has been downloaded by each thread is placed in the IF (This.data.size () ==this.threads.length) {//below calculates the length of the data that has been downloaded by the wire

          for (int i = 0; i &lt; this.threads.length i++) {this.downloadsize + = This.data.get (i+1);
        Print ("Length already downloaded" + this.downloadsize); //Calculate the data length downloaded by each 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");
      The 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 the file name is not 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 (". *filen

          Ame= (. *) "). Matcher (Mine.tolowercase ());

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

  filename = uuid.randomuuid () + ". tmp";//default to take a filename} return filename; /** * Start downloading files * @param listener listening for changes in the number of downloads, if you do not need to know the number of real-time downloads, you can setNULL * @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 length of the data that each thread has downloaded is 0}

        

        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 have completed downloading Thread.Sleep (900); Notfinish = false;//assumes that all threads download complete for (int i = 0; i &lt; this.threads.length; i++) {if (this.th

            

            Reads[i]!= null &amp;&amp;!this.threads[i].isfinish ()) {//If discovery thread has not completed download Notfinish = true;//Setting flag is not completed for download if (this.threads[i].getdownlength () = = 1) {///If download fails, download this.threads[i again] = new Downloa

              Dthread (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; gethttprespons

    

    Eheader (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 field * @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);
 }

}

Downloadthread 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 to 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.set Requestproperty ("Range", "bytes=" + startpos + "-" + endpos);/sets the scope to get Entity Data Http.setrequestproperty ("User-agent", "M ozilla/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); /** * Download is complete *@return * * Public boolean isfinish () {return finish;
  /** * Already downloaded content size * @return If the return value is-1, on behalf of download failed/public long getdownlength () {returns downlength;
 }}

The operation effect is as follows:

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.