Search in sources :

Example 6 with FileDownloadModel

use of com.liulishuo.filedownloader.model.FileDownloadModel in project FileDownloader by lingochamp.

the class DefaultDatabaseImpl method refreshDataFromDB.

private void refreshDataFromDB() {
    long start = System.currentTimeMillis();
    Cursor c = db.rawQuery("SELECT * FROM " + TABLE_NAME, null);
    List<Integer> dirtyList = new ArrayList<>();
    //noinspection TryFinallyCanBeTryWithResources
    try {
        while (c.moveToNext()) {
            FileDownloadModel model = new FileDownloadModel();
            model.setId(c.getInt(c.getColumnIndex(FileDownloadModel.ID)));
            model.setUrl(c.getString(c.getColumnIndex(FileDownloadModel.URL)));
            model.setPath(c.getString(c.getColumnIndex(FileDownloadModel.PATH)), c.getShort(c.getColumnIndex(FileDownloadModel.PATH_AS_DIRECTORY)) == 1);
            model.setStatus((byte) c.getShort(c.getColumnIndex(FileDownloadModel.STATUS)));
            model.setSoFar(c.getLong(c.getColumnIndex(FileDownloadModel.SOFAR)));
            model.setTotal(c.getLong(c.getColumnIndex(FileDownloadModel.TOTAL)));
            model.setErrMsg(c.getString(c.getColumnIndex(FileDownloadModel.ERR_MSG)));
            model.setETag(c.getString(c.getColumnIndex(FileDownloadModel.ETAG)));
            model.setFilename(c.getString(c.getColumnIndex(FileDownloadModel.FILENAME)));
            if (model.getStatus() == FileDownloadStatus.progress || model.getStatus() == FileDownloadStatus.connected || model.getStatus() == FileDownloadStatus.error || (model.getStatus() == FileDownloadStatus.pending && model.getSoFar() > 0)) {
                // Ensure can be covered by RESUME FROM BREAKPOINT.
                model.setStatus(FileDownloadStatus.paused);
            }
            final String targetFilePath = model.getTargetFilePath();
            if (targetFilePath == null) {
                // no target file path, can't used to resume from breakpoint.
                dirtyList.add(model.getId());
                continue;
            }
            final File targetFile = new File(targetFilePath);
            // consider check in new thread, but SQLite lock | file lock aways effect, so sync
            if (model.getStatus() == FileDownloadStatus.paused && FileDownloadMgr.isBreakpointAvailable(model.getId(), model, model.getPath(), null)) {
                // can be reused in the old mechanism(no-temp-file).
                final File tempFile = new File(model.getTempFilePath());
                if (!tempFile.exists() && targetFile.exists()) {
                    final boolean successRename = targetFile.renameTo(tempFile);
                    if (FileDownloadLog.NEED_LOG) {
                        FileDownloadLog.d(this, "resume from the old no-temp-file architecture [%B], [%s]->[%s]", successRename, targetFile.getPath(), tempFile.getPath());
                    }
                }
            }
            /**
                 * Remove {@code model} from DB if it can't used for judging whether the
                 * old-downloaded file is valid for reused & it can't used for resuming from
                 * BREAKPOINT, In other words, {@code model} is no use anymore for FileDownloader.
                 */
            if (model.getStatus() == FileDownloadStatus.pending && model.getSoFar() <= 0) {
                // This model is redundant.
                dirtyList.add(model.getId());
            } else if (!FileDownloadMgr.isBreakpointAvailable(model.getId(), model)) {
                // It can't used to resuming from breakpoint.
                dirtyList.add(model.getId());
            } else if (targetFile.exists()) {
                // It has already completed downloading.
                dirtyList.add(model.getId());
            } else {
                downloaderModelMap.put(model.getId(), model);
            }
        }
    } finally {
        c.close();
        FileDownloadUtils.markConverted(FileDownloadHelper.getAppContext());
        // db
        if (dirtyList.size() > 0) {
            String args = TextUtils.join(", ", dirtyList);
            if (FileDownloadLog.NEED_LOG) {
                FileDownloadLog.d(this, "delete %s", args);
            }
            //noinspection ThrowFromFinallyBlock
            db.execSQL(FileDownloadUtils.formatString("DELETE FROM %s WHERE %s IN (%s);", TABLE_NAME, FileDownloadModel.ID, args));
        }
        // 566 data consumes about 140ms
        if (FileDownloadLog.NEED_LOG) {
            FileDownloadLog.d(this, "refresh data %d , will delete: %d consume %d", downloaderModelMap.size(), dirtyList.size(), System.currentTimeMillis() - start);
        }
    }
}
Also used : ArrayList(java.util.ArrayList) FileDownloadModel(com.liulishuo.filedownloader.model.FileDownloadModel) Cursor(android.database.Cursor) File(java.io.File)

Example 7 with FileDownloadModel

use of com.liulishuo.filedownloader.model.FileDownloadModel in project FileDownloader by lingochamp.

the class FileDownloadMgr method pause.

public boolean pause(final int id) {
    if (FileDownloadLog.NEED_LOG) {
        FileDownloadLog.d(this, "request pause the task %d", id);
    }
    final FileDownloadModel model = mDatabase.find(id);
    if (model == null) {
        return false;
    }
    mThreadPool.cancel(id);
    //        mClient.cancel(id);
    return true;
}
Also used : FileDownloadModel(com.liulishuo.filedownloader.model.FileDownloadModel)

Example 8 with FileDownloadModel

use of com.liulishuo.filedownloader.model.FileDownloadModel in project FileDownloader by lingochamp.

the class FileDownloadMgr method start.

// synchronize for safe: check downloading, check resume, update data, execute runnable
public synchronized void start(final String url, final String path, final boolean pathAsDirectory, final int callbackProgressTimes, final int callbackProgressMinIntervalMillis, final int autoRetryTimes, final boolean forceReDownload, final FileDownloadHeader header, final boolean isWifiRequired) {
    if (FileDownloadLog.NEED_LOG) {
        FileDownloadLog.d(this, "request start the task with url(%s) path(%s) isDirectory(%B)", url, path, pathAsDirectory);
    }
    final int id = FileDownloadUtils.generateId(url, path, pathAsDirectory);
    FileDownloadModel model = mDatabase.find(id);
    if (!pathAsDirectory && model == null) {
        // try dir data.
        final int dirCaseId = FileDownloadUtils.generateId(url, FileDownloadUtils.getParent(path), true);
        model = mDatabase.find(dirCaseId);
        if (model != null && path.equals(model.getTargetFilePath())) {
            if (FileDownloadLog.NEED_LOG) {
                FileDownloadLog.d(this, "task[%d] find model by dirCaseId[%d]", id, dirCaseId);
            }
        }
    }
    if (FileDownloadHelper.inspectAndInflowDownloading(id, model, this, true)) {
        if (FileDownloadLog.NEED_LOG) {
            FileDownloadLog.d(this, "has already started download %d", id);
        }
        return;
    }
    final String targetFilePath = model != null ? model.getTargetFilePath() : FileDownloadUtils.getTargetFilePath(path, pathAsDirectory, null);
    if (FileDownloadHelper.inspectAndInflowDownloaded(id, targetFilePath, forceReDownload, true)) {
        if (FileDownloadLog.NEED_LOG) {
            FileDownloadLog.d(this, "has already completed downloading %d", id);
        }
        return;
    }
    final long sofar = model != null ? model.getSoFar() : 0;
    final String tempFilePath = model != null ? model.getTempFilePath() : FileDownloadUtils.getTempPath(targetFilePath);
    if (FileDownloadHelper.inspectAndInflowConflictPath(id, sofar, tempFilePath, targetFilePath, this)) {
        if (FileDownloadLog.NEED_LOG) {
            FileDownloadLog.d(this, "there is an another task with the same target-file-path %d %s", id, targetFilePath);
            // because of the file is dirty for this task.
            if (model != null) {
                mDatabase.remove(id);
            }
        }
        return;
    }
    // real start
    // - create model
    boolean needUpdate2DB;
    if (model != null && (model.getStatus() == FileDownloadStatus.paused || // FileDownloadRunnable invoke
    model.getStatus() == FileDownloadStatus.error)) // #isBreakpointAvailable to determine whether it is really invalid.
    {
        if (model.getId() != id) {
            // in try dir case.
            mDatabase.remove(model.getId());
            model.setId(id);
            model.setPath(path, pathAsDirectory);
            needUpdate2DB = true;
        } else {
            needUpdate2DB = false;
        }
    } else {
        if (model == null) {
            model = new FileDownloadModel();
        }
        model.setUrl(url);
        model.setPath(path, pathAsDirectory);
        model.setId(id);
        model.setSoFar(0);
        model.setTotal(0);
        model.setStatus(FileDownloadStatus.pending);
        needUpdate2DB = true;
    }
    // - update model to db
    if (needUpdate2DB) {
        mDatabase.update(model);
    }
    // - execute
    mThreadPool.execute(new FileDownloadRunnable(this, mOutputStreamCreator, mConnectionCreator, model, mDatabase, autoRetryTimes, header, callbackProgressMinIntervalMillis, callbackProgressTimes, forceReDownload, isWifiRequired));
}
Also used : FileDownloadModel(com.liulishuo.filedownloader.model.FileDownloadModel)

Example 9 with FileDownloadModel

use of com.liulishuo.filedownloader.model.FileDownloadModel in project FileDownloader by lingochamp.

the class FileDownloadRunnable method loop.

@SuppressWarnings("ConstantConditions")
private void loop(FileDownloadModel model) {
    int retryingTimes = 0;
    boolean revisedInterval = false;
    FileDownloadConnection connection = null;
    do {
        // loop for retry
        long soFar = 0;
        final int id = mId;
        try {
            // Step 1, check state
            if (checkState()) {
                if (FileDownloadLog.NEED_LOG) {
                    FileDownloadLog.d(this, "already canceled %d %d", id, model.getStatus());
                }
                onPause();
                break;
            }
            if (FileDownloadLog.NEED_LOG) {
                FileDownloadLog.d(FileDownloadRunnable.class, "start download %s %s", id, model.getUrl());
            }
            // Step 2, handle resume from breakpoint
            checkIsResumeAvailable();
            connection = mConnectionCreator.create(model.getUrl());
            addHeader(connection);
            // start download----------------
            // Step 3, init request
            // get the request header in here, because of there are many connection
            // component(such as HttpsURLConnectionImpl, HttpURLConnectionImpl in okhttp3) don't
            // allow access to the request header after it connected.
            final Map<String, List<String>> requestHeader = connection.getRequestHeaderFields();
            if (FileDownloadLog.NEED_LOG) {
                FileDownloadLog.d(this, "%s request header %s", id, requestHeader);
            }
            // Step 4, build connect
            connection.execute();
            final int code = connection.getResponseCode();
            final boolean isSucceedStart = code == HttpURLConnection.HTTP_OK || code == FileDownloadConnection.NO_RESPONSE_CODE;
            // if the response status code isn't point to PARTIAL/OFFSET, isSucceedResume will
            // be assigned to false, so filedownloader will download the file from very beginning.
            final boolean isSucceedResume = ((code == HttpURLConnection.HTTP_PARTIAL) || (code == FileDownloadConnection.RESPONSE_CODE_FROM_OFFSET)) && isResumeDownloadAvailable;
            if (isResumeDownloadAvailable && !isSucceedResume) {
                FileDownloadLog.d(this, "want to resume from the breakpoint[%d], but the " + "response status code is[%d]", model.getSoFar(), code);
            }
            if (isSucceedStart || isSucceedResume) {
                long total = model.getTotal();
                final String transferEncoding = connection.getResponseHeaderField("Transfer-Encoding");
                // Step 5, check response's header
                if (isSucceedStart || total <= 0) {
                    if (transferEncoding == null) {
                        total = FileDownloadUtils.convertContentLengthString(connection.getResponseHeaderField("Content-Length"));
                    } else {
                        // if transfer not nil, ignore content-length
                        total = TOTAL_VALUE_IN_CHUNKED_RESOURCE;
                    }
                }
                // TODO consider if not is chunked & http 1.0/(>=http1.1 & connect not be keep live) may not give content-length
                if (total < 0) {
                    // invalid total length
                    final boolean isEncodingChunked = transferEncoding != null && transferEncoding.equals("chunked");
                    if (!isEncodingChunked) {
                        // not chunked transfer encoding data
                        if (FileDownloadProperties.getImpl().HTTP_LENIENT) {
                            // do not response content-length either not chunk transfer encoding,
                            // but HTTP lenient is true, so handle as the case of transfer encoding chunk
                            total = TOTAL_VALUE_IN_CHUNKED_RESOURCE;
                            if (FileDownloadLog.NEED_LOG) {
                                FileDownloadLog.d(this, "%d response header is not legal but " + "HTTP lenient is true, so handle as the case of " + "transfer encoding chunk", id);
                            }
                        } else {
                            throw new FileDownloadGiveUpRetryException("can't know the size of the " + "download file, and its Transfer-Encoding is not Chunked " + "either.\nyou can ignore such exception by add " + "http.lenient=true to the filedownloader.properties");
                        }
                    }
                }
                if (isSucceedResume) {
                    soFar = model.getSoFar();
                }
                // Step 6, callback on connected, and update header to db. for save etag.
                onConnected(isSucceedResume, total, findEtag(connection), findFilename(connection));
                // Step 7, check whether has same task running after got filename from server/local generate.
                if (model.isPathAsDirectory()) {
                    // this scope for caring about the case of there is another task is provided
                    // the same path to store file and the same url.
                    final String targetFilePath = model.getTargetFilePath();
                    // get the ID after got the filename.
                    final int fileCaseId = FileDownloadUtils.generateId(model.getUrl(), targetFilePath);
                    // whether the file with the filename has been existed.
                    if (FileDownloadHelper.inspectAndInflowDownloaded(id, targetFilePath, isForceReDownload, false)) {
                        helper.remove(id);
                        break;
                    }
                    final FileDownloadModel fileCaseModel = helper.find(fileCaseId);
                    if (fileCaseModel != null) {
                        // whether the another task with the same file and url is downloading.
                        if (FileDownloadHelper.inspectAndInflowDownloading(id, fileCaseModel, threadPoolMonitor, false)) {
                            //it has been post to upper layer the 'warn' message, so the current
                            // task no need to continue download.
                            helper.remove(id);
                            break;
                        }
                        // the another task with the same file name and url is paused
                        helper.remove(fileCaseId);
                        deleteTargetFile();
                        if (FileDownloadMgr.isBreakpointAvailable(fileCaseId, fileCaseModel)) {
                            model.setSoFar(fileCaseModel.getSoFar());
                            model.setTotal(fileCaseModel.getTotal());
                            model.setETag(fileCaseModel.getETag());
                            helper.update(model);
                            // re connect to resume from breakpoint.
                            continue;
                        }
                    }
                    // whether there is an another running task with the same target-file-path.
                    if (FileDownloadHelper.inspectAndInflowConflictPath(id, model.getSoFar(), getTempFilePath(), targetFilePath, threadPoolMonitor)) {
                        helper.remove(id);
                        break;
                    }
                }
                // Step 8, start fetch datum from input stream & write to file
                if (fetch(connection, isSucceedResume, soFar, total)) {
                    break;
                }
            } else {
                final FileDownloadHttpException httpException = new FileDownloadHttpException(code, requestHeader, connection.getResponseHeaderFields());
                if (revisedInterval) {
                    throw httpException;
                }
                revisedInterval = true;
                switch(code) {
                    case HTTP_REQUESTED_RANGE_NOT_SATISFIABLE:
                        deleteTaskFiles();
                        FileDownloadLog.w(FileDownloadRunnable.class, "%d response code %d, " + "range[%d] isn't make sense, so delete the dirty file[%s]" + ", and try to redownload it from byte-0.", id, code, model.getSoFar(), model.getTempFilePath());
                        onRetry(httpException, retryingTimes++);
                        break;
                    default:
                        throw httpException;
                }
            }
        } catch (Throwable ex) {
            // TODO 决策是否需要重试,是否是用户决定,或者根据错误码处理
            if (autoRetryTimes > retryingTimes++ && !(ex instanceof FileDownloadGiveUpRetryException)) {
                // retry
                onRetry(ex, retryingTimes);
            } else {
                // error
                onError(ex);
                break;
            }
        } finally {
            if (connection != null) {
                connection.ending();
            }
        }
    } while (true);
}
Also used : FileDownloadHttpException(com.liulishuo.filedownloader.exception.FileDownloadHttpException) FileDownloadConnection(com.liulishuo.filedownloader.connection.FileDownloadConnection) FileDownloadGiveUpRetryException(com.liulishuo.filedownloader.exception.FileDownloadGiveUpRetryException) FileDownloadModel(com.liulishuo.filedownloader.model.FileDownloadModel) List(java.util.List)

Example 10 with FileDownloadModel

use of com.liulishuo.filedownloader.model.FileDownloadModel in project FileDownloader by lingochamp.

the class DefaultDatabaseImpl method update.

@Override
public void update(List<FileDownloadModel> downloadModelList) {
    if (downloadModelList == null) {
        FileDownloadLog.w(this, "update a download list, but list == null!");
        return;
    }
    db.beginTransaction();
    try {
        for (FileDownloadModel model : downloadModelList) {
            if (find(model.getId()) != null) {
                // replace
                downloaderModelMap.remove(model.getId());
                downloaderModelMap.put(model.getId(), model);
                db.update(TABLE_NAME, model.toContentValues(), FileDownloadModel.ID + " = ? ", new String[] { String.valueOf(model.getId()) });
            } else {
                // insert new one.
                downloaderModelMap.put(model.getId(), model);
                db.insert(TABLE_NAME, null, model.toContentValues());
            }
        }
        db.setTransactionSuccessful();
    } finally {
        db.endTransaction();
    }
}
Also used : FileDownloadModel(com.liulishuo.filedownloader.model.FileDownloadModel)

Aggregations

FileDownloadModel (com.liulishuo.filedownloader.model.FileDownloadModel)13 ConnectionModel (com.liulishuo.filedownloader.model.ConnectionModel)3 File (java.io.File)2 Application (android.app.Application)1 Cursor (android.database.Cursor)1 IThreadPoolMonitor (com.liulishuo.filedownloader.IThreadPoolMonitor)1 FileDownloadConnection (com.liulishuo.filedownloader.connection.FileDownloadConnection)1 FileDownloadDatabase (com.liulishuo.filedownloader.database.FileDownloadDatabase)1 DownloadLaunchRunnable (com.liulishuo.filedownloader.download.DownloadLaunchRunnable)1 FileDownloadGiveUpRetryException (com.liulishuo.filedownloader.exception.FileDownloadGiveUpRetryException)1 FileDownloadHttpException (com.liulishuo.filedownloader.exception.FileDownloadHttpException)1 FileDownloadNetworkPolicyException (com.liulishuo.filedownloader.exception.FileDownloadNetworkPolicyException)1 FileDownloadHeader (com.liulishuo.filedownloader.model.FileDownloadHeader)1 FileDownloadHelper (com.liulishuo.filedownloader.util.FileDownloadHelper)1 ArrayList (java.util.ArrayList)1 Iterator (java.util.Iterator)1 List (java.util.List)1 Test (org.junit.Test)1