android 多线程断点续传下载 三

今天跟大家一起分享下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
plain
copy

  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", null, 1);  
  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
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 {    
  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
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.        
  84.     }  
  85.       
  86. }  

注意子线程不要影响主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(this, null, 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 - 1, 0, urlstr);  
  60.                  infos.add(info);  
  61.              }  
  62.              DownloadInfo info = new DownloadInfo(threadcount - 1,(threadcount - 1) * range, fileSize - 1, 0, 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
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>  

 

[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>  

运行效果如下

 

 

源码下载地址

时间: 2024-09-19 23:01:08

android 多线程断点续传下载 三的相关文章

android 多线程断点续传下载 二

在上一节中,我们简单介绍了如何创建多任务下载,但那种还不能拿来实用,这一集我们重点通过代码为大家展示如何创建多线程断点续传下载,这在实际项目中很常用. main.xml: [html] view plaincopy <?xml version="1.0" encoding="utf-8"?>   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android&

android 多线程断点续传下载

http://blog.csdn.net/shimiso/article/details/6763986 多线程断点续传下载,这在实际项目中很常用. main.xml: [html] view plaincopy <?xml version="1.0" encoding="utf-8"?>   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android&q

Android多线程断点续传下载示例详解

一.概述 在上一篇博文<Android多线程下载示例>中,我们讲解了如何实现Android的多线程下载功能,通过将整个文件分成多个数据块,开启多个线程,让每个线程分别下载一个相应的数据块来实现多线程下载的功能.多线程下载中,可以将下载这个耗时的操作放在子线程中执行,即不阻塞主线程,又符合Android开发的设计规范. 但是当下载的过程当中突然出现手机卡死,或者网络中断,手机电量不足关机的现象,这时,当手机可以正常使用后,如果重新下载文件,似乎不太符合大多数用户的心理期望,那如何实现当手机可以正

android 多线程断点续传下载 一

想做一个下载功能,当然理想的功能要支持多任务同时下载,断点续传的功能,我想一步一步来,首先困难摆在了多任务这里 开始我的思路是在一个Service中启动下载的流操作,然后通过Service中声明一个Activity中的Handler更新UI(比如进度条...) 可是我发现在Service中声明一个Activity中的Handler是做不到的(可能只是我做不到吧,无法申请内存) 于是,我决定在Activity中直接启动线程,让其运行,调用自身的Handler来更新UI,没想到在这个下载Activi

Android实现网络多线程断点续传下载实例_Android

我们编写的是Andorid的HTTP协议多线程断点下载应用程序.直接使用单线程下载HTTP文件对我们来说是一件非常简单的事.那么,多线程断点需要什么功能? 1.多线程下载, 2.支持断点. 使用多线程的好处:使用多线程下载会提升文件下载的速度.那么多线程下载文件的过程是: (1)首先获得下载文件的长度,然后设置本地文件的长度. HttpURLConnection.getContentLength();//获取下载文件的长度 RandomAccessFile file = new RandomAc

Android编程开发实现多线程断点续传下载器实例_Android

本文实例讲述了Android编程开发实现多线程断点续传下载器.分享给大家供大家参考,具体如下: 使用多线程断点续传下载器在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度,在下载过程中记录每个线程已拷贝数据的数量,如果下载中断,比如无信号断线.电量不足等情况下,这就需要使用到断点续传功能,下次启动时从记录位置继续下载,可避免重复部分的下载.这里采用数据库来记录下载的进度. 效果图:   断点续传 1.断点续传需要在下载过程中记录每条线程的下载进度 2.每次下载开始之前先读取数据库

Android编程开发实现多线程断点续传下载器实例

本文实例讲述了Android编程开发实现多线程断点续传下载器.分享给大家供大家参考,具体如下: 使用多线程断点续传下载器在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度,在下载过程中记录每个线程已拷贝数据的数量,如果下载中断,比如无信号断线.电量不足等情况下,这就需要使用到断点续传功能,下次启动时从记录位置继续下载,可避免重复部分的下载.这里采用数据库来记录下载的进度. 效果图: 断点续传 1.断点续传需要在下载过程中记录每条线程的下载进度 2.每次下载开始之前先读取数据库,查

HTTP多线程断点续传下载的尝试

直接看代码吧,废话一点不多说. 功能介绍: 1 多线程HTTP下载 2 支持断点续传 3 临时文件下载,成功后改名 4 提供防盗链的破解 Java代码 1.import java.io.BufferedInputStream; 2.import java.io.BufferedWriter; 3.import java.io.File; 4.import java.io.OutputStreamWriter; 5.import java.io.RandomAccessFile; 6.import

Android实现网络多线程断点续传下载

本示例介绍在Android平台下通过HTTP协议实现断点续传下载. 我们编写的是Andorid的HTTP协议多线程断点下载应用程序.直接使用单线程下载HTTP文件对我们来说是一件非常简单的事.那么,多线程断点需要什么功能? 1.多线程下载, 2.支持断点.   使用多线程的好处:使用多线程下载会提升文件下载的速度.那么多线程下载文件的过程是:    (1)首先获得下载文件的长度,然后设置本地文件的长度.       HttpURLConnection.getContentLength();//获