Android uses AsyncTask to realize breakpoint continuous transmission

  • 2021-09-11 21:25:11
  • OfStack

Before the company project download modules are used to provide xUtils, recently looked at the source of xUtils, it is also using AsyncTask to perform asynchronous tasks, its download also includes the breakpoint continued transmission function. Here, I also use AsyncTask to realize the function of simple breakpoint continuous transmission.

First of all, let's talk about AsyncTask. First, let's look at the definition of AsyncTask:


 public abstract class AsyncTask<Params, Progress, Result> 

The three generic types respectively represent "input parameters for starting task execution", "progress of background task execution" and "type of background calculation result". In certain situations, not all types are used, and if not, the type java. lang. Void can be used instead.

The execution of 1 asynchronous task generally includes the following steps:

1. execute (Params... params), to execute an asynchronous task, we need to call this method in the code to trigger the execution of the asynchronous task.

2. onPreExecute (), which is executed immediately after execute (Params... params) is called, is generally used to mark UI before performing background tasks.

3. doInBackground (Params... params), which executes immediately after onPreExecute () completes and is used to perform time-consuming operations. This method receives input parameters and returns calculation results. publishProgress (Progress... values) can be called during execution to update progress information.

4. onProgressUpdate (Progress... values). When publishProgress (Progress... values) is called, this method is executed to update the progress information directly to the UI component.

5. onPostExecute (Result result), when the background operation is finished, this method will be called, and the calculation result will be passed to this method as a parameter, and the result will be displayed directly on the UI component.

When using it, there are several points that need special attention:

1. An instance of an asynchronous task must be created in an UI thread.

2. The execute (Params... params) method must be called in the UI thread.

3. Do not manually call the methods onPreExecute (), doInBackground (Params... params), onProgressUpdate (Progress... values), onPostExecute (Result result).

4. You cannot change the information for the UI component in doInBackground (Params... params).

5.1 Task instances can only be executed once, and an exception will be thrown if they are executed the second time.

The following is the code to implement breakpoint continued transmission using AsyncTask:

The idea of breakpoint continuation is actually quite simple. First, judge whether the file to be downloaded exists locally. If it exists, it means that the file has been downloaded for one part. You only need to obtain the current size of the file, that is, the downloaded size, and set it to header of http:


 Header header_size = new BasicHeader("Range", "bytes=" + readedSize + "-");
 request.addHeader(header_size); 

1. Layout file code:


<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:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin"
  android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".AsyncTaskDemoActivity"
  android:orientation="vertical">
  <Button
    android:id="@+id/begin"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text=" Start downloading "/>
  <Button
    android:id="@+id/end"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text=" Pause download "/>
  <ProgressBar
    android:id="@+id/progressbar"
    style="?android:attr/progressBarStyleHorizontal"
    android:layout_width="fill_parent"
    android:layout_height="3dp"
    android:layout_marginTop="10dp"
    android:max="100" />
</LinearLayout>

Layout is relatively simple, on two buttons and a progress bar control, button control download and pause.

2. Breakpoint continuation core code:


package com.bbk.lling.myapplication;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.Toast;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
public class AsyncTaskDemoActivity extends Activity {
  private ProgressBar progressBar;
  // Download path 
  private String downloadPath = Environment.getExternalStorageDirectory() +
      File.separator + "download";
  private DownloadAsyncTask downloadTask;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_async_task_demo);
    progressBar = (ProgressBar) findViewById(R.id.progressbar);
    // Start downloading 
    findViewById(R.id.begin).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        /**
         * 1 A AsyncTask Can only be executed 1 Otherwise, an exception will be thrown 
         * java.lang.IllegalStateException: Cannot execute task: the task is already running.
         *  If you want to restart the task, recreate it AsyncTask Object 
         */
        if(downloadTask != null && !downloadTask.isCancelled()) {
          return;
        }
        downloadTask = new DownloadAsyncTask("http://bbk-lewen.u.qiniudn.com/3d5b1a2c-4986-4e4a-a626-b504a36e600a.flv");
        downloadTask.execute();
      }
    });
    // Pause download 
    findViewById(R.id.end).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        if(downloadTask != null && downloadTask.getStatus() == AsyncTask.Status.RUNNING) {
          downloadTask.cancel(true);
        }
      }
    });
  }
  /**
   *  Downloaded AsyncTask
   */
  private class DownloadAsyncTask extends AsyncTask<String, Integer, Long> {
    private static final String TAG = "DownloadAsyncTask";
    private String mUrl;
    public DownloadAsyncTask(String url) {
      this.mUrl = url;
    }
    @Override
    protected Long doInBackground(String... params) {
      Log.i(TAG, "downloading");
      if(mUrl == null) {
        return null;
      }
      HttpClient client = new DefaultHttpClient();
      HttpGet request = new HttpGet(mUrl);
      HttpResponse response = null;
      InputStream is = null;
      RandomAccessFile fos = null;
      OutputStream output = null;
      try {
        // Create a storage folder 
        File dir = new File(downloadPath);
        if(!dir.exists()) {
          dir.mkdir();
        }
        // Local file 
        File file = new File(downloadPath + File.separator + mUrl.substring(mUrl.lastIndexOf("/") + 1));
        if(!file.exists()){
          // Create a file output stream 
          output = new FileOutputStream(file);
          // Get the download input stream 
          response = client.execute(request);
          is = response.getEntity().getContent();
          // Write to local 
          file.createNewFile();
          byte buffer [] = new byte[1024];
          int inputSize = -1;
          // Gets the total file size used to calculate progress 
          long total = response.getEntity().getContentLength();
          int count = 0; // Downloaded size 
          while((inputSize = is.read(buffer)) != -1) {
            output.write(buffer, 0, inputSize);
            count += inputSize;
            // Update progress 
            this.publishProgress((int) ((count / (float) total) * 100));
            //1 Exit the loop once the task is cancelled, otherwise 1 Execute straight until the end 
            if(isCancelled()) {
              output.flush();
              return null;
            }
          }
          output.flush();
        } else {
          long readedSize = file.length(); // File size, that is, downloaded size 
          // Set the location of downloaded data XX Byte to XX Byte 
          Header header_size = new BasicHeader("Range", "bytes=" + readedSize + "-");
          request.addHeader(header_size);
          // Execute the request to get the download input stream 
          response = client.execute(request);
          is = response.getEntity().getContent();
          // Total file size = Downloaded size + Size not downloaded 
          long total = readedSize + response.getEntity().getContentLength();
          // Create a file output stream 
          fos = new RandomAccessFile(file, "rw");
          // From the file's size Start writing in the future position, but you don't need to write it directly. Sometimes multithreaded downloads require 
          fos.seek(readedSize);
          // Used here RandomAccessFile And FileOutputStream Anything, just use FileOutputStream When you want to pass in the first 2 Brother parameter true, Indicates filling from behind 
//          output = new FileOutputStream(file, true);
          byte buffer [] = new byte[1024];
          int inputSize = -1;
          int count = (int)readedSize;
          while((inputSize = is.read(buffer)) != -1) {
            fos.write(buffer, 0, inputSize);
//            output.write(buffer, 0, inputSize);
            count += inputSize;
            this.publishProgress((int) ((count / (float) total) * 100));
            if(isCancelled()) {
//              output.flush();
              return null;
            }
          }
//          output.flush();
        }
      } catch (MalformedURLException e) {
        Log.e(TAG, e.getMessage());
      } catch (IOException e) {
        Log.e(TAG, e.getMessage());
      } finally{
        try{
          if(is != null) {
            is.close();
          }
          if(output != null) {
            output.close();
          }
          if(fos != null) {
            fos.close();
          }
        } catch(Exception e) {
          e.printStackTrace();
        }
      }
      return null;
    }
    @Override
    protected void onPreExecute() {
      Log.i(TAG, "download begin ");
      Toast.makeText(AsyncTaskDemoActivity.this, " Start downloading ", Toast.LENGTH_SHORT).show();
      super.onPreExecute();
    }
    @Override
    protected void onProgressUpdate(Integer... values) {
      super.onProgressUpdate(values);
      Log.i(TAG, "downloading " + values[0]);
      // Update Interface Progress Bar 
      progressBar.setProgress(values[0]);
    }
    @Override
    protected void onPostExecute(Long aLong) {
      Log.i(TAG, "download success " + aLong);
      Toast.makeText(AsyncTaskDemoActivity.this, " Download End ", Toast.LENGTH_SHORT).show();
      super.onPostExecute(aLong);
    }
  }
}

Such a simple breakpoint continued transmission function is realized, which is only for a single thread. If multiple threads are involved in downloading at the same time, it is much more complicated, so we will ponder it later.

Source download: https://github.com/liuling07/MultiTaskAndThreadDownload

Summarize


Related articles: