Android Breakpoint Continuous Download File, Continuous Download even when disconnected or disconnected

Keywords: Android network Database REST

I read a lot of articles about Android breakpoint continuation on the Internet. There are various methods, but there are few cases about disconnection and power outage.Because my company does box games, which use an Android set-top box to communicate between Unity3D and the server.When the boss asks to update the APP app, he asks to continue the update instead of downloading it when the wifi disconnects or the box suddenly shuts down and the app is reopened.This problem has been bothering me for a few days and I ended up referencing it http://blog.csdn.net/a1533588867/article/details/53129259 The content of this blog and some modifications to its source code are just what the boss needs.Thank you so much for this blogger!

First, let's talk about the core content, the normal process: when downloading a file, create two files, one is the total file after downloading, one is the temporary file, set the size to be written such as 5M, start a download thread, write to the temporary file with FileOutputStream, pause the download when writing 5M, open a file splicing thread, use RandomAccessFile stitches temporary files into the total file. After stitching, delete the temporary files and continue downloading until the download is complete.

When an exception occurs, such as wifi is disconnected or powered off, HttpURLConnection operates on temporary files before connecting to the network after opening the download thread. If temporary files exist, the download thread is suspended, and the file stitching thread is opened, temporary files are stitched into the total file, after stitching is completed, temporary files are deleted, and the download thread is opened to continueLoad.That's about it.How to initialize threads, how to save progress, how to distribute and accept download progress, how to manage download threads, etc. are all covered in the blog above. I won't go into more detail.

Some core code is posted below for easy understanding.

This is the download thread.

public class DownloadThread extends Thread {

    private FileBean fileBean;
    private ThreadBean threadBean;
    private DownloadCallBack callback;
    private Boolean isPause = false;
    private Context mContext;
    private final int divisionLen = 1024 * 1024 * 5;
    private boolean downloadOver = false;

    public DownloadThread(FileBean fileBean, ThreadBean threadBean, DownloadCallBack callback, Context mContext) {
        this.fileBean = fileBean;
        this.threadBean = threadBean;
        this.callback = callback;
        this.mContext = mContext;
    }

    public void setPause(Boolean pause) {
        isPause = pause;
    }

    @Override
    public void run() {
        HttpURLConnection connection = null;
        FileOutputStream fos = null;
        InputStream inputStream = null;

        //Set Download Start Location
        int start = threadBean.getStart() + threadBean.getFinished();
        String sp_tempFileName = Utils.getShardPreferences(mContext, "tempFileName");
        if (sp_tempFileName != null) {
            File sp_tempFile = new File(Config.downLoadPath,sp_tempFileName);
            //Processing Temporary Files
            if (sp_tempFile.exists() && sp_tempFile.length() <= divisionLen) {
                //Call the pause download method to destroy the download thread
                // Be sure to call the pause method first to destroy the download thread, and then open the file stitching thread.
                //Because temporary files may be 0KB in size when power is cut off, the stitching process will be completed instantly if the thread for stitching files is opened first.
                // So that the download thread may not have been destroyed yet, the download thread is opened, and then the download thread is destroyed
                Utils.pauseDownload(mContext,fileBean);
                new AddFileThread(sp_tempFileName, fileBean, start, (int) sp_tempFile.length(), mContext, false).start();
                threadBean.setFinished((int) (threadBean.getFinished() + sp_tempFile.length()));
                //Modification Progress
                callback.pauseCallBack(threadBean);
                return;
            }
        } 
        try {
            URL url = new URL(threadBean.getUrl());
            connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(10000);
            connection.setRequestMethod("GET");
            connection.setRequestProperty("Range", "bytes=" + start + "-" + threadBean.getEnd());
            //Set Write Location to Write Temporary File
            String tempFileName = System.currentTimeMillis() + "tempFile";
            Utils.setShardPreferences(mContext, "tempFileName", tempFileName);
            File tempFile = new File(Config.downLoadPath, tempFileName);
            fos = new FileOutputStream(tempFile);
            //Start downloading
	    //Here is HTTP_PARTIAL not HTTP_OK
            if (connection.getResponseCode() == HttpURLConnection.HTTP_PARTIAL) {
                inputStream = connection.getInputStream();
                byte[] bytes = new byte[1024];
                int len = -1;
                int subsLen = 0;
                while (subsLen < divisionLen) {
                    len = inputStream.read(bytes);
                    //Exit cycle after download
                    if (len < 0) {
                        downloadOver = true;
                        break;
                    }
                    //Download has reached sub-download volume and exits the cycle
                    if ((subsLen + len) > divisionLen) {
                        break;
                    }
                    if (isPause){
                        //Call the pause download method to destroy the download thread
                        Log.e("tag", "Call the pause download method to destroy the download thread");
                        Utils.pauseDownload(mContext,fileBean);
                        break;
                    }
                    fos.write(bytes, 0, len);
                    subsLen += len;
                    //Callback the progress of the load
                    callback.progressCallBack(len);
                }

                if (!isPause){
                    // For the entire file download progress.Save Progress
                    int lengthCount = threadBean.getFinished() + subsLen;
                    threadBean.setFinished(lengthCount);
                    //After downloading in batches, you need to save the data to the database
                    callback.pauseCallBack(threadBean);
                    Log.e("tag", "Downloaded altogether: lengthCount = " + lengthCount);
                    //Since splicing 5M files is a time-consuming process, the order of destroying download threads and opening file splicing threads can be followed
                    //Call the pause download method to destroy the download thread
                    Utils.pauseDownload(mContext,fileBean);
                    //Open thread for file stitching
                    new AddFileThread(tempFileName, fileBean, start, subsLen, mContext, downloadOver).start();
                }

                //Download complete
                if (downloadOver) {
                    callback.threadDownLoadFinished(threadBean);
                }
            }

        } catch (Exception e) {
            //When the network is disconnected, a connection timeout exception is thrown, so call the pause download method in the catch to destroy the thread.
            Utils.pauseDownload(mContext,fileBean);
        } finally {
            try {
                inputStream.close();
                fos.close();
                connection.disconnect();
            } catch (Exception e) {
                Log.e("tag", "Exception-111--" + e.getMessage());
            }
        }
    }
}

Next is the file stitching thread.

public class AddFileThread extends Thread {

    private String tempFileName;
    private FileBean fileBean;
    private int filePointer;
    private int downloadLen;
    private Context context;
    private boolean downloadOver;

    /**
     * @param tempFileName Temporary file name
     * @param filePointer Where to start stitching temporary files from the total file
     * @param downloadLen Length of stitching
     * @param downloadOver Is download complete
     */
    public AddFileThread(String tempFileName, FileBean fileBean, int filePointer, int downloadLen, Context context,boolean downloadOver){
        this.tempFileName = tempFileName;
        this.fileBean = fileBean;
        this.filePointer = filePointer;
        this.downloadLen = downloadLen;
        this.context = context;
        this.downloadOver = downloadOver;
    }
    @Override
    public void run() {
        super.run();
	//general act
        File file = new File(Config.downLoadPath,fileBean.getFileName());
	//Temporary Files
        File tempFile = new File(Config.downLoadPath,tempFileName);
        RandomAccessFile raf = null;
        FileInputStream fis =null;
        try {
            raf = new RandomAccessFile(file,"rw");
	    //Temporary files spliced to a specific location in the total file
            raf.seek(filePointer);
            fis = new FileInputStream(tempFile);
            int len = -1;
            int countLen = 0;
            byte buf[] = new byte[1024];
            while(countLen < downloadLen){
                len = fis.read(buf);
                countLen += len;
                raf.write(buf,0,len);
                //Processing last read-write operation
                if((countLen + 1024) > downloadLen){
                    byte buff[] = new byte[downloadLen - countLen];
                    len = fis.read(buff);
                    Log.e("tag","The length of the last read is:" + len);
                    raf.write(buff,0,len);
                    break;
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                raf.close();
                fis.close();
            } catch (IOException e) {
                Log.e("tag","Exception --- "+e.getMessage());
            }

        }
        //Delete Temporary Files
        tempFile.delete();
        //Open the download thread and download the file
        if (!downloadOver){
            Log.e("tag","Continue downloading");
            Utils.startDownload(context,fileBean);
        }
    }
}

The rest of the code is basically the same as the blogger's.

Finally, the blogger's demo supports multi-threaded downloads, which I should not support. I did not test.And there may be a bug in that pause button, because there is no pause button in my requirement, so I didn't study it carefully. The same code was tested three times, right one time, bug was reported two times, [confusing] [confusing] in all, no big problem.

I'm just a little intern. Welcome all the big guys to correct their mistakes!Make progress together.

Posted by hungryOrb on Tue, 04 Jun 2019 09:38:33 -0700