Multi-thread breakpoint download in Android and android breakpoint download

Source: Internet
Author: User
Tags file info

Multi-thread breakpoint download in Android and android breakpoint download

First, let's take a look at the principle of multi-threaded download. Multi-threaded download is to divide the original files on the same network into equal parts based on the number of threads, and then download the corresponding part of each separate thread, then, splice the downloaded files in the order of the original files.
Complete file. This greatly improves the File Download efficiency. For file downloads, multi-threaded downloads must be considered.

Multi-threaded download can be roughly divided into the following steps:

1. Get the size of the target file on the server
Obviously, you need to first access the network and obtain the total size of the target file. The purpose is to calculate the download tasks that each thread should allocate.

2. Create a local file of the same size as the original file
You can use RandomAccessFile locally to create a file of the same size as the target file. This api supports read/write operations at any location of the file. This facilitates multi-threaded download. Each thread only needs to write data within the specified start and end scripts.

3. Calculate the start position and end position of each download thread
We can regard the original file as a byte array. Each thread only downloads the part between the specified start position and the specified end position of the "array. In the first step, we know the total length of the "array. Therefore, you only need to know the total number of enabled threads to calculate the download range of each thread. The number of bytes to be downloaded by each thread (blockSize) = Total number of bytes (totalSize)/number of threads (threadCount ). Assume that the thread is numbered in the order of 0, 1, 2, 3... n, the range of files downloaded by the n thread is:
Start pin startIndex = n * blockSize.
End Script endIndex = (n-1) * blockSize-1.
Considering totalSize/threadCount may not be divisible, so the last thread should be specially processed, the formula for calculating the start script of the last thread remains unchanged, but the end script is labeled as endIndex = totalSize-1.
4. enable multiple sub-threads to start downloading
Implement read stream operations in subthreads and read conn. getInputStream () to RandomAccessFile.
5. Record the download progress
Creates a temporary file for each separate thread to record the download progress of the thread. For a single thread, each part of data downloaded records the number of bytes currently downloaded in a local file. In this case, if the download task ends abnormally, the next time you start the download, you can continue with the previous download progress.
6. Delete temporary files
After multiple threads are downloaded, the last downloaded thread deletes all temporary files.

Android has an interface to interact well with users. On the interface, you can enter the original file address and number of threads, and then click OK to start downloading. In order to allow users to clearly see the download progress of each thread, an equivalent progress bar (ProgressBar) is generated dynamically based on the number of threads ). ProgressBar is a progress bar control that displays the progress of a task. There are two styles, one of which is circular, which is the default style. Because the specific progress value cannot be displayed, it is suitable for situations where you are not sure how long to wait; the other is a long bar, which has two colors. The highlighted color indicates the overall progress of the task. For our download task, the total task (the number of bytes to download) is clear, and the current task (the number of bytes already downloaded) is clear, therefore, the latter is especially suitable for use. In our requirements, the ProgressBar needs to be dynamically added based on the number of threads and must be a long bar. Therefore, you can write the ProgressBar style in the layout file in advance. Fill in the layout when necessary. The max attribute of ProgressBar represents its maximum scale value, and the progress attribute represents the current progress value. The usage is as follows:
ProgressBar. setMax (int max); sets the maximum scale value.
ProgressBar. setProgress (int progress); sets the current progress value.
You can set the maximum scale value and the modification progress value for ProgressBar to operate in the Child thread. It has been specially processed internally, so you do not need to send a Message via handler to let the main thread modify the progress.

The following describes the multithreading In the android environment we have written.

The multi-thread download interface is displayed as follows. The three progress bars indicate the download progress of the three sub-threads respectively.

<LinearLayout xmlns: android = "http://schemas.android.com/apk/res/android" xmlns: tools = "http://schemas.android.com/tools" android: layout_width = "match_parent" android: layout_height = "match_parent" android: orientation = "vertical" android: paddingBottom = "@ dimen/activity_vertical_margin" android: paddingLeft = "@ dimen/activity_horizontal_margin" android: paddingRight = "@ dimen/activity_horizontal_margin" android: PaddingTop = "@ dimen/activity_vertical_margin" tools: context = ". mainActivity "> <EditText android: id =" @ + id/et_path "android: layout_width =" match_parent "android: layout_height =" wrap_content "android: hint = "Enter the file resource path to download" android: text = "http: // 192.168.1.104: 8080/gg.exe"/> <Button android: layout_width = "match_parent" android: layout_height = "wrap_content" android: onClick = "download" android: text = "download"/> <Prog RessBar android: id = "@ + id/pb0" style = "? Android: attr/progressBarStyleHorizontal "android: layout_width =" match_parent "android: layout_height =" wrap_content "/> <ProgressBar android: id =" @ + id/pb1 "style = "? Android: attr/progressBarStyleHorizontal "android: layout_width =" match_parent "android: layout_height =" wrap_content "/> <ProgressBar android: id =" @ + id/k2" style = "? Android: attr/progressBarStyleHorizontal "android: layout_width =" match_parent "android: layout_height =" wrap_content "/> </LinearLayout>

 

The internal logic of multi-threaded download is as follows. In fact, this is already at the beginning, but it is the implementation of the Code.

Public class MainActivity extends Activity {private EditText et_path; private ProgressBar pb0; private ProgressBar pb1; private ProgressBar k22; /*** enable several threads to download data from the server */public static int threadCount = 3; public static int runningThreadCount; private String path; @ Override protected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); // Initialize the control et_path = (EditText) findViewById (R. id. et_path); pb0 = (ProgressBar) findViewById (R. id. pb0); pb1 = (ProgressBar) findViewById (R. id. pb1); PBS = (ProgressBar) findViewById (R. id. master);} // click the download button to download the event public void download (View view) {path = et_path.getText (). toString (). trim (); if (TextUtils. isEmpty (path) | (! Path. startsWith ("http: //") {Toast. makeText (this, "sorry the path is invalid", 0 ). show (); return;} new Thread () {public void run () {try {// 1. obtain the size of the target file on the server. URL = new url (path); HttpURLConnection conn = (HttpURLConnection) URL. openConnection (); conn. setConnectTimeout (5000); conn. setRequestMethod ("GET"); int code = conn. getResponseCode (); if (code = 200) {int length = conn. getContentLength (); System. out. pr Intln ("the length of the Server File is:" + length); // 2. create a file named RandomAccessFile raf = new RandomAccessFile (Environment. getExternalStorageDirectory (). getAbsolutePath () + "/" + getFileName (path), "rw"); raf. setLength (length); raf. close (); // 3. calculate the start position and end position of each thread download. int blocksize = length/threadCount; runningThreadCount = threadCount; for (int threadId = 0; threadId <threadCount; threadId ++) {int startIndex = thr EadId * blocksize; int endIndex = (threadId + 1) * blocksize-1; if (threadId = (threadCount-1) {endIndex = length-1;} // 4. start Multiple Sub-threads to download new DownloadThread (threadId, startIndex, endIndex ). start () ;}} catch (Exception e) {e. printStackTrace ();}};}. start ();} private class DownloadThread extends Thread {/*** Thread id */private int threadId;/*** theoretical start position of Thread download */private int startIndex; /* ** End position of thread download */private int endIndex;/*** the location where the current thread downloads the file. */private int currentPosition; public DownloadThread (int threadId, int startIndex, int endIndex) {this. threadId = threadId; this. startIndex = startIndex; this. endIndex = endIndex; System. out. the download range of println (threadId + ":" + startIndex + "~~ "+ EndIndex); currentPosition = startIndex;} @ Override public void run () {try {URL url = new URL (path); HttpURLConnection conn = (HttpURLConnection) url. openConnection (); // check whether a part of data has been downloaded by the current thread. File info = new File (Environment. getExternalStorageDirectory (). getAbsolutePath () + "/" + threadId + ". position "); RandomAccessFile raf = new RandomAccessFile (Environment. getExternalStorageDirectory (). getAbsol UtePath () + "/" + getFileName (path), "rw"); if (info. exists () & info. length ()> 0) {FileInputStream FCM = new FileInputStream (info); BufferedReader br = new BufferedReader (new InputStreamReader (FCM); currentPosition = Integer. valueOf (br. readLine (); conn. setRequestProperty ("Range", "bytes =" + currentPosition + "-" + endIndex); System. out. println ("the original download progress exists. Continue to download from the last terminated location" + "bytes =" + currentPosition + "-" + endIndex); f Is. close (); raf. seek (currentPosition); // the start position of each thread to write a file is different .} else {// tell the server to download only part of the resource conn. setRequestProperty ("Range", "bytes =" + startIndex + "-" + endIndex); System. out. println ("No download progress, new download" + "bytes =" + startIndex + "-" + endIndex); raf. seek (startIndex); // the start position of each thread to write files is different .} inputStream is = conn. getInputStream (); byte [] buffer = new byte [1024]; int len =-1; while (len = is. read (buffer ))! =-1) {// put the data downloaded by each thread in its own space. // System. out. println ("thread:" + threadId + "downloading:" + new String (buffer); raf. write (buffer, 0, len); // 5. record the download progress currentPosition + = len; File file = new File (Environment. getExternalStorageDirectory (). getAbsolutePath () + "/" + threadId + ". position "); RandomAccessFile fos = new RandomAccessFile (file," rwd "); // System. out. println ("thread:" + threadId + "written to" + currentPosition); fos. write (String. valueOf (currentPosition ). getBytes (); fos. close (); // fileoutstream data is not necessarily written to the underlying device, and may be stored in the cache. // In the rwd mode of raf, data is immediately stored in the underlying hard disk device. // update progress bar display int max = endIndex-startIndex; int progress = currentPosition-startIndex; if (threadId = 0) {pb0.setMax (max); pb0.setProgress (progress );} else if (threadId = 1) {pb1.setMax (max); pb1.setProgress (progress);} else if (threadId = 2) {pb2.setMax (max ); pb2.setProgress (progress) ;}} raf. close (); is. close (); System. out. println ("thread:" + threadId + "download completed... "); File f = new File (Environment. getExternalStorageDirectory (). getAbsolutePath () + "/" + threadId + ". position "); f. renameTo (new File (Environment. getExternalStorageDirectory (). getAbsolutePath () + "/" + threadId + ". position. finish "); synchronized (MainActivity. class) {runningThreadCount --; // 6. delete a temporary File if (runningThreadCount <= 0) {for (int I = 0; I <threadCount; I ++) {File ft = new File (Environment. getExternalStorageDirectory (). getAbsolutePath () + "/" + I + ". position. finish "); ft. delete () ;}}} catch (Exception e) {e. printStackTrace () ;}}/ *** get a file name * @ param path * @ return */public String getFileName (String path) {int start = path. lastIndexOf ("/") + 1; return path. substring (start );}}

Finally, do not forget to add permissions. In this project, not only network access but also sdcard storage are used, so you need to add two permissions.

<uses-permission android:name="android.permission.INTERNET"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

In addition, xUtils can also implement multi-threaded download. XUtils is an open-source free Android toolkit and its code is hosted on github. At present, xUtils mainly has four modules: DbUtils module, which is mainly used to operate the database framework. The ViewUtils module allows you to manage the UI, resources, and event binding through annotations. The HttpUtils module provides convenient network access and resumable data transfer functions. The BitmapUtils module provides powerful image processing tools. Here we only use the HttpUtils tool in the xUtils tool. Third-party packages are easy to use. Copy the xUtils jar package to the libs directory and add the dependency.

Next we can use the httpUtils function in xUtils:

HttpUtils http = new HttpUtils ();/*** parameter 1: original file network address * parameter 2: locally saved address * parameter 3: whether resumable data transfer is supported; true: yes. false: * parameter 4 is not supported. All methods in this interface are called in the main thread. * That Is, You can modify the UI for methods in this interface */http. download (path, "/mnt/sdcard/xxx.exe", true, new RequestCallBack <File> () {// call @ Override public void onSuccess (ResponseInfo <File> arg0) {Toast. makeText (MainActivity. this, "Download successful", 0 ). show ();}/*** is called every time a part is downloaded. You can use this method to know the current download progress * parameter 1: Total Bytes of the original file * parameter 2: number of bytes downloaded currently * parameter 3: whether to upload. For download, this value is false */@ Override public void onLoading (long total, long current, boolean isUploading) {pb0.setMax (int) total); pb0.setProgress (int) current); super. onLoading (total, current, isUploading);} // call once after failure @ Override public void onFailure (HttpException arg0, String arg1) {Toast. makeText (MainActivity. this, "Download failed" + arg1, 0 ). show ();}});

 

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.