国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android編程開發實現多線程斷點續傳下載器實例

2020-04-11 11:04:36
字體:
來源:轉載
供稿:網友

本文實例講述了Android編程開發實現多線程斷點續傳下載器。分享給大家供大家參考,具體如下:

使用多線程斷點續傳下載器在下載的時候多個線程并發可以占用服務器端更多資源,從而加快下載速度,在下載過程中記錄每個線程已拷貝數據的數量,如果下載中斷,比如無信號斷線、電量不足等情況下,這就需要使用到斷點續傳功能,下次啟動時從記錄位置繼續下載,可避免重復部分的下載。這里采用數據庫來記錄下載的進度。

效果圖

 

斷點續傳

1.斷點續傳需要在下載過程中記錄每條線程的下載進度
2.每次下載開始之前先讀取數據庫,查詢是否有未完成的記錄,有就繼續下載,沒有則創建新記錄插入數據庫
3.在每次向文件中寫入數據之后,在數據庫中更新下載進度
4.下載完成之后刪除數據庫中下載記錄

Handler傳輸數據

這個主要用來記錄百分比,每下載一部分數據就通知主線程來記錄時間

1.主線程中創建的View只能在主線程中修改,其他線程只能通過和主線程通信,在主線程中改變View數據
2.我們使用Handler可以處理這種需求

主線程中創建Handler,重寫handleMessage()方法

新線程中使用Handler發送消息,主線程即可收到消息,并且執行handleMessage()方法

動態生成新View

可實現多任務下載

1.創建XML文件,將要生成的View配置好
2.獲取系統服務LayoutInflater,用來生成新的View

復制代碼 代碼如下:
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);

3.使用inflate(int resource, ViewGroup root)方法生成新的View
4.調用當前頁面中某個容器的addView,將新創建的View添加進來

示例

進度條樣式 download.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"  android:layout_width="fill_parent"  android:layout_height="wrap_content"  >  <LinearLayout    android:orientation="vertical"    android:layout_width="fill_parent"    android:layout_height="wrap_content"    android:layout_weight="1"    >    <!--進度條樣式默認為圓形進度條,水平進度條需要配置style屬性,    ?android:attr/progressBarStyleHorizontal -->    <ProgressBar      android:layout_width="fill_parent"      android:layout_height="20dp"      style="?android:attr/progressBarStyleHorizontal"      />    <TextView      android:layout_width="wrap_content"      android:layout_height="wrap_content"      android:layout_gravity="center"      android:text="0%"      />  </LinearLayout>  <Button    android:layout_width="40dp"    android:layout_height="40dp"    android:onClick="pause"    android:text="||"    /></LinearLayout>

頂部樣式 main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  android:orientation="vertical"  android:layout_width="fill_parent"  android:layout_height="fill_parent"  android:id="@+id/root"  >  <TextView    android:layout_width="fill_parent"    android:layout_height="wrap_content"    android:text="請輸入下載路徑"    />  <LinearLayout    android:layout_width="fill_parent"    android:layout_height="wrap_content"    android:layout_marginBottom="30dp"    >    <EditText      android:id="@+id/path"      android:layout_width="fill_parent"      android:layout_height="wrap_content"      android:singleLine="true"      android:layout_weight="1"      />    <Button      android:layout_width="wrap_content"      android:layout_height="wrap_content"      android:text="下載"      android:onClick="download"      />  </LinearLayout></LinearLayout>

MainActivity.java

public class MainActivity extends Activity {  private LayoutInflater inflater;  private LinearLayout rootLinearLayout;  private EditText pathEditText;  @Override  public void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.main);    //動態生成新View,獲取系統服務LayoutInflater,用來生成新的View    inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);    rootLinearLayout = (LinearLayout) findViewById(R.id.root);    pathEditText = (EditText) findViewById(R.id.path);    // 窗體創建之后, 查詢數據庫是否有未完成任務, 如果有, 創建進度條等組件, 繼續下載    List<String> list = new InfoDao(this).queryUndone();    for (String path : list)      createDownload(path);  }  /**   * 下載按鈕   * @param view   */  public void download(View view) {    String path = "http://192.168.1.199:8080/14_Web/" + pathEditText.getText().toString();    createDownload(path);  }  /**   * 動態生成新View   * 初始化表單數據   * @param path   */  private void createDownload(String path) {    //獲取系統服務LayoutInflater,用來生成新的View    LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);    LinearLayout linearLayout = (LinearLayout) inflater.inflate(R.layout.download, null);    LinearLayout childLinearLayout = (LinearLayout) linearLayout.getChildAt(0);    ProgressBar progressBar = (ProgressBar) childLinearLayout.getChildAt(0);    TextView textView = (TextView) childLinearLayout.getChildAt(1);    Button button = (Button) linearLayout.getChildAt(1);    try {      button.setOnClickListener(new MyListener(progressBar, textView, path));      //調用當前頁面中某個容器的addView,將新創建的View添加進來      rootLinearLayout.addView(linearLayout);    } catch (Exception e) {      e.printStackTrace();    }  }  private final class MyListener implements OnClickListener {    private ProgressBar progressBar;    private TextView textView;    private int fileLen;    private Downloader downloader;    private String name;    /**     * 執行下載     * @param progressBar //進度條     * @param textView //百分比     * @param path //下載文件路徑     */    public MyListener(ProgressBar progressBar, TextView textView, String path) {      this.progressBar = progressBar;      this.textView = textView;      name = path.substring(path.lastIndexOf("/") + 1);      downloader = new Downloader(getApplicationContext(), handler);      try {        downloader.download(path, 3);      } catch (Exception e) {        e.printStackTrace();        Toast.makeText(getApplicationContext(), "下載過程中出現異常", 0).show();        throw new RuntimeException(e);      }    }    //Handler傳輸數據    private Handler handler = new Handler() {      @Override      public void handleMessage(Message msg) {        switch (msg.what) {          case 0:            //獲取文件的大小            fileLen = msg.getData().getInt("fileLen");            //設置進度條最大刻度:setMax()            progressBar.setMax(fileLen);            break;          case 1:            //獲取當前下載的總量            int done = msg.getData().getInt("done");            //當前進度的百分比            textView.setText(name + "/t" + done * 100 / fileLen + "%");            //進度條設置當前進度:setProgress()            progressBar.setProgress(done);            if (done == fileLen) {              Toast.makeText(getApplicationContext(), name + " 下載完成", 0).show();              //下載完成后退出進度條              rootLinearLayout.removeView((View) progressBar.getParent().getParent());            }            break;        }      }    };    /**     * 暫停和繼續下載     */    public void onClick(View v) {      Button pauseButton = (Button) v;      if ("||".equals(pauseButton.getText())) {        downloader.pause();        pauseButton.setText("▶");      } else {        downloader.resume();        pauseButton.setText("||");      }    }  }}

Downloader.java

public class Downloader {  private int done;  private InfoDao dao;  private int fileLen;  private Handler handler;  private boolean isPause;  public Downloader(Context context, Handler handler) {    dao = new InfoDao(context);    this.handler = handler;  }  /**   * 多線程下載   * @param path 下載路徑   * @param thCount 需要開啟多少個線程   * @throws Exception   */  public void download(String path, int thCount) throws Exception {    URL url = new URL(path);    HttpURLConnection conn = (HttpURLConnection) url.openConnection();    //設置超時時間    conn.setConnectTimeout(3000);    if (conn.getResponseCode() == 200) {      fileLen = conn.getContentLength();      String name = path.substring(path.lastIndexOf("/") + 1);      File file = new File(Environment.getExternalStorageDirectory(), name);      RandomAccessFile raf = new RandomAccessFile(file, "rws");      raf.setLength(fileLen);      raf.close();      //Handler發送消息,主線程接收消息,獲取數據的長度      Message msg = new Message();      msg.what = 0;      msg.getData().putInt("fileLen", fileLen);      handler.sendMessage(msg);      //計算每個線程下載的字節數      int partLen = (fileLen + thCount - 1) / thCount;      for (int i = 0; i < thCount; i++)        new DownloadThread(url, file, partLen, i).start();    } else {      throw new IllegalArgumentException("404 path: " + path);    }  }  private final class DownloadThread extends Thread {    private URL url;    private File file;    private int partLen;    private int id;    public DownloadThread(URL url, File file, int partLen, int id) {      this.url = url;      this.file = file;      this.partLen = partLen;      this.id = id;    }    /**     * 寫入操作     */    public void run() {      // 判斷上次是否有未完成任務      Info info = dao.query(url.toString(), id);      if (info != null) {        // 如果有, 讀取當前線程已下載量        done += info.getDone();      } else {        // 如果沒有, 則創建一個新記錄存入        info = new Info(url.toString(), id, 0);        dao.insert(info);      }      int start = id * partLen + info.getDone(); // 開始位置 += 已下載量      int end = (id + 1) * partLen - 1;      try {        HttpURLConnection conn = (HttpURLConnection) url.openConnection();        conn.setReadTimeout(3000);        //獲取指定位置的數據,Range范圍如果超出服務器上數據范圍, 會以服務器數據末尾為準        conn.setRequestProperty("Range", "bytes=" + start + "-" + end);        RandomAccessFile raf = new RandomAccessFile(file, "rws");        raf.seek(start);        //開始讀寫數據        InputStream in = conn.getInputStream();        byte[] buf = new byte[1024 * 10];        int len;        while ((len = in.read(buf)) != -1) {          if (isPause) {            //使用線程鎖鎖定該線程            synchronized (dao) {              try {                dao.wait();              } catch (InterruptedException e) {                e.printStackTrace();              }            }          }          raf.write(buf, 0, len);          done += len;          info.setDone(info.getDone() + len);          // 記錄每個線程已下載的數據量          dao.update(info);          //新線程中用Handler發送消息,主線程接收消息          Message msg = new Message();          msg.what = 1;          msg.getData().putInt("done", done);          handler.sendMessage(msg);        }        in.close();        raf.close();        // 刪除下載記錄        dao.deleteAll(info.getPath(), fileLen);      } catch (IOException e) {        e.printStackTrace();      }    }  }  //暫停下載  public void pause() {    isPause = true;  }  //繼續下載  public void resume() {    isPause = false;    //恢復所有線程    synchronized (dao) {      dao.notifyAll();    }  }}

Dao:

DBOpenHelper:

public class DBOpenHelper extends SQLiteOpenHelper {  public DBOpenHelper(Context context) {    super(context, "download.db", null, 1);  }  @Override  public void onCreate(SQLiteDatabase db) {    db.execSQL("CREATE TABLE info(path VARCHAR(1024), thid INTEGER, done INTEGER, PRIMARY KEY(path, thid))");  }  @Override  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  }}

InfoDao:

public class InfoDao {  private DBOpenHelper helper;  public InfoDao(Context context) {    helper = new DBOpenHelper(context);  }  public void insert(Info info) {    SQLiteDatabase db = helper.getWritableDatabase();    db.execSQL("INSERT INTO info(path, thid, done) VALUES(?, ?, ?)", new Object[] { info.getPath(), info.getThid(), info.getDone() });  }  public void delete(String path, int thid) {    SQLiteDatabase db = helper.getWritableDatabase();    db.execSQL("DELETE FROM info WHERE path=? AND thid=?", new Object[] { path, thid });  }  public void update(Info info) {    SQLiteDatabase db = helper.getWritableDatabase();    db.execSQL("UPDATE info SET done=? WHERE path=? AND thid=?", new Object[] { info.getDone(), info.getPath(), info.getThid() });  }  public Info query(String path, int thid) {    SQLiteDatabase db = helper.getWritableDatabase();    Cursor c = db.rawQuery("SELECT path, thid, done FROM info WHERE path=? AND thid=?", new String[] { path, String.valueOf(thid) });    Info info = null;    if (c.moveToNext())      info = new Info(c.getString(0), c.getInt(1), c.getInt(2));    c.close();    return info;  }  public void deleteAll(String path, int len) {    SQLiteDatabase db = helper.getWritableDatabase();    Cursor c = db.rawQuery("SELECT SUM(done) FROM info WHERE path=?", new String[] { path });    if (c.moveToNext()) {      int result = c.getInt(0);      if (result == len)        db.execSQL("DELETE FROM info WHERE path=? ", new Object[] { path });    }  }  public List<String> queryUndone() {    SQLiteDatabase db = helper.getWritableDatabase();    Cursor c = db.rawQuery("SELECT DISTINCT path FROM info", null);    List<String> pathList = new ArrayList<String>();    while (c.moveToNext())      pathList.add(c.getString(0));    c.close();    return pathList;  }}

希望本文所述對大家Android程序設計有所幫助。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 铁岭市| 辽阳县| 泽州县| 吉林市| 万全县| 休宁县| 枞阳县| 呼玛县| 凉城县| 壤塘县| 洛浦县| 青铜峡市| 英德市| 四川省| 罗江县| 广水市| 安义县| 汪清县| 合水县| 镇雄县| 宁海县| 西华县| 嘉荫县| 德江县| 石柱| 双城市| 肇源县| 桐柏县| 岳池县| 西安市| 博客| 东阳市| 哈尔滨市| 新平| 巫山县| 尼玛县| 尼玛县| 嘉义市| 涿州市| 广元市| 巢湖市|