Android class FileDownloadList analysis

  • 2020-06-03 08:17:40
  • OfStack

Code first, then analyze


public class FileDownloadList {
 
 /** context */
 private Context mContext;
 /** The request object */
 private BaseRequestLims fileRequest = null;
 /** Progress bar dialog box */
 private AlertDialog progressDialog = null;
 /** Progress bar control variables */
 private ProgressBar mProgress;
 /** Percentage display control */
 private TextView mProgressPercent;
 
 private File localFile = null;
 /** receive HttpHelper , the broadcast sent after getting the file size, determines the file size */
 private DownLoadReceiver receiver;
 /** The file size */
 private long fileLength = -1L;
 /** Whether the broadcast mark has been registered */
 private boolean castFlag = false;
 /** Whether to display a progress bar flag */
 private boolean showDialog = false;
 /** The callback interface after the file is downloaded */
 private Runnable mCallback = null;
 
 private Handler mHandler = new Handler(){
  @Override
  public void handleMessage(Message msg) {
   super.handleMessage(msg);
   int tempSize = (int)localFile.length();
   if(tempSize < fileLength){
// File download 
        if(showDialog){
// Shows the progress bar case , Update progress bar 
          int progress = (int)((Double.valueOf(tempSize) / Double.valueOf(fileLength)) * 100);
          mProgress.setProgress(tempSize);
          mProgressPercent.setText(progress + "%");
        }
      }else{
// Download the file 
    if(castFlag){// If registered, cancel the broadcast 
      mContext.unregisterReceiver(receiver);
      castFlag = false;  
    }
    if(showDialog){
     mProgress.setProgress((int)fileLength);
     mProgressPercent.setText("100%");
     progressDialog.dismiss();
    }
          
    if(mCallback != null){
     try{
      Thread.sleep(500);
      mCallback.run();
     }catch (Exception e) {
      e.printStackTrace();
     }
    }
   }
  }
 };
 
 /**
  *  The constructor 
  * @param activity
  */
 
 /**
  *  The constructor 
  * @param activity
  * @param showDialog  Displays the progress bar flag 
  */
 public FileDownloadList(Context context, boolean showDialog){
  mContext = context;
  this.showDialog = showDialog;
  fileRequest = new BaseRequestLims(context,ClientServiceType.FILE_DOWN);
  fileRequest.setMethodType(BaseRequestLims.METHOD_TYPE_POST);
  fileRequest.setContext(mContext);
    
 }
  public BaseRequestLims getFileRequest(){
  return fileRequest;
 }
 
 /**
  *  Download the file by associating the type 
  * @param fileName  File name or file relative path plus name on the server 
  * @param saveDir  Save to the local file directory 
  * @param saveName  The name of the file saved locally 
  * @param gllx  Association types 
  * @param callback  After downloading the processing thread 
  */
 public void downloadFile(String fileName, String saveDir, String saveName, Runnable callback){
  if(callback != null){
   mCallback = callback;
  }
  
  File saveDirFile = new File(saveDir);
  
  //judge the save dir path exist or not 
  if(!saveDirFile.exists()){
   saveDirFile.mkdirs();
  }
  localFile = new File(saveDir,saveName);
  
  if(localFile.isDirectory()){
   new AlertDialog.Builder(mContext).setTitle(" prompt ").setMessage("the save file is directory").show();
   return;
  }
  if(fileRequest.getServiceType()==null){
   fileRequest.setServiceType(ClientServiceType.FILE_DOWN);
  }
  fileRequest.addParameter("fpath", fileName);
  fileRequest.addParameter("fname", saveName);
  fileRequest.setStreamPath(localFile.getAbsolutePath());
  fileRequest.setStream(true);
  if(localFile.exists()){
   if(localFile.length() == 0){
    invokeFile(fileRequest);
   }else{
    // File exists directly open 
    if(showDialog)
     buildProgressDialog().show();
    mHandler.sendMessage(mHandler.obtainMessage());
   }
  }else{
   invokeFile(fileRequest);
  }
 }
 
 /**
  *  Enter the file download child thread 
  * @param request
  */
 private void invokeFile(final BaseRequestLims request){
  try{
   if(showDialog){
    buildProgressDialog().show();
   }
   receiver = new DownLoadReceiver();
   IntentFilter filter = new IntentFilter();
   filter.addAction("SAVE_DOWNLOAD_FILE");
   mContext.registerReceiver(receiver, filter);
   castFlag = true;
   // Downloaded child thread 
   new Thread(){
    @Override
    public void run() {
     super.run();
     HttpHelper.invoke(request);
    }
   }.start();
  }catch (Exception e) {
   e.printStackTrace();
  }
 }
 
 /**
  *  Create a progress dialog 
  * @return
  */
 private AlertDialog buildProgressDialog(){
  AlertDialog.Builder builder = new Builder(mContext);
  builder.setTitle(" Downloading file , Please wait for a while ...");
  RelativeLayout container = new RelativeLayout(mContext);
  mProgress = new ProgressBar(mContext);
  mProgress.setId("progress".hashCode());
  BeanUtils.setFieldValue(mProgress, "mOnlyIndeterminate", Boolean.valueOf(false));
  mProgress.setIndeterminate(false);
  LayerDrawable layerDrawable = (LayerDrawable)mContext.getResources().getDrawable(android.R.drawable.progress_horizontal);
  ClipDrawable clipDrawable = (ClipDrawable)layerDrawable.getDrawable(2);
  clipDrawable.setColorFilter(Color.parseColor("#32B5E5"), Mode.SRC_IN);
  mProgress.setProgressDrawable(layerDrawable);
  mProgress.setPadding(0, 0, 0, 0);
  mProgress.setIndeterminateDrawable(
    mContext.getResources().getDrawable(android.R.drawable.progress_indeterminate_horizontal));
  mProgressPercent = new TextView(mContext);
  mProgressPercent.setId("percent".hashCode());
  mProgressPercent.setText("0%");
  mProgressPercent.setTextSize(18);
  
  int containerPadding = DimensionUtils.dip2Px(mContext, 10);
  container.setPadding(containerPadding, containerPadding, containerPadding, containerPadding);
  
  LayoutParams progressLayoutParams = new LayoutParams(
    LayoutParams.MATCH_PARENT, DimensionUtils.dip2Px(mContext, 4));
  progressLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
  progressLayoutParams.addRule(RelativeLayout.LEFT_OF, mProgressPercent.getId());
  mProgress.setLayoutParams(progressLayoutParams);
  
  LayoutParams percentLayoutParams = new LayoutParams(
    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
  percentLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
  percentLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
  mProgressPercent.setLayoutParams(percentLayoutParams);
  
  container.addView(mProgressPercent);
  container.addView(mProgress);
  builder.setView(container);
  builder.setNegativeButton(" cancel ", new OnClickListener() { 
   @Override
   public void onClick(DialogInterface dialog, int which) {
    dialog.dismiss();
   }
  });
  
  progressDialog = builder.create();
  return progressDialog;
 }
 
 class DownLoadReceiver extends BroadcastReceiver{
  @Override
  public void onReceive(Context context, Intent intent) {
   // Show progress bar 
   fileLength = intent.getLongExtra("FILE_LENGTH", -1);
   if(showDialog){
    mProgress.setMax((int)fileLength);
   }
   // The thread that updates the progress bar 
   new Thread(){
    @Override
    public void run() {
     super.run();
     while(true){
      try{
       Thread.sleep(500);
      }catch (Exception e) {
       e.printStackTrace();
      }
      mHandler.sendMessage(mHandler.obtainMessage());
      // Gets the size of the download file 
      int loadedSize = (int)localFile.length();
      if(loadedSize >= fileLength){
       break;
      }
     }
    }
   }.start();
  }
 }
 
 public DownLoadReceiver getReciver()
 {
  return receiver;
 }
}

Its logic:

Once you have created an FileDownloadList object, you can use the following method directly to implement the download functionality.


downloadFile(String fileName, String saveDir, String saveName, Runnable callback)

In practice it means:

1. Open the download thread in the current context. When you get the size of the file to download, send a broadcast (not shown in the code above).

2. In the current context, register a broadcast listener to listen for broadcasts identified as SAVE_DOWNLOAD_FILE. After the first broadcast is heard, the first broadcast is sent containing the size of the file to be downloaded, and the size of the local file is detected every 5 milliseconds until the size of the local file (loadedSize) is greater than or equal to the size of the file to be downloaded (fileLength). Exit the loop.

mHandler. sendMessage(ES23en. obtainMessage()); To have the UI thread update the progress bar.

The download thread constantly writes the data stream returned by the server to the local file, so the size of the local file will change until it is the same size as the file to be downloaded, and then it exits the thread that constantly detects the size of the local file.

Other things that are not represented in the above code (in other parts of the code) :

1. In invokeFile(final BaseRequestLims request) method, a download thread is opened as follows, which will write the file stream returned by the server to the local file (localFile); It then sends a broadcast identified as SAVE_DOWNLOAD containing information about the file size of the file to be downloaded fileLength.


// Downloaded child thread 
   new Thread(){
    @Override
    public void run() {
     super.run();
     HttpHelper.invoke(request);
    }
   }.start();

Problems with the above code:

1. Context, using some Activity, if the system calls onDestroy() of this Activity, the download thread has not completed, which means that the size of loadedSize is still smaller than fileLength. Thus, the thread that constantly detects the size of the local file is executing.

The local file size detection thread and the download thread are still executing:

Thread that detects local file size:


new Thread(){
    @Override
    public void run() {
     super.run();
     while(true){
      try{
       Thread.sleep(500);
      }catch (Exception e) {
       e.printStackTrace();
      }
      mHandler.sendMessage(mHandler.obtainMessage());
      // Gets the size of the download file 
      int loadedSize = (int)localFile.length();
      if(loadedSize >= fileLength){
       break;
      }
     }
    }
   }.start();

Download thread:


new Thread(){
        @Override
        public void run() {
          super.run();
          HttpHelper.invoke(request);
        }
      }.start();

So what could go wrong?

1). What I can confirm is that mContext will leak.

2). DownLoadReceiver cannot be cancelled normally.

Analysis, to be continued.


Related articles: