private void _start() {

    try {

      // Whether service was already started.
      if (!_checkCanStart()) {
        // Not ready
        return;
      }

      FileDownloadList.getImpl().add(this);
      if (_checkCanReuse()) {
        // Will be removed when the complete message is received in #update
        return;
      }

      if (FileDownloadLog.NEED_LOG) {
        FileDownloadLog.d(this, "start downloaded by ui process %s", getUrl());
      }

      if (!_startExecute()) {
        setEx(new RuntimeException("not run download, not got download id"));
        FileDownloadList.getImpl().removeByError(this);
      }

    } catch (Throwable e) {
      e.printStackTrace();

      setEx(e);
      FileDownloadList.getImpl().removeByError(this);
    }
  }
  /**
   * start download
   *
   * <p>用于启动一个单独任务
   *
   * @return Download id
   */
  public int start() {
    if (FileDownloadMonitor.isValid()) {
      FileDownloadMonitor.getMonitor().onRequestStart(this);
    }

    if (FileDownloadLog.NEED_LOG) {
      FileDownloadLog.v(
          this,
          "call start " + "url[%s], setPath[%s] listener[%s], tag[%s]",
          url,
          path,
          listener,
          tag);
    }

    boolean ready = true;

    try {
      _adjust();
      _checkFile(path);
    } catch (Throwable e) {
      ready = false;

      setStatus(FileDownloadStatus.error);
      setEx(e);
      FileDownloadList.getImpl().add(this);
      FileDownloadList.getImpl().removeByError(this);
    }

    if (ready) {
      FileDownloadEventPool.getImpl().send2Service(new DownloadTaskEvent(this).requestStart());
    }

    return getDownloadId();
  }
  /** @param transfer In order to optimize some of the data in some cases is not back */
  void update(final FileDownloadTransferModel transfer) {
    switch (transfer.getStatus()) {
      case FileDownloadStatus.pending:
        if (getStatus() == FileDownloadStatus.pending) {
          FileDownloadLog.w(this, "already pending %d", getDownloadId());
          break;
        }
        this.setStatus(transfer.getStatus());
        this.setSoFarBytes(transfer.getSoFarBytes());
        this.setTotalBytes(transfer.getTotalBytes());

        // notify
        getDriver().notifyPending();
        break;
      case FileDownloadStatus.connected:
        if (getStatus() == FileDownloadStatus.connected) {
          FileDownloadLog.w(this, "already connected %d", getDownloadId());
          break;
        }

        setStatus(transfer.getStatus());
        setTotalBytes(transfer.getTotalBytes());
        setSoFarBytes(transfer.getSoFarBytes());
        this.isContinue = transfer.isContinue();
        this.etag = transfer.getEtag();

        // notify
        getDriver().notifyConnected();
        break;
      case FileDownloadStatus.progress:
        if (getStatus() == FileDownloadStatus.progress
            && transfer.getSoFarBytes() == getLargeFileSoFarBytes()) {
          FileDownloadLog.w(this, "%d unused values! by process callback", getDownloadId());
          break;
        }

        setStatus(transfer.getStatus());
        setSoFarBytes(transfer.getSoFarBytes());

        // notify
        getDriver().notifyProgress();
        break;
      case FileDownloadStatus.blockComplete:
        /** Handled by {@link FileDownloadList#removeByCompleted(BaseDownloadTask)} */
        break;
      case FileDownloadStatus.retry:
        if (getStatus() == FileDownloadStatus.retry
            && getRetryingTimes() == transfer.getRetryingTimes()) {
          FileDownloadLog.w(
              this,
              "%d already retry! %d %d %s",
              getDownloadId(),
              getRetryingTimes(),
              getAutoRetryTimes(),
              transfer.getThrowable());
          break;
        }

        setStatus(transfer.getStatus());
        setSoFarBytes(transfer.getSoFarBytes());
        setEx(transfer.getThrowable());
        _setRetryingTimes(transfer.getRetryingTimes());

        // notify
        getDriver().notifyRetry();
        break;
      case FileDownloadStatus.error:
        if (getStatus() == FileDownloadStatus.error) {
          FileDownloadLog.w(
              this,
              "%d already err(%s) , callback by other status same transfer",
              getDownloadId(),
              getEx());
          break;
        }

        setStatus(transfer.getStatus());
        setEx(transfer.getThrowable());
        setSoFarBytes(transfer.getSoFarBytes());

        // to FileDownloadList
        FileDownloadList.getImpl().removeByError(this);

        break;
      case FileDownloadStatus.paused:
        /** Handled by {@link #pause()} */
        break;
      case FileDownloadStatus.completed:
        if (getStatus() == FileDownloadStatus.completed) {
          FileDownloadLog.w(
              this,
              "%d already completed , callback by process with same transfer",
              getDownloadId());
          break;
        }

        this.isReusedOldFile = transfer.isUseOldFile();
        setStatus(transfer.getStatus());
        // only carry total data back
        setSoFarBytes(transfer.getTotalBytes());
        setTotalBytes(transfer.getTotalBytes());

        // to FileDownloadList
        FileDownloadList.getImpl().removeByCompleted(this);

        break;
      case FileDownloadStatus.warn:
        if (getStatus() == FileDownloadStatus.warn) {
          FileDownloadLog.w(
              this, "%d already warn , callback by process with same transfer", getDownloadId());
          break;
        }

        final int count = FileDownloadList.getImpl().count(getDownloadId());
        if (count <= 1) {
          // 1. this progress kill by sys and relive,
          // for add at least one listener
          // or 2. pre downloading task has already completed/error/paused
          // request status
          final int currentStatus = _getStatusFromServer(downloadId);
          FileDownloadLog.w(
              this,
              "warn, but no listener to receive progress, " + "switch to pending %d %d",
              getDownloadId(),
              currentStatus);

          if (FileDownloadStatus.isIng(currentStatus)) {
            // ing, has callbacks
            // keep and wait callback

            setStatus(FileDownloadStatus.pending);
            getDriver().notifyPending();
            break;
          } else {
            // already over and no callback
          }
        }

        setStatus(transfer.getStatus());

        // to FileDownloadList
        FileDownloadList.getImpl().removeByWarn(this);
        break;
    }
  }