Greendao多執行緒下載

TTMMJJ99發表於2017-11-22

今天跟大家一起分享下android開發中比較難的一個環節,可能很多人看到這個標題就會感覺頭很大,的確如果沒有良好的編碼能力和邏輯思維,這塊是很難搞明白的,前面2次總結中已經為大家分享過有關技術的一些基本要領,我們先一起簡單回顧下它的基本原理。

http://blog.csdn.net/shimiso/article/details/6763664  android 多執行緒斷點續傳下載 一

http://blog.csdn.net/shimiso/article/details/6763986  android 多執行緒斷點續傳下載 二

什麼是多執行緒下載?

多執行緒下載其實就是迅雷,BT一些下載原理,通過多個執行緒同時和伺服器連線,那麼你就可以榨取到較高的頻寬了,大致做法是將檔案切割成N塊,每塊交給單獨一個執行緒去下載,各自下載完成後將檔案塊組合成一個檔案,程式上要完成做切割和組裝的小演算法

什麼是斷點續傳?

斷點續傳,就是當我們下載未結束時候,退出儲存下載進度,當下次開啟繼續下載的時接著上次的進度繼續下載,不用每次下載都重新開始,那麼有關斷點續傳的原理和實現手段,可參考我以前的一篇總結http://blog.csdn.net/shimiso/article/details/5956314 裡面詳細講解http協議斷點續傳的原理,務必要看懂,否則你無法真正理解本節程式碼

怎麼完成多執行緒斷點續傳?

將兩者合二為一需要程式記住每個檔案塊的下載進度,並儲存入庫,當下載程式啟動時候你需要判斷程式是否已經下載過該檔案,並取出各個檔案塊的儲存記錄,換算出下載進度繼續下載,在這裡你需要掌握java多執行緒的基本知識,handler的使用,以及集合,演算法,檔案操作等基本技能,同時還要解決sqlite資料庫的同步問題,因為它是不太怎麼支援多執行緒操作的,控制不好經常會出現庫被鎖定的異常,同時在android2.3以後就不能activity中直接操作http,否則你將收到系統送上的NetworkOnMainThreadException異常,在UI體驗上一定記住要使用非同步完成,既然大致思路已經清楚,下面我們開始分析程式:

依賴:

apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao' // apply plugin需要新增的外掛

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.bwie.duoloadding"
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

}
//對greendao的設定
greendao {
    schemaVersion 1
    daoPackage 'com.bwie.yue2'//設定DaoMaster、DaoSession、Dao包名
    targetGenDir 'src/main/java'//設定DaoMaster、DaoSession、Dao目錄即在什麼目錄下生成
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    compile 'org.greenrobot:greendao:3.2.0' // add library 需要新增的依賴新增
}
許可權

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.bwie.duoloadding">
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />

    <application
        android:name="com.bwie.yue2.MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
User類:

[java] view plain copy
  1. @Entity//表示這個實體類一會會在資料庫中生成對應的表
    public class User {
        @Id
        private Long id;
        private Integer thread_id;
        private Integer start_pos;
        private Integer end_pos;
        private Integer compelete_size;
        private String url;

 通過builder-》make module 'module名'生成Usedao,DaoMaster,daosession

 資料庫操作要藉助單例和同步,來保證執行緒的執行順序,以免多個執行緒爭相搶用sqlite資源導致異常出現

[java] view plain copy
  1. package cn.demo.Dao;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. import android.content.Context;  
  7. import android.database.Cursor;  
  8. import android.database.sqlite.SQLiteDatabase;  
  9. import cn.demo.DBHelper.DBHelper;  
  10. import cn.demo.entity.DownloadInfo;  
  11.   
  12. /** 
  13.  *  
  14.  * 一個業務類 
  15.  */  
  16. public class Dao {
        private static Dao dao = null;
        private Context context;
    
        private Dao(Context context) {
            this.context = context;
        }
    
        public static Dao getInstance(Context context) {
            if (dao == null) {
                dao = new Dao(context);
            }
            return dao;
        }
    
        /**
         * 檢視資料庫中是否有資料
         */
        public synchronized boolean isHasInfors(String urlstr) {
            int count = -1;
            Cursor cursor = null;
            try {
                List<User> list1 = MyApplication.userDao.queryBuilder().where(UserDao.Properties.Url.eq(urlstr)).list();
                count = list1.size();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (null != cursor) {
                    cursor.close();
                }
            }
            return count == 0;
        }
    
        /**
         * 儲存 下載的具體資訊
         */
        public synchronized void saveInfos(List<DownloadInfo> infos) {
            try {
                for (DownloadInfo info : infos) {
                    User user = new User(null, info.getThreadId(), info.getStartPos(), info.getEndPos(), info.getCompeleteSize(), info.getUrl());
                    MyApplication.userDao.insert(user);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 得到下載具體資訊
         */
        public synchronized List<DownloadInfo> getInfos(String urlstr) {
            List<DownloadInfo> list = new ArrayList<DownloadInfo>();
                String sql = "select thread_id, start_pos, end_pos,compelete_size,url from download_info where url=?";
                List<User> list1 = MyApplication.userDao.queryBuilder().where(UserDao.Properties.Url.eq(urlstr))/*.build()*/.list();
                for (User user : list1) {
                    DownloadInfo infoss = new DownloadInfo(
                            user.getThread_id(), user.getStart_pos(), user.getEnd_pos(),
                            user.getCompelete_size(), user.getUrl());
                    list.add(infoss);
                }
            return list;
        }
    
        /**
         * 更新資料庫中的下載資訊
         */
        public synchronized void updataInfos(int threadId, int compeleteSize, String urlstr) {
            User user = MyApplication.userDao.queryBuilder()
                    .where(UserDao.Properties.Thread_id.eq(threadId), UserDao.Properties.Url.eq(urlstr)).build().unique();
            user.setCompelete_size(compeleteSize);
            MyApplication.userDao.update(user);
        }
    
        /**
         * 下載完成後刪除資料庫中的資料
         */
        public synchronized void delete(String url) {
            MyApplication.userDao.deleteAll();
        }
    

單例模式

 

package com.bwie.yue2;

import android.app.Application;

/**
 * Created by T_baby on 17/11/22.
 */

public  class MyApplication extends Application {
    public static UserDao userDao;
    @Override
    public void onCreate() {
        super.onCreate();
        DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(this, "lenvess.db", null);
        DaoMaster daoMaster = new DaoMaster(devOpenHelper.getWritableDb());
        DaoSession daoSession = daoMaster.newSession();
        userDao = daoSession.getUserDao();
    }
}

介面卡: 

[java] view plain copy
  1. package cn.demo.download;  
  2.   
  3. import java.util.List;  
  4. import java.util.Map;  
  5.   
  6. import android.content.Context;  
  7. import android.view.LayoutInflater;  
  8. import android.view.View;  
  9. import android.view.View.OnClickListener;  
  10. import android.view.ViewGroup;  
  11. import android.widget.BaseAdapter;  
  12. import android.widget.Button;  
  13. import android.widget.TextView;  
  14.   
  15.   
  16.   
  17.   
  18. public class DownLoadAdapter extends BaseAdapter{  
  19.        
  20.     private LayoutInflater mInflater;  
  21.     private List<Map<String, String>> data;  
  22.     private Context context;  
  23.     private OnClickListener click;  
  24.       
  25.     public DownLoadAdapter(Context context,List<Map<String, String>> data) {  
  26.         this.context=context;  
  27.         mInflater = LayoutInflater.from(context);  
  28.         this.data=data;  
  29.     }  
  30.     public void refresh(List<Map<String, String>> data) {  
  31.         this.data=data;  
  32.         this.notifyDataSetChanged();  
  33.     }  
  34.     public void setOnclick(OnClickListener click) {  
  35.          this.click=click;  
  36.     }  
  37.       
  38.       
  39.     @Override  
  40.     public int getCount() {  
  41.         return data.size();  
  42.     }  
  43.   
  44.     @Override  
  45.     public Object getItem(int position) {  
  46.         return data.get(position);  
  47.     }  
  48.   
  49.     @Override  
  50.     public long getItemId(int position) {  
  51.         return position;  
  52.     }  
  53.   
  54.     @Override  
  55.     public View getView(final int position, View convertView, ViewGroup parent) {  
  56.         final Map<String, String> bean=data.get(position);  
  57.         ViewHolder holder = null;  
  58.         if (convertView == null) {  
  59.             convertView = mInflater.inflate(R.layout.list_item, null);  
  60.             holder = new ViewHolder();   
  61.             holder.resouceName=(TextView) convertView.findViewById(R.id.tv_resouce_name);  
  62.             holder.startDownload=(Button) convertView.findViewById(R.id.btn_start);  
  63.             holder.pauseDownload=(Button) convertView.findViewById(R.id.btn_pause);  
  64.             convertView.setTag(holder);  
  65.         } else {  
  66.             holder = (ViewHolder) convertView.getTag();  
  67.         }   
  68.         holder.resouceName.setText(bean.get("name"));   
  69.         return convertView;  
  70.     }  
  71.     public OnClickListener getClick() {  
  72.         return click;  
  73.     }  
  74.     public void setClick(OnClickListener click) {  
  75.         this.click = click;  
  76.     }  
  77.     private class ViewHolder {   
  78.         public TextView resouceName;  
  79.         public Button startDownload;  
  80.         public Button pauseDownload;  
  81.     }  
  82.       
  83. }  



注意子執行緒不要影響主UI執行緒,靈活運用task和handler,各取所長,保證使用者體驗,handler通常在主執行緒中有利於專門負責處理UI的一些工作

[java] view plain copy
  1. package cn.demo.download;  
  2.    
  3.  import java.util.ArrayList;  
  4. import java.util.HashMap;  
  5. import java.util.List;  
  6. import java.util.Map;  
  7.   
  8. import android.app.ListActivity;  
  9. import android.os.AsyncTask;  
  10. import android.os.Bundle;  
  11. import android.os.Handler;  
  12. import android.os.Message;  
  13. import android.view.View;  
  14. import android.widget.Button;  
  15. import android.widget.LinearLayout;  
  16. import android.widget.LinearLayout.LayoutParams;  
  17. import android.widget.ProgressBar;   
  18. import android.widget.TextView;  
  19. import android.widget.Toast;  
  20. import cn.demo.entity.LoadInfo;  
  21. import cn.demo.service.Downloader;  
  22.   
  23.  public class MainActivity extends ListActivity {   
  24.      // 固定下載的資源路徑,這裡可以設定網路上的地址  
  25.      private static final String URL = "http://download.haozip.com/";  
  26.      // 固定存放下載的音樂的路徑:SD卡目錄下  
  27.      private static final String SD_PATH = "/mnt/sdcard/";  
  28.      // 存放各個下載器  
  29.      private Map<String, Downloader> downloaders = new HashMap<String, Downloader>();  
  30.      // 存放與下載器對應的進度條  
  31.      private Map<String, ProgressBar> ProgressBars = new HashMap<String, ProgressBar>();  
  32.      /** 
  33.       * 利用訊息處理機制適時更新進度條 
  34.       */  
  35.      private Handler mHandler = new Handler() {  
  36.          public void handleMessage(Message msg) {  
  37.              if (msg.what == 1) {  
  38.                  String url = (String) msg.obj;  
  39.                  int length = msg.arg1;  
  40.                  ProgressBar bar = ProgressBars.get(url);  
  41.                  if (bar != null) {  
  42.                      // 設定進度條按讀取的length長度更新  
  43.                      bar.incrementProgressBy(length);  
  44.                      if (bar.getProgress() == bar.getMax()) {  
  45.                          LinearLayout layout = (LinearLayout) bar.getParent();  
  46.                          TextView resouceName=(TextView)layout.findViewById(R.id.tv_resouce_name);  
  47.                          Toast.makeText(MainActivity.this"["+resouceName.getText()+"]下載完成!", Toast.LENGTH_SHORT).show();  
  48.                          // 下載完成後清除進度條並將map中的資料清空  
  49.                          layout.removeView(bar);  
  50.                          ProgressBars.remove(url);  
  51.                          downloaders.get(url).delete(url);  
  52.                          downloaders.get(url).reset();  
  53.                          downloaders.remove(url);  
  54.                            
  55.                          Button btn_start=(Button)layout.findViewById(R.id.btn_start);  
  56.                          Button btn_pause=(Button)layout.findViewById(R.id.btn_pause);  
  57.                          btn_pause.setVisibility(View.GONE);  
  58.                          btn_start.setVisibility(View.GONE);  
  59.                      }  
  60.                  }  
  61.              }  
  62.          }  
  63.      };  
  64.      @Override  
  65.      public void onCreate(Bundle savedInstanceState) {  
  66.          super.onCreate(savedInstanceState);  
  67.          setContentView(R.layout.main);   
  68.          showListView();  
  69.      }  
  70.      // 顯示listView,這裡可以隨便新增  
  71.      private void showListView() {  
  72.          List<Map<String, String>> data = new ArrayList<Map<String, String>>();  
  73.          Map<String, String> map = new HashMap<String, String>();  
  74.          map.put("name""haozip_v3.1.exe");  
  75.          data.add(map);  
  76.          map = new HashMap<String, String>();  
  77.          map.put("name""haozip_v3.1_hj.exe");  
  78.          data.add(map);  
  79.          map = new HashMap<String, String>();  
  80.          map.put("name""haozip_v2.8_x64_tiny.exe");  
  81.          data.add(map);  
  82.          map = new HashMap<String, String>();  
  83.          map.put("name""haozip_v2.8_tiny.exe");  
  84.          data.add(map);  
  85.          DownLoadAdapter adapter=new DownLoadAdapter(this,data);    
  86.          setListAdapter(adapter);  
  87.            
  88.      }  
  89.      /** 
  90.       * 響應開始下載按鈕的點選事件 
  91.       */  
  92.      public void startDownload(View v) {  
  93.          // 得到textView的內容   
  94.          LinearLayout layout = (LinearLayout) v.getParent();  
  95.          String resouceName = ((TextView) layout.findViewById(R.id.tv_resouce_name)).getText().toString();  
  96.          String urlstr = URL + resouceName;  
  97.          String localfile = SD_PATH + resouceName;  
  98.          //設定下載執行緒數為4,這裡是我為了方便隨便固定的  
  99.          String threadcount = "4";  
  100.          DownloadTask downloadTask=new DownloadTask(v);  
  101.          downloadTask.execute(urlstr,localfile,threadcount);  
  102.          
  103.      };  
  104.     class DownloadTask extends AsyncTask<String, Integer, LoadInfo>{  
  105.         Downloader downloader=null;   
  106.         View v=null;  
  107.         String urlstr=null;  
  108.         public DownloadTask(final View v){  
  109.             this.v=v;  
  110.         }    
  111.         @Override  
  112.         protected void onPreExecute() {   
  113.             Button btn_start=(Button)((View)v.getParent()).findViewById(R.id.btn_start);  
  114.             Button btn_pause=(Button)((View)v.getParent()).findViewById(R.id.btn_pause);  
  115.             btn_start.setVisibility(View.GONE);  
  116.             btn_pause.setVisibility(View.VISIBLE);  
  117.         }  
  118.         @Override  
  119.         protected LoadInfo doInBackground(String... params) {  
  120.             urlstr=params[0];  
  121.             String localfile=params[1];  
  122.             int threadcount=Integer.parseInt(params[2]);  
  123.              // 初始化一個downloader下載器  
  124.              downloader = downloaders.get(urlstr);  
  125.              if (downloader == null) {  
  126.                  downloader = new Downloader(urlstr, localfile, threadcount, MainActivity.this, mHandler);  
  127.                  downloaders.put(urlstr, downloader);  
  128.              }  
  129.              if (downloader.isdownloading())  
  130.                  return null;  
  131.              // 得到下載資訊類的個陣列成集合  
  132.              return downloader.getDownloaderInfors();   
  133.         }  
  134.         @Override  
  135.         protected void onPostExecute(LoadInfo loadInfo) {  
  136.             if(loadInfo!=null){  
  137.                  // 顯示進度條  
  138.                  showProgress(loadInfo, urlstr, v);  
  139.                  // 呼叫方法開始下載  
  140.                  downloader.download();  
  141.             }   
  142.         }  
  143.            
  144.      };  
  145.      /** 
  146.       * 顯示進度條 
  147.       */  
  148.      private void showProgress(LoadInfo loadInfo, String url, View v) {  
  149.          ProgressBar bar = ProgressBars.get(url);  
  150.          if (bar == null) {  
  151.              bar = new ProgressBar(thisnull, android.R.attr.progressBarStyleHorizontal);  
  152.              bar.setMax(loadInfo.getFileSize());  
  153.              bar.setProgress(loadInfo.getComplete());  
  154.              ProgressBars.put(url, bar);  
  155.              LinearLayout.LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT, 5);  
  156.              ((LinearLayout) ((LinearLayout) v.getParent()).getParent()).addView(bar, params);  
  157.          }  
  158.      }  
  159.      /** 
  160.       * 響應暫停下載按鈕的點選事件 
  161.       */  
  162.      public void pauseDownload(View v) {  
  163.          LinearLayout layout = (LinearLayout) v.getParent();  
  164.          String resouceName = ((TextView) layout.findViewById(R.id.tv_resouce_name)).getText().toString();  
  165.          String urlstr = URL + resouceName;  
  166.          downloaders.get(urlstr).pause();  
  167.          Button btn_start=(Button)((View)v.getParent()).findViewById(R.id.btn_start);  
  168.          Button btn_pause=(Button)((View)v.getParent()).findViewById(R.id.btn_pause);  
  169.          btn_pause.setVisibility(View.GONE);  
  170.          btn_start.setVisibility(View.VISIBLE);  
  171.      }  
  172.  }  


 

 這是一個資訊的實體,記錄了一些字典資訊,可以認為是一個簡單bean物件
[java] view plain copy
  1. package cn.demo.entity;  
  2.  /** 
  3.   *建立一個下載資訊的實體類 
  4.   */  
  5.  public class DownloadInfo {  
  6.      private int threadId;//下載器id  
  7.      private int startPos;//開始點  
  8.      private int endPos;//結束點  
  9.      private int compeleteSize;//完成度  
  10.      private String url;//下載器網路標識  
  11.      public DownloadInfo(int threadId, int startPos, int endPos,  
  12.              int compeleteSize,String url) {  
  13.          this.threadId = threadId;  
  14.          this.startPos = startPos;  
  15.          this.endPos = endPos;  
  16.          this.compeleteSize = compeleteSize;  
  17.          this.url=url;  
  18.      }  
  19.      public DownloadInfo() {  
  20.      }  
  21.      public String getUrl() {  
  22.          return url;  
  23.      }  
  24.      public void setUrl(String url) {  
  25.          this.url = url;  
  26.      }  
  27.      public int getThreadId() {  
  28.          return threadId;  
  29.      }  
  30.      public void setThreadId(int threadId) {  
  31.          this.threadId = threadId;  
  32.      }  
  33.      public int getStartPos() {  
  34.          return startPos;  
  35.      }  
  36.      public void setStartPos(int startPos) {  
  37.          this.startPos = startPos;  
  38.      }  
  39.      public int getEndPos() {  
  40.          return endPos;  
  41.      }  
  42.      public void setEndPos(int endPos) {  
  43.          this.endPos = endPos;  
  44.      }  
  45.      public int getCompeleteSize() {  
  46.          return compeleteSize;  
  47.      }  
  48.      public void setCompeleteSize(int compeleteSize) {  
  49.          this.compeleteSize = compeleteSize;  
  50.      }  
  51.    
  52.      @Override  
  53.      public String toString() {  
  54.          return "DownloadInfo [threadId=" + threadId  
  55.                  + ", startPos=" + startPos + ", endPos=" + endPos  
  56.                  + ", compeleteSize=" + compeleteSize +"]";  
  57.      }  
  58.  }  

[java] view plain copy
  1. package cn.demo.entity;  
  2.  /** 
  3.   *自定義的一個記載下載器詳細資訊的類  
  4.   */  
  5.  public class LoadInfo {  
  6.      public int fileSize;//檔案大小  
  7.      private int complete;//完成度  
  8.      private String urlstring;//下載器標識  
  9.      public LoadInfo(int fileSize, int complete, String urlstring) {  
  10.          this.fileSize = fileSize;  
  11.          this.complete = complete;  
  12.          this.urlstring = urlstring;  
  13.      }  
  14.      public LoadInfo() {  
  15.      }  
  16.      public int getFileSize() {  
  17.          return fileSize;  
  18.      }  
  19.      public void setFileSize(int fileSize) {  
  20.          this.fileSize = fileSize;  
  21.      }  
  22.      public int getComplete() {  
  23.          return complete;  
  24.      }  
  25.      public void setComplete(int complete) {  
  26.          this.complete = complete;  
  27.      }  
  28.      public String getUrlstring() {  
  29.          return urlstring;  
  30.      }  
  31.      public void setUrlstring(String urlstring) {  
  32.          this.urlstring = urlstring;  
  33.      }  
  34.      @Override  
  35.      public String toString() {  
  36.          return "LoadInfo [fileSize=" + fileSize + ", complete=" + complete  
  37.                  + ", urlstring=" + urlstring + "]";  
  38.      }  
  39.  }  

 


這是一個核心類,專門用來處理下載的

[java] view plain copy
  1. package cn.demo.service;  
  2.    
  3.  import java.io.File;  
  4. import java.io.InputStream;  
  5. import java.io.RandomAccessFile;  
  6. import java.net.HttpURLConnection;  
  7. import java.net.URL;  
  8. import java.util.ArrayList;  
  9. import java.util.List;  
  10.   
  11. import android.content.Context;  
  12. import android.os.AsyncTask;  
  13. import android.os.Handler;  
  14. import android.os.Message;  
  15. import android.util.Log;  
  16. import cn.demo.Dao.Dao;  
  17. import cn.demo.entity.DownloadInfo;  
  18. import cn.demo.entity.LoadInfo;  
  19.    
  20.  public class Downloader {  
  21.      private String urlstr;// 下載的地址  
  22.      private String localfile;// 儲存路徑  
  23.      private int threadcount;// 執行緒數  
  24.      private Handler mHandler;// 訊息處理器   
  25.      private int fileSize;// 所要下載的檔案的大小  
  26.      private Context context;   
  27.      private List<DownloadInfo> infos;// 存放下載資訊類的集合  
  28.      private static final int INIT = 1;//定義三種下載的狀態:初始化狀態,正在下載狀態,暫停狀態  
  29.      private static final int DOWNLOADING = 2;  
  30.      private static final int PAUSE = 3;  
  31.      private int state = INIT;  
  32.    
  33.      public Downloader(String urlstr, String localfile, int threadcount,  
  34.              Context context, Handler mHandler) {  
  35.          this.urlstr = urlstr;  
  36.          this.localfile = localfile;  
  37.          this.threadcount = threadcount;  
  38.          this.mHandler = mHandler;  
  39.          this.context = context;  
  40.      }  
  41.      /** 
  42.       *判斷是否正在下載  
  43.       */  
  44.      public boolean isdownloading() {  
  45.          return state == DOWNLOADING;  
  46.      }  
  47.      /** 
  48.       * 得到downloader裡的資訊 
  49.       * 首先進行判斷是否是第一次下載,如果是第一次就要進行初始化,並將下載器的資訊儲存到資料庫中 
  50.       * 如果不是第一次下載,那就要從資料庫中讀出之前下載的資訊(起始位置,結束為止,檔案大小等),並將下載資訊返回給下載器 
  51.       */  
  52.      public LoadInfo getDownloaderInfors() {  
  53.          if (isFirst(urlstr)) {  
  54.              Log.v("TAG""isFirst");  
  55.              init();  
  56.              int range = fileSize / threadcount;  
  57.              infos = new ArrayList<DownloadInfo>();  
  58.              for (int i = 0; i < threadcount - 1; i++) {  
  59.                  DownloadInfo info = new DownloadInfo(i, i * range, (i + 1)* range - 10, urlstr);  
  60.                  infos.add(info);  
  61.              }  
  62.              DownloadInfo info = new DownloadInfo(threadcount - 1,(threadcount - 1) * range, fileSize - 10, urlstr);  
  63.              infos.add(info);  
  64.              //儲存infos中的資料到資料庫  
  65.              Dao.getInstance(context).saveInfos(infos);  
  66.              //建立一個LoadInfo物件記載下載器的具體資訊  
  67.              LoadInfo loadInfo = new LoadInfo(fileSize, 0, urlstr);  
  68.              return loadInfo;  
  69.          } else {  
  70.              //得到資料庫中已有的urlstr的下載器的具體資訊  
  71.              infos = Dao.getInstance(context).getInfos(urlstr);  
  72.              Log.v("TAG""not isFirst size=" + infos.size());  
  73.              int size = 0;  
  74.              int compeleteSize = 0;  
  75.              for (DownloadInfo info : infos) {  
  76.                  compeleteSize += info.getCompeleteSize();  
  77.                  size += info.getEndPos() - info.getStartPos() + 1;  
  78.              }  
  79.              return new LoadInfo(size, compeleteSize, urlstr);  
  80.          }  
  81.      }  
  82.    
  83.      /** 
  84.       * 初始化 
  85.       */  
  86.      private void init() {  
  87.          try {  
  88.              URL url = new URL(urlstr);  
  89.              HttpURLConnection connection = (HttpURLConnection) url.openConnection();  
  90.              connection.setConnectTimeout(5000);  
  91.              connection.setRequestMethod("GET");  
  92.              fileSize = connection.getContentLength();  
  93.    
  94.              File file = new File(localfile);  
  95.              if (!file.exists()) {  
  96.                  file.createNewFile();  
  97.              }  
  98.              // 本地訪問檔案  
  99.              RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");  
  100.              accessFile.setLength(fileSize);  
  101.              accessFile.close();  
  102.              connection.disconnect();  
  103.          } catch (Exception e) {  
  104.              e.printStackTrace();  
  105.          }  
  106.      }    
  107.      /** 
  108.       * 判斷是否是第一次 下載 
  109.       */  
  110.      private boolean isFirst(String urlstr) {  
  111.          return Dao.getInstance(context).isHasInfors(urlstr);  
  112.      }  
  113.    
  114.      /** 
  115.       * 利用執行緒開始下載資料 
  116.       */  
  117.      public void download() {  
  118.          if (infos != null) {  
  119.              if (state == DOWNLOADING)  
  120.                  return;  
  121.              state = DOWNLOADING;  
  122.              for (DownloadInfo info : infos) {  
  123.                  new MyThread(info.getThreadId(), info.getStartPos(),  
  124.                          info.getEndPos(), info.getCompeleteSize(),  
  125.                          info.getUrl()).start();  
  126.              }  
  127.          }  
  128.      }  
  129.    
  130.      public class MyThread extends Thread {  
  131.          private int threadId;  
  132.          private int startPos;  
  133.          private int endPos;  
  134.          private int compeleteSize;  
  135.          private String urlstr;  
  136.    
  137.          public MyThread(int threadId, int startPos, int endPos,  
  138.                  int compeleteSize, String urlstr) {  
  139.              this.threadId = threadId;  
  140.              this.startPos = startPos;  
  141.              this.endPos = endPos;  
  142.              this.compeleteSize = compeleteSize;  
  143.              this.urlstr = urlstr;  
  144.          }  
  145.          @Override  
  146.          public void run() {  
  147.              HttpURLConnection connection = null;  
  148.              RandomAccessFile randomAccessFile = null;  
  149.              InputStream is = null;  
  150.              try {  
  151.                  URL url = new URL(urlstr);  
  152.                  connection = (HttpURLConnection) url.openConnection();  
  153.                  connection.setConnectTimeout(5000);  
  154.                  connection.setRequestMethod("GET");  
  155.                  // 設定範圍,格式為Range:bytes x-y;  
  156.                  connection.setRequestProperty("Range""bytes="+(startPos + compeleteSize) + "-" + endPos);  
  157.    
  158.                  randomAccessFile = new RandomAccessFile(localfile, "rwd");  
  159.                  randomAccessFile.seek(startPos + compeleteSize);  
  160.                  // 將要下載的檔案寫到儲存在儲存路徑下的檔案中  
  161.                  is = connection.getInputStream();  
  162.                  byte[] buffer = new byte[4096];  
  163.                  int length = -1;  
  164.                  while ((length = is.read(buffer)) != -1) {  
  165.                      randomAccessFile.write(buffer, 0, length);  
  166.                      compeleteSize += length;  
  167.                      // 更新資料庫中的下載資訊  
  168.                      Dao.getInstance(context).updataInfos(threadId, compeleteSize, urlstr);  
  169.                      // 用訊息將下載資訊傳給進度條,對進度條進行更新  
  170.                      Message message = Message.obtain();  
  171.                      message.what = 1;  
  172.                      message.obj = urlstr;  
  173.                      message.arg1 = length;  
  174.                      mHandler.sendMessage(message);  
  175.                      if (state == PAUSE) {  
  176.                          return;  
  177.                      }  
  178.                  }  
  179.              } catch (Exception e) {  
  180.                  e.printStackTrace();  
  181.              }    
  182.          }  
  183.      }  
  184.      //刪除資料庫中urlstr對應的下載器資訊  
  185.      public void delete(String urlstr) {  
  186.          Dao.getInstance(context).delete(urlstr);  
  187.      }  
  188.      //設定暫停  
  189.      public void pause() {  
  190.          state = PAUSE;  
  191.      }  
  192.      //重置下載狀態  
  193.      public void reset() {  
  194.          state = INIT;  
  195.      }  
  196.  }  


以下是些xml檔案

item佈局

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2.  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.             android:orientation="vertical"  
  4.             android:layout_width="fill_parent"  
  5.             android:layout_height="wrap_content">  
  6.      <LinearLayout  
  7.             android:orientation="horizontal"  
  8.             android:layout_width="fill_parent"  
  9.             android:layout_height="wrap_content"  
  10.             android:layout_marginBottom="5dip">  
  11.          <TextView   
  12.              android:layout_width="fill_parent"  
  13.              android:layout_height="wrap_content"  
  14.              android:layout_weight="1"  
  15.              android:id="@+id/tv_resouce_name"/>  
  16.          <Button  
  17.              android:layout_width="fill_parent"  
  18.              android:layout_height="wrap_content"  
  19.              android:layout_weight="1"  
  20.              android:text="下載"  
  21.              android:id="@+id/btn_start"  
  22.              android:onClick="startDownload"/>  
  23.          <Button  
  24.              android:layout_width="fill_parent"  
  25.              android:layout_height="wrap_content"  
  26.              android:layout_weight="1"  
  27.              android:text="暫停"  
  28.              android:visibility="gone"  
  29.              android:id="@+id/btn_pause"  
  30.              android:onClick="pauseDownload"/>  
  31.        </LinearLayout>  
  32.  </LinearLayout>  


 main佈局

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2.  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.      android:orientation="vertical"  
  4.      android:layout_width="fill_parent"  
  5.      android:layout_height="fill_parent"  
  6.      android:id="@+id/llRoot">  
  7.      <ListView android:id="@android:id/list"  
  8.          android:layout_width="fill_parent"  
  9.          android:layout_height="fill_parent">  
  10.      </ListView>  
  11.  </LinearLayout>  


 

[html] view plain copy
  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     package="cn.demo.download"  
  3.     android:versionCode="1"  
  4.     android:versionName="1.0">  
  5.   
  6.     <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="8" />  
  7.     <uses-permission android:name="android.permission.INTERNET"/>   
  8.     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>  
  9.     <application android:label="@string/app_name"  
  10.         android:icon="@drawable/ic_launcher"  
  11.         android:theme="@style/AppTheme">  
  12.         <activity  
  13.                 android:name=".MainActivity"   
  14.                 android:label="@string/app_name" >  
  15.                 <intent-filter>  
  16.                     <action android:name="android.intent.action.MAIN" />   
  17.                     <category android:name="android.intent.category.LAUNCHER" />  
  18.                 </intent-filter>  
  19.             </activity>  
  20.     </application>  
  21.   
  22. </manifest>  


執行效果如下

 

 

原始碼下載地址

http://blog.csdn.net/shimiso/article/details/49450551  android 多執行緒斷點續傳下載 四-模仿下載助手


 

轉載請標明出處 http://blog.csdn.net/shimiso

相關文章