android 多線程斷點續傳下載 三

來自:http://blog.csdn.net/shimiso/article/details/8448544


今天跟大家一起分享下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體驗上一定記住要使用異步完成,既然大致思路已經清楚,下面我們開始分析程序:

[java] view plaincopy
  1. package cn.demo.DBHelper;  
  2.    
  3.  import android.content.Context;  
  4. import android.database.sqlite.SQLiteDatabase;  
  5. import android.database.sqlite.SQLiteOpenHelper;  
  6.    
  7.      /** 
  8.       * 建立一個數據庫幫助類 
  9.       */  
  10.  public class DBHelper extends SQLiteOpenHelper {  
  11.      //download.db-->數據庫名  
  12.      public DBHelper(Context context) {  
  13.          super(context, "download.db"null1);  
  14.      }  
  15.        
  16.      /** 
  17.       * 在download.db數據庫下創建一個download_info表存儲下載信息 
  18.       */  
  19.      @Override  
  20.      public void onCreate(SQLiteDatabase db) {  
  21.          db.execSQL("create table download_info(_id integer PRIMARY KEY AUTOINCREMENT, thread_id integer, "  
  22.                  + "start_pos integer, end_pos integer, compelete_size integer,url char)");  
  23.      }  
  24.      @Override  
  25.      public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
  26.    
  27.      }  
  28.    
  29.  }  

 

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

[java] view plaincopy
  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 {    
  17.     private static Dao dao=null;  
  18.     private Context context;   
  19.     private  Dao(Context context) {   
  20.         this.context=context;  
  21.     }  
  22.     public static  Dao getInstance(Context context){  
  23.         if(dao==null){  
  24.             dao=new Dao(context);   
  25.         }  
  26.         return dao;  
  27.     }  
  28.     public  SQLiteDatabase getConnection() {  
  29.         SQLiteDatabase sqliteDatabase = null;  
  30.         try {   
  31.             sqliteDatabase= new DBHelper(context).getReadableDatabase();  
  32.         } catch (Exception e) {    
  33.         }  
  34.         return sqliteDatabase;  
  35.     }  
  36.   
  37.     /** 
  38.      * 查看數據庫中是否有數據 
  39.      */  
  40.     public synchronized boolean isHasInfors(String urlstr) {  
  41.         SQLiteDatabase database = getConnection();  
  42.         int count = -1;  
  43.         Cursor cursor = null;  
  44.         try {  
  45.             String sql = "select count(*)  from download_info where url=?";  
  46.             cursor = database.rawQuery(sql, new String[] { urlstr });  
  47.             if (cursor.moveToFirst()) {  
  48.                 count = cursor.getInt(0);  
  49.             }   
  50.         } catch (Exception e) {  
  51.             e.printStackTrace();  
  52.         } finally {  
  53.             if (null != database) {  
  54.                 database.close();  
  55.             }  
  56.             if (null != cursor) {  
  57.                 cursor.close();  
  58.             }  
  59.         }  
  60.         return count == 0;  
  61.     }  
  62.   
  63.     /** 
  64.      * 保存 下載的具體信息 
  65.      */  
  66.     public synchronized void saveInfos(List<DownloadInfo> infos) {  
  67.         SQLiteDatabase database = getConnection();  
  68.         try {  
  69.             for (DownloadInfo info : infos) {  
  70.                 String sql = "insert into download_info(thread_id,start_pos, end_pos,compelete_size,url) values (?,?,?,?,?)";  
  71.                 Object[] bindArgs = { info.getThreadId(), info.getStartPos(),  
  72.                         info.getEndPos(), info.getCompeleteSize(),  
  73.                         info.getUrl() };  
  74.                 database.execSQL(sql, bindArgs);  
  75.             }  
  76.         } catch (Exception e) {  
  77.             e.printStackTrace();  
  78.         } finally {  
  79.             if (null != database) {  
  80.                 database.close();  
  81.             }  
  82.         }  
  83.     }  
  84.   
  85.     /** 
  86.      * 得到下載具體信息 
  87.      */  
  88.     public synchronized List<DownloadInfo> getInfos(String urlstr) {  
  89.         List<DownloadInfo> list = new ArrayList<DownloadInfo>();  
  90.         SQLiteDatabase database = getConnection();  
  91.         Cursor cursor = null;  
  92.         try {  
  93.             String sql = "select thread_id, start_pos, end_pos,compelete_size,url from download_info where url=?";  
  94.             cursor = database.rawQuery(sql, new String[] { urlstr });  
  95.             while (cursor.moveToNext()) {  
  96.                 DownloadInfo info = new DownloadInfo(cursor.getInt(0),  
  97.                         cursor.getInt(1), cursor.getInt(2), cursor.getInt(3),  
  98.                         cursor.getString(4));  
  99.                 list.add(info);  
  100.             }  
  101.         } catch (Exception e) {  
  102.             e.printStackTrace();  
  103.         } finally {  
  104.             if (null != database) {  
  105.                 database.close();  
  106.             }  
  107.             if (null != cursor) {  
  108.                 cursor.close();  
  109.             }  
  110.         }  
  111.         return list;  
  112.     }  
  113.   
  114.     /** 
  115.      * 更新數據庫中的下載信息 
  116.      */  
  117.     public synchronized void updataInfos(int threadId, int compeleteSize, String urlstr) {  
  118.         SQLiteDatabase database = getConnection();  
  119.         try {  
  120.             String sql = "update download_info set compelete_size=? where thread_id=? and url=?";  
  121.             Object[] bindArgs = { compeleteSize, threadId, urlstr };  
  122.             database.execSQL(sql, bindArgs);  
  123.         } catch (Exception e) {  
  124.             e.printStackTrace();  
  125.         } finally {  
  126.             if (null != database) {  
  127.                 database.close();  
  128.             }  
  129.         }  
  130.     }  
  131.   
  132.     /** 
  133.      * 下載完成後刪除數據庫中的數據 
  134.      */  
  135.     public synchronized void delete(String url) {  
  136.         SQLiteDatabase database = getConnection();  
  137.         try {  
  138.             database.delete("download_info""url=?"new String[] { url });  
  139.         } catch (Exception e) {  
  140.             e.printStackTrace();  
  141.         } finally {  
  142.             if (null != database) {  
  143.                 database.close();  
  144.             }  
  145.         }  
  146.     }  
  147. }  


 

[java] view plaincopy
  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.        
  84.     }  
  85.       
  86. }  



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

[java] view plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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文件

[html] view plaincopy
  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>  


 

[html] view plaincopy
  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 plaincopy
  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>  


運行效果如下

 

 

源碼下載地址

 

 


發佈了5 篇原創文章 · 獲贊 15 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章