Android download file (a) download progress & resumable breakpoint

The index

  • Android download file (a) download progress & resumable breakpoint
  • Android download file (2) Single task multithreading concurrency & Breakpoint continuation (to be continued)
  • Android download file (3) Custom progress bar (to be continued)
  • (4) Persistent storage of task information (to be continued)
  • Android download file (5) IPC (to continue)
  • XDownloader (to be continued)

    preface

    I have been in touch with Android development for nearly two years. I have been standing on the shoulders of giants all the way. I am really grateful to those who have made contributions to the open source world. Having said that, it doesn’t feel right to be developing for so long using someone else’s work, so I decided to write a few articles to share my understanding of Android download files, and finally integrate and open source a framework, which is a little summary of my Android journey.

    Note: MY ability is limited, if there are mistakes, unreasonable, can be optimized, please be sure to inform me!Copy the code

    Implementation effect

    This section describes how to obtain the download progress and resumable files on Android. The results are as follows

    Knowledge needed

  • volatile
  • RandomAccessFile
  • HttpURLConnection
  • Handler

    volatile

    Volatile is the Java keyword that modifies variables. We’ll focus on its properties here and use it later.

    For details, see 12.3.3 Special Rules for Volatile Variables in Understanding the Java VIRTUAL Machine

1. Ensure visibility According to the JVM memory model, the JVM divides memory into main memory and working memory, and all variables are stored in main memory. Each thread has its own working memory, which stores a copy of some variables in main memory. The operation of the thread on the variables must be completed in the working memory, and then updated to main memory. When a shared variable is volatile, it guarantees that the changed value is immediately updated to main memory, where the new value will be read by other threads accessing it. Common shared variables do not guarantee visibility, because it is uncertain when a common shared variable will be written to main memory after modification. When another thread attempts to read a common shared variable, the same old value may still be in main memory, so visibility cannot be guaranteed.

2. Disable instruction reordering The JVM optimizes the order in which instructions are executed when code is compiled, but volatile does not, as shown below

//flag is volatile x = 2; //flag is volatile x = 2; // statement 1 y = 0; // statement 2 flag =true; // statement 3 x = 4; // statement 4 y = -1; 5 / / statementCopy the code

Statement 3 must be executed after statement 1/2, but the order of statement 1/2 is not guaranteed. Similarly, statement 3 must be executed before statement 4/5, and the order of statement 4/5 is not guaranteed.

3. Non-atomic volatile variables are not guaranteed atomicity, but it is important to note that the volatile keyword guarantees atomicity for long/double get/set operations, as described here.

HttpURLConnection

Android basic network request class, this need not say more, contact Android development students will also understand, if it is Android new students please point me. As for why I use HttpURLConnection instead of OKhttp or Retrofit, it’s because eventually I’m going to open source an Android framework for downloading files, so I don’t have too many external dependencies.

RandomAccessFile

This class is very special, although it is under the java. IO package, but only implements the DataOutput, DataInput, Closeable interface, the only parent class is Object. Its function is to read and write files randomly, in other words, it can be read or written anywhere in a file. In this paper, it is used to realize file download resumable breakpoint.

Handler

Android development will inevitably involve things, new students please point me.

1. If you want to download the file, you need to download the link/path/file name, so we’ll write a JavaBean that uses volatile

public class TaskInfo { private String name; // File name private String path; // File path private String URL; // link private long contentLen; // Total file length /** * So far Java virtual machines have used 32-bit atomic operations, while long and double are 64-bit. When a thread * reads a long/double variable into a register, it requires two 32-bit operations. If the first 32-bit operation * changes the value of the variable, The result is an error. In short, long/double is a non-thread-safe, volatile * -modified long/double get/setThe method is atomic. */ private volatile long completedLen; // The length getter/setter is omittedCopy the code

2. The file download needs to be carried out in the child thread, so we write a class to implement the Runnable interface, convenient task creation

public class DownloadRunnable implements Runnable { private TaskInfo info; JavaBean private Boolean isStop; Public DownloadRunnable(TaskInfo info) {this.info = info; } /** * stop downloading */ public voidstop() {
        isStop = true; } /** * Override public void Override public void Overriderun() { HttpURLConnection conn; // HTTP connection object BufferedInputStream bis; // Buffer the input stream and get RandomAccessFile raf from the server; Int len = 0; int len = 0; Byte [] buffer = new byte[1024 * 8]; Try {// Instantiate File File File = new File(info.getPath() + info.getName())); Raf = new RandomAccessFile(file,"rwd"); conn = (HttpURLConnection) new URL(info.getUrl()).openConnection(); conn.setConnectTimeout(120000); Conn. setReadTimeout(120000); // Read timeout conn.setrequestMethod ("GET"); // Request type is GETifInfo.setcontentlen (long.parselong (conn.getheaderfield ()) == 0) {// If the file length is 0, the task needs to download from the beginning."content-length")));
            } else{// Otherwise set request properties, request scope of the file stream conn.setrequestProperty ("Range"."bytes=" + info.getCompletedLen() + "-"+ info.getContentLen()); } raf.seek(info.getCompletedLen()); // Move RandomAccessFile to the position where it was written, starting with conn.connect(); // bis = new BufferedInputStream(conn.getinputStream ()); // Gets the input stream and wraps it as a buffer stream // reads an array of bytes from the stream into a bufferwhile(! isStop && -1 ! = (len = bis.read(buffer))) {// Write the byte array to the file raf.write(buffer, 0, len); // Update the completed file length attribute info.setCompletedLen(info.getCompletedLen() + len) in the task information; }ifLog.i(len == -1) {log.i (len == -1) {"tag"."Download done");
            } else{// Stop log. I ("tag"."Downloading stopped.");
            }
        } catch (IOException e) {
            e.printStackTrace();
            Log.i("tag",e.toString()); }}}Copy the code

3. Task start/stop and schedule callback

public class MainActivity3 extends AppCompatActivity { private ProgressBar bar; // Progress bar private TaskInfo info; // Task information private DownloadRunnable runnable; Private Handler Handler = new Handler(new Handler).Callback() {@ Override public Boolean handleMessage (Message MSG) {/ / using Handler to make a 200 milliseconds for periodic cycle Handler. SendEmptyMessageDelayed (1, 200); Int l = (int) ((float) info.getCompletedLen() / (float) info.getContentLen() * 100); // Set the progress bar to bar.setProgress(l);if(l > = 100) {/ / when progress > = 100, cancel the Handler cycle Handler. RemoveCallbacksAndMessages (null); }return true; }}); @Override protected voidonDestroy() {// Remove the callback and MSG when the Activity is destroyed and empty it to prevent memory leaksif(handler ! = null){ handler.removeCallbacksAndMessages(null); handler = null; } super.onDestroy(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);setContentView(R.layout.activity_main3); // Instantiate task information object info = new TaskInfo("aa.apk"
                , Environment.getExternalStorageDirectory().getAbsolutePath() 
                + "/Download/"
                , "https://download.alicdn.com/wireless/taobao4android/latest/702757.apk"); bar = (ProgressBar) findViewById(R.id.bar); // Set the maximum progress bar bar.setmax (100); } @param view */ public void start(view view) {// Create a download task runnable = new DownloadRunnable(info); New Thread(runnable).start(); / / Handler cycle Handler. SendEmptyMessageDelayed (1, 200); } @param view */ public void stop(view view) {// Call the stop method DownloadRunnable to stop downloading runnable.stop(); runnable = null; // if the object is not in use, manually null it}}Copy the code

Q: Why doesn’t handler send progress information to the main thread instead of getting progress directly from TaskInfo in main memory? A: A single thread task indeed can be used to download information handler for thread, but after we will involve the multi-thread download, a download task can even reach 128 concurrent thread, so many children threads “and” to give a message to the main thread, the main thread too much stress can cause “frame”, that is what we call the card, All attributes in TaskInfo are atomic and there are no thread-safety issues.

Q: Does Handler not leak memory if it is non-static? A: No, the memory leak is caused by Message holding Handler, which holds the Activity, creating A message-handler-activity reference chain, which cannot be collected by GC when the Activity is destroyed. However, unprocessed messages are removed when the Activity is destroyed, thus resolving the memory leak at its source.

Afterword.

Again, my ability is limited, it is inevitable that there are gaps or omissions in the knowledge, if there is any deficiency, please let me know! I will continue to update in my spare time. Thank you for reading.