91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android原生實現多線程斷點下載實例代碼

發布時間:2020-09-26 01:45:57 來源:腳本之家 閱讀:140 作者:Shan can can 欄目:移動開發

各位父老鄉親,我單漢三又回來了,今天為大家帶來一個用原生的安卓寫的多線程斷點下載Demo。

通過本文你可以學習到:

  1. SQLite的基本使用,數據庫的增刪改查。
  2. Handler的消息處理與更新UI。
  3. Service(主要用于下載)的進階與使用。
  4. 原生的json文件解析(多層嵌套)。
  5. RandomAccessFile的基本使用,可以將文件分段。
  6. 基于HttpURLConnection的大文件下載。
  7. 上面內容結合,實現多線程,斷點下載。

Demo是在TV上運行的,圖片顯示的問題不要糾結了。

Android原生實現多線程斷點下載實例代碼

文件下載的Demo已完成,沒時間上傳與講解,今天為您展示并講解一下,純原生的東西來下載文件,希望可以幫你理解更多安卓比較基礎的問題。

我們的思路:建立一個數據庫,兩個表,一個用來保存網絡數據,一個保存本地下載的進度等等。在點擊下載按鈕的時候啟動DownloadService,進行比對之后下載

先看一下Demo的目錄結構:

Android原生實現多線程斷點下載實例代碼

所有的步驟在代碼里有非常詳細的講解,一定要看代碼(下面是抽取的幾個重要的類講解)!

數據庫的建立與DAO

/**
 * Created by Administrator on 2017/3/6 0006.
 */
public class DownLoadDBHelper extends SQLiteOpenHelper {
  /**
   * DownLoadDBHelper用于創建數據庫,如果不會使用原生的建庫的話
   *
   * 跟隨小司機我的腳步來一起練一練
   * 建兩個表:
   * download_info表存儲下載信息
   * localdownload_info表存儲本地下載信息
   * 之后對比兩個表進行繼續下載等等
   */
  public static String DATABASE_NAME = "downloadFILES.db";
  public static String TABLE_DOWNLOAD_INFO = "download_info";
  public static String TABLE_LOCALDOWNLOAD_INFO = "localdownload_info";
  private static int version = 1;
  public DownLoadDBHelper(Context context) {
    super(context, DATABASE_NAME, null, version);
  }
  @Override
  public void onCreate(SQLiteDatabase db) {
    /*在此進行創建數據庫和表格,來一起動手寫一遍,就是兩個sqlite語句*/
    db.execSQL("create table " + TABLE_DOWNLOAD_INFO + "(" + "id integer PRIMARY KEY AUTOINCREMENT," +
        "thread_id integer," + "start_position integer," + "end_position integer," + " completed_size integer," + "url varchar(100))");
    db.execSQL("create table " + TABLE_LOCALDOWNLOAD_INFO + "(" + "id integer PRIMARY KEY AUTOINCREMENT," + "name varchar(50)," +
        "url varchar(100)," + "completedSize integer," + "fileSize integer," + "status integer)");
  }
  @Override
  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    /*數據庫更新升級,檢測到版本的變化,發現版本號不一樣,就會自動調用onUpgrade函數
    * 新版本號和老版本號都會作為onUpgrade函數的參數傳進來,便于開發者知道數據庫應該從哪個版本升級到哪個版本。
    * */
    String sql = "drop table if exists " + TABLE_DOWNLOAD_INFO + "";
    String sqlOne = "drop table if exists " + TABLE_LOCALDOWNLOAD_INFO + "";
    db.execSQL(sql);
    db.execSQL(sqlOne);
    onCreate(db);//刪除數據庫,重新創建。這里只是簡單的,并沒有添加或者減少數據庫中的其他字段
  }
}

DAO對數據庫進行增刪改查

/**
 * Created by ShanCanCan on 2017/3/6 0006.
 */
public class Dao {
  /*
  * DAO層主要是做數據持久層的工作,負責與數據庫進行聯絡的一些任務都封裝在此。
  * DAO層所定義的接口里的方法都大同小異,這是由我們在DAO層對數據庫訪問的操作來決定的,
  * 對數據庫的操作,我們基本要用到的就是新增,更新,刪除,查詢等方法。
  * 因而DAO層里面基本上都應該要涵蓋這些方法對應的操作。
  * */
  private static Dao dao;
  private static DownLoadDBHelper dbHelper;
  public static final byte[] Lock = new byte[0]; //新建兩個字節作為對象鎖
  public static final byte[] file_Lock = new byte[0];
  public Dao() {//空構造方法,
  }
  public static synchronized Dao getInstance(Context context) {//本demo用單例模式中的懶漢模式+線程不安全 線程安全的代價是效率變低
    if (dao == null) {
      dao = new Dao();
      dbHelper = new DownLoadDBHelper(context);
    }
    return dao;
  }
  /* public static synchronized Dao getInstance(Context context) {//本demo用單例模式中的懶漢模式+線程安全 線程安全的代價是效率變低,99%情況下不需要同步
    if (dao == null) { //你可以在這兩個方法中隨便選擇一個
      dao = new Dao();
      dbHelper = new DownLoadDBHelper(context);
    }
    return dao;
  }*/
  /***************************************  下方Dao層中對數據庫的增、刪、改、查  *********************************************************/
  /**
   * 檢查本地下載記錄,是否下載過
   *
   * @param url
   * @return
   */
  public boolean isExist(String url) {
    SQLiteDatabase database = dbHelper.getReadableDatabase(); //獲取本app所創建的數據庫
    String sql = "select count(*) from " + TABLE_LOCALDOWNLOAD_INFO + " where url=?"; //查詢語句,查詢總共有多少條的語句
    Cursor cursor = database.rawQuery(sql, new String[]{url});
    /**
     *
     * @Cursor
     * Cursor 是每行的集合。
     * 使用 moveToFirst() 定位第一行。
     * 你必須知道每一列的名稱。
     * 你必須知道每一列的數據類型。
     * Cursor 是一個隨機的數據源。
     * 所有的數據都是通過下標取得。
     * Cursor按照我的理解就是一個箭頭,指到哪一行就是那一行的集合
     * 比較重要的方法有:close(),moveToFirst(),moveToNext(),moveToLast(),moveToPrevious(),getColumnCount()等。
     *
     * @rawQuery
     * rawQuery是直接使用SQL語句進行查詢的,也就是第一個參數字符串,
     * 在字符串內的“?”會被后面的String[]數組逐一對換掉
     * cursor用完之后要關閉,cursor用完之后要關閉,cursor用完之后要關閉。重要的事情說三遍!!!
     *
     * */
    cursor.moveToFirst();
    int count = cursor.getInt(0);
    cursor.close();
    return count > 0;
  }
  /**
   * 是否為首次下載
   *
   * @param url
   * @return
   */
  public boolean isFirstDownload(String url) {
    SQLiteDatabase database = dbHelper.getReadableDatabase();
    String sql = "select count(*) from " + TABLE_DOWNLOAD_INFO + " where url=?";
    Cursor cursor = database.rawQuery(sql, new String[]{url});
    cursor.moveToFirst();
    int count = cursor.getInt(0);
    cursor.close();
    return count == 0;
  }
  /**
   * 保存下載的具體信息 保存所下載的list集合中的數據
   *
   * @param infos
   * @param context
   */
  public void saveInfos(List<DownLoadInfo> infos, Context context) {
    /**
     * 事務(Transaction)是并發控制的單位,是用戶定義的一個操作序列。
     * 這些操作要么都做,要么都不做,是一個不可分割的工作單位。
     * 通過事務,SQL Server能將邏輯相關的一組操作綁定在一起,
     * 以便保持數據的完整性。
     *
     * 事務具有四個特征:原子性( Atomicity )、一致性( Consistency )、
     * 隔離性( Isolation )和持續性( Durability )。這四個特性簡稱為 ACID 特性。
     *
     * */
    synchronized (Lock) {
      SQLiteDatabase database = dbHelper.getWritableDatabase();
      database.beginTransaction();//開啟事務
      try {//如果有異常,在這里捕獲
        for (DownLoadInfo info : infos) {//for循環將數據存入數據庫
          String sql = "insert into " + TABLE_DOWNLOAD_INFO + "(thread_id,start_position, end_position, completed_size, url) values (?,?,?,?,?)";
          Object[] bindArgs = {info.getThreadId(), info.getStartPosition(), info.getEndPosition(), info.getCompletedSize(), info.getUrl()};
          database.execSQL(sql, bindArgs);
        }
        database.setTransactionSuccessful();//結束事務
      } catch (SQLException e) {
        e.printStackTrace();
      } finally {
        database.endTransaction();//關閉事務
      }
    }
  }
  /**
   * 得到下載具體信息
   *
   * @param urlstr
   * @return List<DownloadInfo> 一個下載器信息集合器,里面存放了每條線程的下載信息
   */
  public List<DownLoadInfo> getInfos(String urlstr) {
    List<DownLoadInfo> list = new ArrayList<DownLoadInfo>();
    SQLiteDatabase database = dbHelper.getReadableDatabase();
    String sql = "select thread_id, start_position, end_position, completed_size, url from " + TABLE_DOWNLOAD_INFO + " where url=?";
    Cursor cursor = database.rawQuery(sql, new String[]{urlstr});
    while (cursor.moveToNext()) {//通過cursor取到下載器信息,循環遍歷,得到下載器集合
      DownLoadInfo info = new DownLoadInfo(cursor.getInt(0), cursor.getInt(1), cursor.getInt(2), cursor.getInt(3), cursor.getString(4));
      list.add(info);
    }
    cursor.close();
    return list;
  }
  /**
   * 本地下載列表添加記錄,添加本地數據庫信息,完成度等等
   *
   * @param fileStatus
   **/
  public void insertFileStatus(FileStatus fileStatus) {
    synchronized (file_Lock) {//異步加開啟事務,保證數據的完整性
      SQLiteDatabase database = dbHelper.getWritableDatabase();
      database.beginTransaction();
      try {
        String sql = "insert into " + TABLE_LOCALDOWNLOAD_INFO + " (name,url,completedSize,fileSize,status) values(?,?,?,?,?)";
        Object[] bindArgs = {fileStatus.getName(), fileStatus.getUrl(), fileStatus.getCompletedSize(), fileStatus.getFileSize(), fileStatus.getStatus()};
        database.execSQL(sql, bindArgs);
        database.setTransactionSuccessful();
      } catch (SQLException e) {
        e.printStackTrace();
      } finally {
        database.endTransaction();
      }
    }
  }
  /**
   * @param context
   * @param compeletedSize
   * @param threadId
   * @param urlstr     這里是更新數據庫,建議在保存一個表格的時候就對另一個表格數據庫進行更新
   */
  public void updataInfos(int threadId, int compeletedSize, String urlstr, Context context) {
    synchronized (Lock) {
      String sql = "update " + TABLE_DOWNLOAD_INFO + "set completed_size = ? where thread_id =? and url=?";
      String localSql = "update " + TABLE_LOCALDOWNLOAD_INFO + "set completedSize = (select sum(completed_size) from " +
          TABLE_DOWNLOAD_INFO + "where url=? group by url ) where url=?";
      Object[] bindArgs = {compeletedSize, threadId, urlstr};
      Object[] localArgs = {urlstr, urlstr};
      SQLiteDatabase database = dbHelper.getWritableDatabase();
      database.beginTransaction();
      try {
        database.execSQL(sql, bindArgs);
        database.execSQL(localSql, localArgs);
        database.setTransactionSuccessful();
      } catch (SQLException e) {
        e.printStackTrace();
      } finally {
        database.endTransaction();
      }
    }
  }
  /**
   * @param url 更新文件的狀態,0為正在下載,1為已經下載完成,2為下載出錯
   **/
  public void updateFileStatus(String url) {
    synchronized (file_Lock) {
      String sql = "update " + TABLE_LOCALDOWNLOAD_INFO + " set status = ? where url = ?";
      Object[] bindArgs = {1, url};
      SQLiteDatabase database = dbHelper.getWritableDatabase();
      database.beginTransaction();
      try {
        database.execSQL(sql, bindArgs);
        database.setTransactionSuccessful();
      } catch (SQLException e) {
        e.printStackTrace();
      } finally {
        database.endTransaction();
      }
    }
  }
  /**
   * @return List<FileStatus>
   * 取出本地下載列表數據,如在重新進入應用時,要重新把進度之類的設置好
   **/
  public List<FileStatus> getFileStatus() {
    List<FileStatus> list = new ArrayList<FileStatus>();
    SQLiteDatabase database = dbHelper.getReadableDatabase();
    //String sql = "slect * from " + TABLE_LOCALDOWNLOAD_INFO + ""; //不能用,需要哪些條件就在語句中寫出哪些條件
    String sql = "select name, url, status, completedSize, fileSize from " + TABLE_LOCALDOWNLOAD_INFO + "";
    Cursor cursor = database.rawQuery(sql, null);
    while (cursor.moveToNext()) {
      FileStatus fileState = new FileStatus(cursor.getString(0), cursor.getString(1), cursor.getInt(2), cursor.getInt(3), cursor.getInt(4));
      list.add(fileState);
    }
    cursor.close();
    return list;
  }
  /**
   * @param url
   * @param completeSize
   * @param status    更新文件的下載狀態
   **/
  public void updateFileDownStatus(int completeSize, int status, String url) {
    synchronized (file_Lock) {
      String sql = "update " + TABLE_LOCALDOWNLOAD_INFO + " set completedSize = ?,status = ? where url = ?";
      SQLiteDatabase database = dbHelper.getWritableDatabase();
      database.beginTransaction();
      try {
        Object[] bindArgs = {completeSize, status, url};
        database.execSQL(sql, bindArgs);
        database.delete(TABLE_DOWNLOAD_INFO, "url = ?", new String[]{url});
        database.setTransactionSuccessful();
      } catch (SQLException e) {
        e.printStackTrace();
      } finally {
        database.endTransaction();
      }
    }
  }
  /**
   * @param url 獲取文件名稱
   **/
  public String getFileName(String url) {
    String result = "";
    String sql = "select name from " + TABLE_LOCALDOWNLOAD_INFO + " where url = ?";
    SQLiteDatabase database = dbHelper.getReadableDatabase();
    Cursor cursor = database.rawQuery(sql, new String[]{url});
    if (cursor.moveToNext()) {
      result = cursor.getString(0);
    }
    cursor.close();
    return result;
  }
  /**
   * 刪除文件之后,要刪除下載的數據,一個是用戶可以重新下載
   * 另一個是表再次添加一條數據的時候不出現錯誤
   *
   * @param url
   */
  public void deleteFile(String url) {
    SQLiteDatabase database = dbHelper.getWritableDatabase();
    database.beginTransaction();
    try {
      database.delete(TABLE_DOWNLOAD_INFO, " url = ?", new String[]{url});
      database.delete(TABLE_LOCALDOWNLOAD_INFO, " url = ?", new String[]{url});
      database.setTransactionSuccessful();
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      database.endTransaction();
    }
  }
  /**
   * 關閉數據庫
   *
   * @close
   */
  public void closeDB() {
    dbHelper.close();
  }
}

DownloadService 主要代碼

@SuppressLint("HandlerLeak")
public class DownloadService extends Service
{
 public IBinder binder = new MyBinder();
 public class MyBinder extends Binder
 {
 public DownloadService getService()
 {
  return DownloadService.this;
 }
 }
 @Override
 public IBinder onBind(Intent intent)
 {
 return binder;
 }
 public static int number = 0;
 // 文件保存地址
 public final String savePath = "/mnt/sdcard/MultiFileDownload/";
 // 存放下載列表的引用
 public static List<FileStatus> list = new ArrayList<FileStatus>();
 public static Map<String, String> localDownList = new HashMap<String, String>();
 // 保存每個文件下載的下載器
 public static Map<String, Downloader> downloaders = new HashMap<String, Downloader>();
 // 每個下載文件完成的長度
 private Map<String, Integer> completeSizes = new HashMap<String, Integer>();
 // 每個下載文件的總長度
 private Map<String, Integer> fileSizes = new HashMap<String, Integer>();
 private Downloader downloader;
 private int threadCount = 5;
 private Dao dao;
 private DownLoadCallback loadCallback;
 
 private FileStatus mFileStatus = null;
 private Handler handler = new Handler()
 {
 @Override
 public void handleMessage(Message msg)
 {
  super.handleMessage(msg);
  if (msg.what == 1)
  {
  String url = (String) msg.obj;
  int length = msg.arg1;
  int completeSize = completeSizes.get(url);
  int fileSize = fileSizes.get(url);
  completeSize += length;
  completeSizes.put(url, completeSize);
  synchronized (list)
  {
   for (int i = 0; i < list.size(); i++)
   {
   FileStatus fileStatus = list.get(i);
   if (fileStatus.getUrl().equals(url))
   {
    if (completeSize == fileStatus.getFileSize())
    {
    System.out.println("-----------下載完成:"+fileStatus.getName()+":"+completeSize+"-----"+fileStatus.getFileSize());
    list.set(i, new FileStatus(fileStatus.getName(), fileStatus.getUrl(), 1, completeSize, fileStatus.getFileSize()));
    dao.updateFileDownStatus(completeSize, 1, url);
    }
    else
    {
    list.set(i, new FileStatus(fileStatus.getName(), fileStatus.getUrl(), 0, completeSize, fileStatus.getFileSize()));
    }
    mFileStatus = list.get(i);
   }
   }
   
   this.postDelayed(new Runnable()
   {
   @Override
   public void run()
   {
    if (loadCallback != null && mFileStatus != null)
    {
    loadCallback.refreshUI(mFileStatus);
    }
   }
   }, 1000);
  }
  
  }
 }
 };
 @Override
 public void onCreate()
 {
 super.onCreate();
 dao = Dao.getInstance(this);
 list = dao.getFileStatus();
 for (FileStatus fileStatus : list)
 {
  localDownList.put(fileStatus.getUrl(), fileStatus.getUrl());
 }
 
 Timer timer = new Timer();
 timer.schedule(new TimerTask()
 {
  @Override
  public void run()
  {
  number++;
  }
 }, 0, 1000);
 }
 public void download(final Button button, final String url, final String name, final Handler mHandler)
 {
 if (dao.isExist(url))
 {
  Toast.makeText(this, "別點了,已經在下載了", Toast.LENGTH_SHORT).show();
  return;
 }
 final String fileName = name + url.substring(url.lastIndexOf("."));
 new Thread(new Runnable()
 {
  @Override
  public void run()
  {
  downloader = downloaders.get(url);
  if (downloader == null)
  {
   downloader = new Downloader(url, savePath, fileName, threadCount, DownloadService.this, handler);
   downloaders.put(url, downloader);
  }
  if (downloader.isDownloading())
   return;
  
  LoadInfo loadInfo = downloader.getDownloaderInfors();
  
  if(loadInfo != null)
  {
   FileStatus fileStatus = new FileStatus(fileName, url, 0, loadInfo.getComplete(), loadInfo.getFileSize());
   dao.insertFileStatus(fileStatus);
   completeSizes.put(url, loadInfo.getComplete());
   fileSizes.put(url, fileStatus.getFileSize());
   list.add(fileStatus);
   localDownList.put(url, url);
   downloader.download();
   Message msg = new Message();
   msg.what = 1;
   msg.obj = button;
   mHandler.sendMessage(msg);
  }
  else
  {
   Message msg = new Message();
   msg.what = 2;
   msg.obj = button;
   mHandler.sendMessage(msg);
  }
  }
 }).start();
 }
 
 //暫停下載
 public void Pause(Downloader downloader)
 {
 downloader.pause();
 }
 
 //繼續下載
 public void reDownload(final Button button, final String url, final String name, final Handler mHandler)
 {
 new Thread(new Runnable()
 {
  @Override
  public void run()
  {
  String fileName = dao.getFileName(url);
  
  downloader = downloaders.get(url);
  if (downloader == null)
  {
   downloader = new Downloader(url, savePath, fileName, threadCount, DownloadService.this, handler);
   downloaders.put(url, downloader);
  }
  if (downloader.isDownloading())
   return;
  
  LoadInfo loadInfo = downloader.getDownloaderInfors();
  
  if(loadInfo != null && !fileName.equals(""))
  {
   if(!completeSizes.containsKey(url))
   {
   completeSizes.put(url, loadInfo.getComplete());
   }
   if(!fileSizes.containsKey(url))
   {
   fileSizes.put(url, loadInfo.getFileSize());
   }
   downloader.download();
   Message msg = new Message();
   msg.what = 1;
   msg.obj = button;
   mHandler.sendMessage(msg);
  }
  else
  {
   Message msg = new Message();
   msg.what = 2;
   msg.obj = button;
   mHandler.sendMessage(msg);
  }
  }
 }).start();
 }
 
 public void delete(final String url)
 {
 Downloader down = downloaders.get(url);
 if(down != null)
 {
  down.pause();
 }
 
 handler.postDelayed(new Runnable()
 {
  @Override
  public void run()
  {
  dao.deleteFile(url);
  
  for (int i = 0; i < list.size(); i++)
  {
   FileStatus fileStatus = list.get(i);
   if (fileStatus.getUrl().equals(url))
   {
   list.remove(i);
   }
  }
  
  localDownList.remove(url);
  downloaders.remove(url);
  completeSizes.remove(url);
  fileSizes.remove(url);
  
  if(loadCallback != null)
  {
   loadCallback.deleteFile(url);
  }
  }
 }, 1000);
 }
 public interface DownLoadCallback
 {
 public void refreshUI(FileStatus fileStatus);
 
 public void deleteFile(String url);
 }
 public void setLoadCallback(DownLoadCallback loadCallback)
 {
 this.loadCallback = loadCallback;
 }
}

下載工具類DownLoadUtil

/**
 * Created by ShanCanCan on 2017/3/7 0007.
 */
public class DownLoadUtil {
  /**
   * 此類的主要功能
   * 1、檢查是否下載
   * 2、下載文件,文件的下載采用httpurlconnection
   */
  private String downPath;// 下載路徑
  private String savePath;// 保存路徑
  private String fileName;// 文件名稱
  private int threadCount;// 線程數
  private Handler mHandler;
  private Dao dao;
  private Context context;
  private int fileSize;// 文件大小
  private int range;
  private List<DownLoadInfo> infos;// 存放下載信息類的集合
  private int state = INIT;
  private static final int INIT = 1;// 定義三種下載的狀態:初始化狀態,正在下載狀態,暫停狀態
  private static final int DOWNLOADING = 2;
  private static final int PAUSE = 3;
  /**
   * 構造方法,獲取dao的對象
   *
   * @param downPath
   * @param savePath
   * @param fileName
   * @param threadCount
   * @param context
   * @param mHandler
   */
  public DownLoadUtil(String downPath, String savePath, String fileName, int threadCount, Handler mHandler, Context context) {
    this.downPath = downPath;
    this.savePath = savePath;
    this.fileName = fileName;
    this.threadCount = threadCount;
    this.mHandler = mHandler;
    this.context = context;
    dao = Dao.getInstance(context);
  }
  /**
   * 判斷是否PAUSE
   **/
  public boolean isPause() {
    return state == PAUSE;
  }
  /**
   * 判斷是否DOWNLOADING
   */
  public boolean isDownloading() {
    return state == DOWNLOADING;
  }
  /**
   * @param url 判斷是否是第一次下載,利用dao查詢數據庫中是否有下載這個地址的記錄
   */
  private boolean isFirst(String url) {
    return dao.isFirstDownload(url);
  }
  /**
   * 獲取要下載的東西
   */
  public LoadItemInfo getDownloadInfos() {
    if (isFirst(downPath)) {
      if (initFirst()) {//如果是第一次下載的話,要進行初始化,1.獲得下載文件的長度 2.創建文件,設置文件的大小
        range = this.fileSize / this.threadCount;
        infos = new ArrayList<DownLoadInfo>();
        //這里就是啟動多線程下載,看出來了嗎?配合RandomAccessFile。每一個DownLoadInfo就是RandomAccessFile文件的一部分
        for (int i = 0; i < this.threadCount - 1; i++) {
          DownLoadInfo info = new DownLoadInfo(i, i * range, (i + 1) * range - 1, 0, downPath);
          infos.add(info);
        }
        DownLoadInfo info = new DownLoadInfo(this.threadCount - 1, (this.threadCount - 1) * range, this.fileSize, 0, downPath);
        infos.add(info);
        dao.saveInfos(infos, this.context);
        //(String urlDownload, int completePercent, int fileSize)
        LoadItemInfo loadInfo = new LoadItemInfo(this.downPath, 0, this.fileSize);
        return loadInfo;
      } else {
        return null;
      }
    } else {
      //不是第一次下載,我們應該怎么做呢?從數據庫里面取回來
      infos = dao.getInfos(this.downPath);
      if (infos != null && infos.size() > 0) {
        int size = 0;
        int completeSize = 0;
        for (DownLoadInfo info : infos) {
          completeSize += info.getCompletedSize();
          size += info.getEndPosition() - info.getStartPosition() + this.threadCount - 1;
        }
        LoadItemInfo loadInfo = new LoadItemInfo(this.downPath, completeSize, size);
        return loadInfo;
      } else {
        return null;
      }
    }
  }
  // 設置暫停
  public void pause() {
    state = PAUSE;
  }
  // 重置下載狀態,將下載狀態設置為init初始化狀態
  public void reset() {
    state = INIT;
  }
  /**
   * 基本上,RandomAccessFile的工作方式是,把DataInputStream和DataOutputStream結合起來,再加上它自己的一些方法,
   * 比如定位用的getFilePointer( ),在文件里移動用的seek( ),以及判斷文件大小的length( )、skipBytes()跳過多少字節數。
   * 此外,它的構造函數還要一個表示以只讀方式("r"),還是以讀寫方式("rw")打開文件的參數 (和C的fopen( )一模一樣)。它不支持只寫文件。
   */
  private boolean initFirst() {
    boolean result = true;
    HttpURLConnection conn = null;
    RandomAccessFile randomFile = null;
    URL url = null;
    try {
      url = new URL(downPath);
      conn = (HttpURLConnection) url.openConnection();
      conn.setConnectTimeout(5 * 1000);
      conn.setRequestMethod("GET");
      // 如果http返回的代碼是200或者206則為連接成功
      if (conn.getResponseCode() == 200 || conn.getResponseCode() == 206) //狀態碼(206),表示服務器已經執行完部分對資源的GET請求
      {
        fileSize = conn.getContentLength();// 得到文件的大小
        if (fileSize <= 0) {
          //("網絡故障,無法獲取文件大小");
          return false;
        }
        File dir = new File(savePath);
        // 如果文件目錄不存在,則創建
        if (!dir.exists()) {
          if (dir.mkdirs()) {
            //("mkdirs success.");
          }
        }
        File file = new File(this.savePath, this.fileName);
        randomFile = new RandomAccessFile(file, "rwd");
        randomFile.setLength(fileSize);// 設置保存文件的大小
        randomFile.close();
        conn.disconnect();
      }
    } catch (Exception e) {
      e.printStackTrace();
      result = false;
    } finally {
      if (randomFile != null) {
        try {
          randomFile.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      if (conn != null) {
        conn.disconnect();
      }
    }
    return result;
  }
  /**
   * 下面的這個方法就是開啟多線程進行下載了數據了
   */
  public void downLoad() {
    if (infos != null) {
      if (state == DOWNLOADING) {
        return;
      }
      state = DOWNLOADING;// 把狀態設置為正在下載
      for (DownLoadInfo info : infos) {//為什么說我們是多線程呢?因為我們分別用新線程去下載剛才分割好的一個RandomAccessFile文件
        new DownLoadThread(info.getThreadId(), info.getStartPosition(), info.getEndPosition(), info.getCompletedSize(), info.getUrl(), this.context).start();
      }
    }
  }
  /**
   * 現在要創建線程用來下載了,這里采用內部類
   */
  public class DownLoadThread extends Thread {
    private int threadId;
    private int startPostion;
    private int endPostion;
    private int compeletedSize;
    private String url;
    private Context context;
    public static final int PROGRESS = 1;
    public DownLoadThread(int threadId, int startPostion, int endPostion, int compeletedSize, String url, Context context) {//構造方法,傳入特定的參數
      this.threadId = threadId;
      this.startPostion = startPostion;
      this.endPostion = endPostion;
      this.compeletedSize = compeletedSize;
      this.url = url;
      this.context = context;
    }
    //開始下載
    @Override
    public void run() {
      HttpURLConnection conn = null;
      RandomAccessFile randomAccessFile = null;
      InputStream inStream = null;
      File file = new File(savePath, fileName);
      URL url = null;
      try {
        url = new URL(this.url);
        conn = (HttpURLConnection) url.openConnection();
        constructConnection(conn);
        if (conn.getResponseCode() == 200 || conn.getResponseCode() == 206) {
          randomAccessFile = new RandomAccessFile(file, "rwd");
          randomAccessFile.seek(this.startPostion + this.compeletedSize);//RandomAccessFile移動指針,到需要下載的塊
          inStream = conn.getInputStream();
          byte buffer[] = new byte[4096];//這個4096為么子呢?我也不知道,就是看阿里的人下載apk的時候都用4096,我也用
          int length = 0;
          while ((length = inStream.read(buffer, 0, buffer.length)) != -1) {
            randomAccessFile.write(buffer, 0, length);
            compeletedSize += length;
            // 更新數據庫中的下載信息
            dao.updataInfos(threadId, compeletedSize, this.url, this.context);
            // 用消息將下載信息傳給進度條,對進度條進行更新
            Message message = Message.obtain();
            message.what = PROGRESS;
            message.obj = this.url;
            message.arg1 = length;
            mHandler.sendMessage(message);// 給DownloadService發送消息
            if (state == PAUSE) {
              //("-----pause-----");
              return;
            }
          }
          // ("------------線程:" + this.threadId + "下載完成");
        }
      } catch (IOException e) {
        e.printStackTrace();
        //("-----下載異常-----"); 這里下載異常我就不處理了,你可以發一條重新下載的消息
      } finally {//用完只后流要關閉,不然容易造成資源搶占,內存泄漏
        try {
          if (inStream != null) {
            inStream.close();
          }
          if (randomAccessFile != null) {
            randomAccessFile.close();
          }
          if (conn != null) {
            conn.disconnect();
          }
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
    /**
     * 構建請求連接時的參數 返回開始下載的位置
     *
     * @param conn
     */
    private void constructConnection(HttpURLConnection conn) throws IOException {
      conn.setConnectTimeout(5 * 1000);// 設置連接超時5秒
      conn.setRequestMethod("GET");// GET方式提交,如果你是用post請求必須添加 conn.setDoOutput(true); conn.setDoInput(true);
      conn.setRequestProperty(
          "Accept",
          "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
      conn.setRequestProperty("Accept-Language", "zh-CN");
      conn.setRequestProperty("Referer", this.url);
      conn.setRequestProperty("Charset", "UTF-8");
      int startPositionNew = this.startPostion + this.compeletedSize;
      // 設置獲取實體數據的范圍
      conn.setRequestProperty("Range", "bytes=" + startPositionNew + "-" + this.endPostion);
      conn.setRequestProperty(
          "User-Agent",
          "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
      conn.setRequestProperty("Connection", "Keep-Alive");
      conn.connect();
    }
  }
}

Github地址:https://github.com/Shanlovana/DownLoadFiles/

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

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

南岸区| 枞阳县| 界首市| 柘城县| 行唐县| 福州市| 云林县| 永安市| 五寨县| 蒙城县| 乐都县| 襄城县| 新野县| 高台县| 南康市| 仲巴县| 敦化市| 安泽县| 辉县市| 和硕县| 香河县| 广饶县| 巩义市| 南昌市| 南澳县| 柘荣县| 晋城| 文水县| 井冈山市| 涡阳县| 房山区| 清流县| 汉中市| 龙门县| 疏附县| 临泉县| 花莲市| 辽中县| 黑山县| 红安县| 合川市|