Android實現斷點多線程下載

這篇文章主要爲大家詳細介紹了Android實現斷點多線程下載,具有一定的參考價值,感興趣的小夥伴們可以參考一下

斷點多線程下載的幾個關鍵點:①:得到要下載的文件大小後,均分給幾個線程。②:使用RandomAccessFile類進行讀寫,可以指定開始寫入的位置。③:數據庫保存下載信息,下一次繼續下載的時候從數據庫取出數據,然後從上次下載結束的地方開始。

這裏我使用了FinalDb的數據庫框架,同時在內存中存儲了一份所有線程的下載信息,負責時時更新和查詢下載進度。我測試用的是百度雲網盤,文件大小50M左右。注意,線程中指定了開始結束下載位置的網絡請求成功的返回碼是206,並不是200。

效果圖:


線程類:線程類負責具體的下載,線程的下載信息被存儲到了數據庫。線程開始下載時,根據線程ID查詢自己的存儲信息,然後開始從指定的位置下載和寫入文件。完畢後根據自己的當前下載結果設置自己當前的下載狀態。時時的下載進度存儲只存儲到了內存,只在本次下載結束才存儲到數據庫。

public class DownloadThread extends Thread {
 
  /**
   * 數據庫操作工具
   */
  private FinalDb finalDb;
 
  /**
   * 下載狀態:未開始
   */
  public static final int STATE_READY = 1;
 
  /**
   * 下載狀態:下載中
   */
  public static final int STATE_LOADING = 2;
 
  /**
   * 下載狀態:下載暫停中
   */
  public static final int STATE_PAUSING = 3;
 
  /**
   * 下載狀態:下載完成
   */
  public static final int STATE_FINISH = 4;
 
  /**
   * 下載狀態
   */
  public int downloadState;
 
  /**
   * 線程ID
   */
  private int threadID;
 
  /**
   * 要下載的URL路徑
   */
  private String url;
 
  /**
   * 本線程要下載的文件
   */
  public RandomAccessFile file;
 
  /**
   * 構造器
   */
  public DownloadThread(Context context, int threadID, String downloadUrl, RandomAccessFile randomAccessFile) {
    this.threadID = threadID;
    this.url = downloadUrl;
    this.file = randomAccessFile;
    finalDb = DBUtil.getFinalDb(context);
  }
 
  @Override
  public void run() {
    //數據庫查詢本線程下載進度
    List<ThreadDownloadInfoBean> list = finalDb.findAllByWhere(ThreadDownloadInfoBean.class, "threadID='" + threadID + "'");
    //下載信息存放到內存
    if (list.get(0) != null) {
      MapUtil.map.put(threadID, list.get(0));
    }
    //取出實體類
    ThreadDownloadInfoBean bean = MapUtil.map.get(threadID);
    Utils.Print("bean:" + bean.toString());
    InputStream is;
    HttpURLConnection conn;
    try {
      Utils.Print("線程" + threadID + "開始連接");
      conn = (HttpURLConnection) new URL(url).openConnection();
      conn.setConnectTimeout(5000);
      conn.setReadTimeout(5000);
      conn.setRequestMethod("GET");
      //設置下載開始和結束的位置
      conn.setRequestProperty("Range", "bytes=" + (bean.startDownloadPosition + bean.downloadedSize) + "-" + bean.endDownloadPosition);
      conn.connect();
      if (conn.getResponseCode() == 206) {
        //更改下載狀態
        downloadState = STATE_LOADING;
        bean.downloadState = STATE_LOADING;
        Utils.Print("線程" + threadID + "連接成功");
        is = conn.getInputStream();
        // 1K的數據緩衝
        byte[] bs = new byte[1024];
        // 讀取到的數據長度
        int len;
        //從指定的位置開始下載
        file.seek(bean.startDownloadPosition);
        // 循環讀取,當已經下載的大小達到了指定的本線程負責的大小時跳出循環,線程之間負責的文件首尾有重合的話沒有影響,因爲寫入的內容時相同的
        while ((len = is.read(bs)) != -1) {
          //不用在這個循環裏面更新數據庫
          file.write(bs, 0, len);
          //時時更新內存中的已下載大小信息
          bean.downloadedSize += len;
          //如果調用者暫停下載,則跳出結束方法
          if (downloadState == STATE_PAUSING) {
            Utils.Print("線程" + threadID + "暫停下載");
            break;
          }
        }
        is.close();
        file.close();
      } else {
        Utils.Print("線程" + threadID + "連接失敗");
      }
      conn.disconnect();
      //如果這個線程已經下載完了自己負責的部分就修改下載狀態
      if (bean.downloadedSize >= bean.downloadTotalSize) {
        bean.downloadState = STATE_FINISH;
      } else {
        bean.downloadState = STATE_PAUSING;
      }
      //內存中信息更新至數據庫
      finalDb.update(bean, "threadID='" + bean.threadID + "'");
    } catch (IOException e) {
      Utils.Print("線程" + threadID + "IO異常");
      e.printStackTrace();
    }
  }
}

線程信息的封裝類:負責存儲每個線程的ID,開始下載的位置,結束下載的位置,已經下載的大小,下載狀態等;這個類用FinalDb數據庫存儲到數據庫,一定要寫get,set方法和空參構造器

@Table(name = "ThreadInfo")
public class ThreadDownloadInfoBean {
 
  /**
   * id
   */
  public int id;
 
  /**
   * 線程ID
   */
  public int threadID;
 
  /**
   * 本線程時時下載開始的位置
   */
  public long startDownloadPosition;
 
  /**
   * 本線程時時下載結束的位置
   */
  public long endDownloadPosition;
 
  /**
   * 本線程負責下載的文件大小
   */
  public long downloadTotalSize;
 
  /**
   * 已經下載了的文件大小
   */
  public long downloadedSize;
 
  /**
   * 本線程的下載狀態
   */
  public int downloadState;
 
  public ThreadDownloadInfoBean() {
 
  }
 
  public ThreadDownloadInfoBean(int downloadState, long downloadedSize, long downloadTotalSize, long endDownloadPosition, long startDownloadPosition, int threadID) {
    this.downloadState = downloadState;
    this.downloadedSize = downloadedSize;
    this.downloadTotalSize = downloadTotalSize;
    this.endDownloadPosition = endDownloadPosition;
    this.startDownloadPosition = startDownloadPosition;
    this.threadID = threadID;
  }
 
  public int getId() {
    return id;
  }
 
  public void setId(int id) {
    this.id = id;
  }
 
  public int getThreadID() {
    return threadID;
  }
 
  public void setThreadID(int threadID) {
    this.threadID = threadID;
  }
 
  public long getStartDownloadPosition() {
    return startDownloadPosition;
  }
 
  public void setStartDownloadPosition(long startDownloadPosition) {
    this.startDownloadPosition = startDownloadPosition;
  }
 
  public long getEndDownloadPosition() {
    return endDownloadPosition;
  }
 
  public void setEndDownloadPosition(long endDownloadPosition) {
    this.endDownloadPosition = endDownloadPosition;
  }
 
  public long getDownloadTotalSize() {
    return downloadTotalSize;
  }
 
  public void setDownloadTotalSize(long downloadTotalSize) {
    this.downloadTotalSize = downloadTotalSize;
  }
 
  public long getDownloadedSize() {
    return downloadedSize;
  }
 
  public void setDownloadedSize(long downloadedSize) {
    this.downloadedSize = downloadedSize;
  }
 
  public int getDownloadState() {
    return downloadState;
  }
 
  public void setDownloadState(int downloadState) {
    this.downloadState = downloadState;
  }
 
  @Override
  public String toString() {
    return "ThreadDownloadInfoBean{" +
        "id=" + id +
        ", threadID=" + threadID +
        ", startDownloadPosition=" + startDownloadPosition +
        ", endDownloadPosition=" + endDownloadPosition +
        ", downloadTotalSize=" + downloadTotalSize +
        ", downloadedSize=" + downloadedSize +
        ", downloadState=" + downloadState +
        '}';
  }
}

下載工具類:這個類負責得到下載文件大小,分配線程下載大小,管理下載線程

public class DownUtil {
 
  /**
   * 數據庫操作工具
   */
  public FinalDb finalDb;
 
  /**
   * 下載狀態:準備好
   */
  public static final int STATE_READY = 1;
 
  /**
   * 下載狀態:下載中
   */
  public static final int STATE_LOADING = 2;
 
  /**
   * 下載狀態:暫停中
   */
  public static final int STATE_PAUSING = 3;
 
  /**
   * 下載狀態:下載完成
   */
  public static final int STATE_FINISH = 4;
 
  /**
   * 下載狀態
   */
  public int downloadState;
 
  /**
   * context
   */
  private Context context;
 
  /**
   * 要下載文件的大小
   */
  public long fileSize;
 
  /**
   * 線程集合
   */
  private ArrayList<DownloadThread> threadList = new ArrayList<>();
 
  /**
   * 構造器
   */
  public DownUtil(Context context) {
    this.context = context;
    finalDb = DBUtil.getFinalDb(context);
    judgeDownState();
  }
 
  /**
   * 初始化時判斷下載狀態
   */
  public void judgeDownState() {
    //取出數據庫中的下載信息,存儲到內存中
    List<ThreadDownloadInfoBean> list = finalDb.findAll(ThreadDownloadInfoBean.class);
    if (list != null && list.size() == DownloadActivity.threadNum) {
      for (int i = 0; i < list.size(); i++) {
        MapUtil.map.put(i, list.get(i));
      }
    }
    //查詢SP中是否存儲過要下載的文件大小
    Long spFileSize = SPUtil.getInstance(context).getLong(DownloadActivity.fileName, 0L);
    long downloadedSize = getFinishedSize();
    //SP中或者數據庫中沒有查詢到說明從沒有進行過下載
    if (spFileSize == 0 || downloadedSize == 0) {
      downloadState = STATE_READY;
    } else if (downloadedSize >= spFileSize) {
      downloadState = STATE_FINISH;
    } else {
      downloadState = STATE_PAUSING;
      fileSize = spFileSize;
    }
  }
 
  /**
   * 點擊了開始按鈕
   */
  public void clickDownloadBtn() {
    if (downloadState == STATE_READY) {
      startDownload();
    } else if (downloadState == STATE_PAUSING) {
      continueDownload();
    }
  }
 
  /**
   * 進入應用第一次開始下載
   */
  private void startDownload() {
    //開啓新線程,得到要下載的文件大小
    new Thread() {
      @Override
      public void run() {
        try {
          HttpURLConnection conn;
          conn = (HttpURLConnection) new URL(DownloadActivity.url).openConnection();
          conn.setConnectTimeout(5000);
          conn.setReadTimeout(5000);
          conn.setRequestMethod("GET");
          conn.connect();
          if (conn.getResponseCode() == 200) {
            Utils.Print("DownUtil連接成功");
            //得到要下載的文件大小
            fileSize = conn.getContentLength();
            //得到文件名後綴名
            String contentDisposition = new String(conn.getHeaderField("Content-Disposition").getBytes("ISO-8859-1"), "UTF-8");
            String fileName = "下載測試" + contentDisposition.substring(contentDisposition.lastIndexOf("."), contentDisposition.lastIndexOf("\""));
            //得到存儲路徑
            String sdCardPath = context.getExternalFilesDir(null).getPath();
            DownloadActivity.fileName = sdCardPath + "/" + fileName;
            SPUtil.getInstance(context).saveString(DownloadActivity.FILE_NAME, DownloadActivity.fileName);
            SPUtil.getInstance(context).saveLong(DownloadActivity.fileName, fileSize);
            /*
             * 計算一下每個線程需要分擔的下載文件大小 比如 總下載量爲100 一共有三個線程
             * 那麼 線程1負責0-32,線程2負責33-65,線程3負責66-99和100,
             * 也就是說下載總量除以線程數如果有餘數,那麼最後一個線程多下載一個餘數部分
             */
            //每個線程均分的大小
            long threadDownSize = fileSize / DownloadActivity.threadNum;
            //線程均分完畢剩餘的大小
            long leftDownSize = fileSize % DownloadActivity.threadNum;
            //創建要寫入的文件
            RandomAccessFile file = new RandomAccessFile(DownloadActivity.fileName, "rw");
            //設置文件大小
            file.setLength(fileSize);
            //關閉
            file.close();
            for (int i = 0; i < DownloadActivity.threadNum; i++) {
              Utils.Print("開啓線程" + i);
              //指定每個線程開始下載的位置
              long startPosition = i * threadDownSize;
              //指定每個線程負責下載的大小,當現場是集合裏面最後一個線程的時候,它要增加leftDownSize的大小
              threadDownSize = i == DownloadActivity.threadNum - 1 ? threadDownSize + leftDownSize : threadDownSize;
              //存儲線程信息
              ThreadDownloadInfoBean bean = new ThreadDownloadInfoBean(DownloadThread.STATE_READY, 0, threadDownSize, startPosition + threadDownSize, startPosition, i);
              finalDb.save(bean);
              RandomAccessFile threadFile = new RandomAccessFile(DownloadActivity.fileName, "rw");
              threadList.add(new DownloadThread(context, i, DownloadActivity.url, threadFile));
              threadList.get(i).start();
            }
            downloadState = STATE_LOADING;
            downloadInfoListener.connectSuccess();
          } else {
            Utils.Print("DownUtil-連接失敗");
            downloadInfoListener.connectFail();
          }
          conn.disconnect();
        } catch (IOException e) {
          Utils.Print("DownUtil-IO異常");
          downloadInfoListener.IOException();
          e.printStackTrace();
        }
      }
    }.start();
  }
 
  /**
   * 繼續下載
   */
  private void continueDownload() {
    List<ThreadDownloadInfoBean> list = finalDb.findAll(ThreadDownloadInfoBean.class);
    for (int i = 0; i < DownloadActivity.threadNum; i++) {
      //當前線程已經下載完了就不再開啓
      if (list.get(i).downloadState != DownloadThread.STATE_FINISH) {
        Utils.Print("重新開啓線程" + i);
        RandomAccessFile threadFile = null;
        try {
          threadFile = new RandomAccessFile(DownloadActivity.fileName, "rw");
        } catch (FileNotFoundException e) {
          e.printStackTrace();
        }
        DownloadThread downloadThread = new DownloadThread(context, i, DownloadActivity.url, threadFile);
        threadList.add(downloadThread);
        downloadThread.start();
      }
    }
    downloadState = STATE_LOADING;
    downloadInfoListener.connectSuccess();
  }
 
  /**
   * 點擊了暫停的按鈕
   */
  public void clickPauseBtn() {
    if (downloadState == STATE_LOADING) {
      stopDownload();
    }
  }
 
  /**
   * 暫停下載
   */
  private void stopDownload() {
    for (int i = 0; i < threadList.size(); i++) {
      if (threadList.get(i).downloadState == DownloadThread.STATE_LOADING) {
        threadList.get(i).downloadState = DownloadThread.STATE_PAUSING;
      }
    }
    downloadState = STATE_PAUSING;
    threadList.clear();
  }
 
  /**
   * 返回此刻所有線程完成的下載大小
   */
  public long getFinishedSize() {
    long totalSize = 0;
    for (int i = 0; i < DownloadActivity.threadNum; i++) {
      ThreadDownloadInfoBean bean = MapUtil.map.get(i);
      if (bean != null) {
        //如果該線程已經下載的部分大於分配給它的部分多餘部分不予計算
        long addSize = bean.downloadedSize > bean.downloadTotalSize ? bean.downloadTotalSize : bean.downloadedSize;
        totalSize += addSize;
      }
    }
    return totalSize;
  }
 
  /**
   * 下載信息監聽器
   */
  private DownloadInfoListener downloadInfoListener;
 
  /**
   * 下載信息監聽器
   */
  public interface DownloadInfoListener {
 
    void connectSuccess();
 
    void connectFail();
 
    void IOException();
  }
 
  /**
   * 設置下載信息監聽器
   */
  public void setDownloadInfoListener(DownloadInfoListener downloadInfoListener) {
    this.downloadInfoListener = downloadInfoListener;
  }
 
}

頁面Activity:負責展示下載進度等信息,提供操作頁面

public class DownloadActivity extends BaseActivity {
 
  /**
   * 下載地址輸入框
   */
  private EditText et_download_url;
 
  /**
   * 確定輸入地址的按鈕
   */
  private Button btn_download_geturl;
 
  /**
   * 進度條
   */
  private NumberProgressView np_download;
 
  /**
   * 開始下載的按鈕
   */
  private Button btn_download_start;
 
  /**
   * 暫停下載的按鈕
   */
  private Button btn_download_pause;
 
  /**
   * 取消下載的按鈕
   */
  private Button btn_download_cancel;
 
  /**
   * 文件信息顯示
   */
  private TextView tv_download_file_info;
 
  /**
   * 下載速度顯示
   */
  private TextView tv_download_speed;
 
  /**
   * 顯示下載信息
   */
  private TextView tv_download_speed_info;
 
  /**
   * 每隔一段時間刷新下載進度顯示
   */
  private final static int WHAT_INCREACE = 1;
 
  /**
   * 得到了文件名稱
   */
  private final static int WHAT_GET_FILENAME = 2;
 
  /**
   * downUtil連接失敗
   */
  private final static int WHAI_CONNECT_FAIL = 3;
 
  /**
   * downUtilIO異常
   */
  private final static int WHAT_IO_EXCEPTION = 4;
 
  /**
   * 下載工具
   */
  private DownUtil downUtil;
 
  /**
   * 需要開啓的線程數量
   */
  public static final int threadNum = 5;
 
  /**
   * 存放文件路徑名稱的SP鍵名
   */
  public static final String FILE_NAME = "fileName";
 
  /**
   * 要下載的文件的url地址
   */
  public static String url = "";
  
  /**
   * 文件下載路徑和文件名稱
   */
  public static String fileName;
 
  /**
   * 上次統計已經完成下載的文件大小
   */
  private long lastFinishedSize;
 
  private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      super.handleMessage(msg);
      switch (msg.what) {
        case WHAT_INCREACE:
          updateView();
          break;
        case WHAT_GET_FILENAME:
          tv_download_file_info.setText("下載路徑:" + fileName);
          break;
        case WHAI_CONNECT_FAIL:
          tv_download_file_info.setText("連接失敗");
          break;
        case WHAT_IO_EXCEPTION:
          tv_download_file_info.setText("IO異常");
          break;
      }
    }
  };
 
  /**
   * 更新視圖
   */
  private void updateView() {
    //當前已經完成下載的文件大小
    long currentFinishedSize = downUtil.getFinishedSize();
    //要顯示的下載信息的文字
    StringBuilder downloadInfo = new StringBuilder();
    tv_download_speed.setText("當前下載速度:" + formateSize(currentFinishedSize - lastFinishedSize) + "/s");
    //本次統計比上次統計增加了,更新視圖,本次統計較上次統計沒有變化,不做視圖更新
    if (currentFinishedSize > lastFinishedSize) {
      lastFinishedSize = currentFinishedSize;
      downloadInfo.append("下載進度:" + formateSize(currentFinishedSize) + "/" + formateSize(downUtil.fileSize) + "\n");
      for (int i = 0; i < threadNum; i++) {
        ThreadDownloadInfoBean bean = MapUtil.map.get(i);
        if (bean.downloadTotalSize != 0) {
          downloadInfo.append("線程" + i + "下載進度:" + bean.downloadedSize * 100 / bean.downloadTotalSize + "%  " + formateSize(bean.downloadedSize) + "/" + formateSize(bean.downloadTotalSize) + "\n");
        }
      }
      np_download.setProgress((int) (currentFinishedSize * 100 / downUtil.fileSize));
      tv_download_speed_info.setText(downloadInfo);
      //下載完成後
      if (currentFinishedSize >= downUtil.fileSize) {
        tv_download_speed.setText("下載完畢");
        handler.removeMessages(WHAT_INCREACE);
        return;
      }
    }
    handler.sendEmptyMessageDelayed(WHAT_INCREACE, 1000);
  }
 
  /**
   * 返回子類要顯示的佈局 R.layout......
   */
  @Override
  protected int childView() {
    return R.layout.activity_download;
  }
 
  /**
   * 強制子類實現該抽象方法,初始化各自的View
   */
  @Override
  protected void findChildView() {
    et_download_url = (EditText) findViewById(R.id.et_download_url);
    btn_download_geturl = (Button) findViewById(R.id.btn_download_geturl);
    np_download = (NumberProgressView) findViewById(R.id.np_download);
    btn_download_start = (Button) findViewById(R.id.btn_download_start);
    btn_download_pause = (Button) findViewById(R.id.btn_download_pause);
    btn_download_cancel = (Button) findViewById(R.id.btn_download_cancel);
    tv_download_file_info = (TextView) findViewById(R.id.tv_download_file_info);
    tv_download_speed = (TextView) findViewById(R.id.tv_download_speed);
    tv_download_speed_info = (TextView) findViewById(R.id.tv_download_speed_info);
  }
 
  /**
   * 強制子類實現該抽象方法,初始化各自數據
   */
  @Override
  protected void initChildData() {
    downUtil = new DownUtil(this);
    lastFinishedSize = downUtil.getFinishedSize();
    StringBuilder downloadInfo = new StringBuilder();
    fileName = SPUtil.getInstance(this).getString(FILE_NAME, null);
    if (fileName != null) {
      downloadInfo.append("下載路徑:" + fileName);
      if (downUtil.downloadState == DownUtil.STATE_FINISH) {
        np_download.setProgress(100);
      } else if (downUtil.downloadState == DownUtil.STATE_PAUSING) {
        np_download.setProgress((int) (downUtil.getFinishedSize() * 100 / downUtil.fileSize));
      }
      tv_download_file_info.setText(downloadInfo);
    }
  }
 
  /**
   * 強制子類實現該抽象方法,設置自己的監聽器
   */
  @Override
  protected View[] setChildListener() {
    downUtil.setDownloadInfoListener(new DownUtil.DownloadInfoListener() {
 
      @Override
      public void connectSuccess() {
        //不能在此更新,需要到主線程刷新UI
        handler.sendEmptyMessage(WHAT_GET_FILENAME);
        handler.sendEmptyMessage(WHAT_INCREACE);
      }
 
      @Override
      public void connectFail() {
        handler.sendEmptyMessage(WHAI_CONNECT_FAIL);
      }
 
      @Override
      public void IOException() {
        handler.sendEmptyMessage(WHAT_IO_EXCEPTION);
      }
    });
    return new View[]{btn_download_start, btn_download_pause, btn_download_geturl, btn_download_cancel};
  }
 
  /**
   * 子類在這個方法裏面實現自己View的點擊監聽事件的相應
   *
   * @param v 父類傳遞到子類的點擊的View
   */
  @Override
  protected void clickChildView(View v) {
    switch (v.getId()) {
 
      case R.id.btn_download_start:
        downUtil.clickDownloadBtn();
        break;
 
      case R.id.btn_download_pause:
        downUtil.clickPauseBtn();
        handler.removeMessages(WHAT_INCREACE);
        break;
 
      case R.id.btn_download_cancel:
        //停止發送消息
        handler.removeMessages(WHAT_INCREACE);
        //暫停下載
        downUtil.clickPauseBtn();
        //重置狀態
        downUtil.downloadState = DownUtil.STATE_READY;
        //統計下載的大小歸零
        lastFinishedSize = 0;
        //重置文本信息
        tv_download_speed.setText("");
        tv_download_file_info.setText("");
        tv_download_speed_info.setText("");
        //進度條歸零
        np_download.setProgress(0);
        //清空內存數據
        MapUtil.map.clear();
        //sp清理
        SPUtil.getInstance(this).removeAll();
        //清空數據庫
        downUtil.finalDb.deleteAll(ThreadDownloadInfoBean.class);
        //刪除文件
        if (fileName != null) {
          File file = new File(fileName);
          if (file.exists()) {
            boolean delete = file.delete();
            if (delete) {
              Utils.ToastS(this, "刪除" + fileName + "成功");
            } else {
              Utils.ToastS(this, "刪除失敗");
            }
          } else {
            Utils.ToastS(this, "文件不存在");
          }
        } else {
          Utils.ToastS(this, "文件不存在");
        }
        break;
 
      case R.id.btn_download_geturl:
        String editTextUrl = et_download_url.getText().toString();
        if (editTextUrl.trim().equals("")) {
          Utils.ToastS(this, "請輸入地址");
        } else {
          url = editTextUrl;
        }
        break;
    }
  }
 
  @Override
  protected void onDestroy() {
    handler.removeCallbacksAndMessages(null);
    downUtil.clickPauseBtn();
    super.onDestroy();
  }
 
  /**
   * 格式化數據大小
   */
  private String formateSize(long size) {
    return Formatter.formatFileSize(this, size);
  }
 
}

佈局

<?xml version="1.0" encoding="utf-8"?>
<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:orientation="vertical"
  tools:context="com.example.testdemo.activity.DownloadActivity">
 
  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">
 
    <EditText
      android:id="@+id/et_download_url"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_weight="1"
      android:hint="輸入下載地址"/>
 
    <Button
      android:id="@+id/btn_download_geturl"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="確定"/>
 
  </LinearLayout>
 
  <com.example.testdemo.view.NumberProgressView
    android:id="@+id/np_download"
    android:layout_width="match_parent"
    android:layout_height="100dp">
  </com.example.testdemo.view.NumberProgressView>
 
  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">
 
    <Button
      android:id="@+id/btn_download_start"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="開始下載"/>
 
    <Button
      android:id="@+id/btn_download_pause"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="暫停下載"/>
 
    <Button
      android:id="@+id/btn_download_cancel"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="取消下載"/>
 
  </LinearLayout>
 
  <TextView
    android:id="@+id/tv_download_file_info"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>
 
  <TextView
    android:id="@+id/tv_download_speed"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>
 
  <TextView
    android:id="@+id/tv_download_speed_info"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>
 
</LinearLayout>

下載Activity的父類

public abstract class BaseActivity extends Activity {
 
  /**
   * 點擊監聽器,子類也可以使用
   */
  protected BaseOnClickListener onClickListener = new BaseOnClickListener();
 
  /**
   * 在onCreate方法裏面,找到視圖,初始化數據,設置點擊監聽器
   */
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(childView());
    findView();
    initData();
    setListener();
  }
 
  /**
   * 返回子類要顯示的佈局 R.layout......
   */
  protected abstract int childView();
 
  /**
   * 找到需要的視圖---網址:https://www.buzzingandroid.com/tools/android-layout-finder/
   */
  private void findView() {
    findChildView();
  }
 
  /**
   * 初始化數據
   */
  private void initData() {
    // 初始化子類的數據
    initChildData();
  }
 
  /**
   * 對需要設置監聽 的視圖設置監聽
   */
  private void setListener() {
    // 子類實現setChildListene()方法,返回一個View數組,裏面包含所有需要設置點擊監聽的View,然後進行For循環,對裏面所有View設置監聽器
    if (setChildListener() == null || setChildListener().length == 0) {
      return;
    }
    View[] viewArray = setChildListener();
    for (View view : viewArray) {
      view.setOnClickListener(onClickListener);
    }
  }
 
  /**
   * 自定義的點擊監聽類
   */
  protected class BaseOnClickListener implements View.OnClickListener {
    @Override
    public void onClick(View v) {
      clickChildView(v);
    }
  }
 
  /**
   * 強制子類實現該抽象方法,初始化各自的View
   */
  protected abstract void findChildView();
 
  /**
   * 強制子類實現該抽象方法,初始化各自數據
   */
  protected abstract void initChildData();
 
  /**
   * 強制子類實現該抽象方法,設置自己的監聽器
   */
  protected abstract View[] setChildListener();
 
  /**
   * 子類在這個方法裏面實現自己View的點擊監聽事件的相應
   *
   * @param v 父類傳遞到子類的點擊的View
   */
 
  protected abstract void clickChildView(View v);
 
}

得到數據庫的操作類

public class DBUtil {
 
  public static FinalDb getFinalDb(final Context context) {
 
    FinalDb finalDb = FinalDb.create(context, "REMUXING.db", false, 1, new FinalDb.DbUpdateListener() {
      @Override
      public void onUpgrade(SQLiteDatabase db, int oldVirsion, int newVirsion) {
 
      }
    });
    return finalDb;
  }
}

內存中存儲下載信息的類

public class MapUtil {
  public static HashMap<Integer, ThreadDownloadInfoBean> map = new HashMap<>();
}

SP存儲的工具類

public enum SPUtil {
 
  SPUTIL;
 
  public static final String NOTIFICATIONBAR_HEIGHT = "notificationbar_height";
 
  private static SharedPreferences sp;
 
  public static SPUtil getInstance(Context context) {
 
    if (sp == null) {
      sp = context.getSharedPreferences("REMUXING", Context.MODE_PRIVATE);
    }
    return SPUTIL;
  }
 
  public void saveLong(String key, Long value) {
    sp.edit().putLong(key, value).apply();
  }
 
  public Long getLong(String key, Long defValue) {
    return sp.getLong(key, defValue);
  }
 
  public void saveString(String key, String value) {
    sp.edit().putString(key, value).apply();
  }
 
  public String getString(String key, String defValue) {
    return sp.getString(key, defValue);
  }
 
  public void removeAll() {
    sp.edit().clear().apply();
  }
 
}

工具類

public class Utils {
 
  /**
   * 打印
   */
  public static void Print(String message) {
    Log.e("TAG", "-----   " + message + "   ------") ;
  }
 
  /**
   * 短吐司
   */
  public static void ToastS(Context context, String message) {
    Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
  }
}

進度條:這個可以忽略,用安卓原生的,代碼看我的另一篇博客:Android自定義View實現水平帶數字百分比進度條

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持神馬文庫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章