android多线程下载和断点续传

Android多线程断点续传详解与案例

分类: Android高级 378人阅读 评论(0) 收藏 举报

(1)Android多线程断点续传

Android多线程断点续传下载器涉及了Android应用开发大部分的核心知识和难点:

(1)Android中主线程和非主线通信机制:Handler、Looper、Message、MessageQueue。

(2)多线程编程、多线程的管理。

(3)Android网络编程。

(4)IoC技术,自己动手实现设计模式中的Listener模式。

(5)Activity、Service、数据库编程等。

(6)文件系统。

(7)缓存。


首先看一下下载器的运行效果图,如下图所示。。


其实上面运行效果的基本原理如下图所示。



1.Android多线程的实现思想

(1)可以据记录当前的下载位置,实现断点下载。

如果现在需要下载一个大小为29 MB的文件,当下载到5 MB时,临时有事情,关闭之后普通的下载不能帮助我们继续下载,而是必须重新开始,而多线程下载器(如迅雷)可以帮助我们记录下上次下载的位置,当再次下载时可以从记录的位置继续下载。

(2)下载速度快。

使用多线程下载文件可以更快地完成文件的下载,多线程下载文件之所以快,是因为其抢占的服务器资源多。假设服务器同时最多服务100个用户,在服务器中一条线程对应一个用户,100条线程在计算机中并非并发执行,而是由CPU划分时间片轮流执行,如果A应用使用了99条线程下载文件,那么相当于占用了99个用户的资源。假设一秒内CPU分配给每条线程的平均执行时间是10 ms,A应用在服务器中一秒内就得到了990 ms的执行时间,而其他应用在一秒内只有10 ms的执行时间。就如同一个水龙头,在每秒出水量相等的情况下,放990 ms的水肯定比放10 ms的水要多。

实现多线程操作的可以分为:

取得网络连接;
初始化多线程下载信息;
"开辟"硬盘空间;
将从网络数据放入已申请的空间中;
关闭资源。

通过下图来加深对多线程的理解。


此文件的大小是6 MB,共有三条线程同时进行下载,实现过程如下所示。

(1)首先要根据要访问的URL路径去调用openConnection()方法,得到HttpUrlConnection对象。HttpUrlConnection调用它的方法得到下载文件的长度,然后设置本地文件的长度。
Int  filesize = HttpURLConnection.getContentLength();  
RandomAccessFile file = new RandomAccessFile("QQWubiSetup.exe","rw"); 
可以使用RandomAccessFile随机访问类。

RandomAccessFile和File的区别:

RandomAccessFile将FileInputStream和FileOutputStream整合到一起,而且支持将从文件任意字节处读或写数据,File类只是将文件作为整体来处理文件的,不能读写文件。
file.setLength(filesize); 
调用setLength(filesize)方法设置文件的长度,file可以达到下载文件的长度,但是它的内部不存在我们要下载文件的的数据,而是File类的特有的一些初始化的数据。

(2)根据文件长度和线程数计算每条线程下载的数据长度和下载位置。例如,文件的长度为6 MB,线程数为3,那么,每条线程下载的数据长度为2 MB,每条线程开始下载的位置如上图所示。
需要计算每一条线程需要下载的长度、每条线程下载的开始位置。例如,如果每一条线程是2 MB,那么第一条线程就是从0开始,第二条就是从2开始,以此类推。
但是这样就引出了一个问题:在下载时,怎样去指定这些线程开始下载的位置呢?
HTTP协议已经为我们解决这个问题,它可以给我们提供了一个Range头。

(3)使用HTTP的Range头字段指定每条线程从文件的什么位置开始下载,例如,指定从文件的2 MB位置开始下载文件,代码如下所示。

HttpURLConnection.setRequestProperty("Range", "bytes=2097152-"); 
我们设置的请求头Range字段,就是bytes=2097152,2 MB的字节,比如说指定了上图的线程2,在下载的过程中只要设置了这个头,那么它就会从文件的A→B开始下载。
每条线程在各自的文件下载完之后,下载完的文件需要保存到一定的位置。这样就引入了RandomAccessFile类。

(4)保存文件,使用RandomAccessFile类指定每条线程从本地文件的一定位置开始写入数据。
下面的代码就可以指定从文件的什么位置开始写入数据。
RandomAccessFile threadfile = new RandomAccessFile("QQWubiSetup.exe ","rw");  
threadfile.seek(2097152); 
用时序图演示在此工程中实现多线程、断点下载的思路,每条线程负责写文件的某一段的数据,如下图所示。



整个工程的结构图如下图所示



多线程、断点实现过程如下:

(1)首先设计main.xml页面,当DownLoadActivity.java中单击下载按钮时,就会触发其单击事件,在单击事件内部中调用download()方法用于实现下载功能。

(2)编写实现下载download()方法,并在方法内开启一个线程,在其run()方法中new FileDownloader类。返回下载文件的大小和已经下载的数量。

在FileDownloader类中构造线程下载器。
在download方法中调用FileService类中操作线程的下载记录业务方法,得到各个线程的最后下载位置。
将DownloadProgressListener接口以对象的形式当做参数传入download方法中。
(3)在FileDownloader类中的download方法中new DownloadThread类实现断点多线程下载,并存储在指定的文件中。DownloadThread线程返回文件是否下载完成。
(4)在FileDownloader类中的download方法中完成未下载完的补救方案。
(5)如果已经下载完成,则删除数据库中的文件,并通过DownloadProgressListener接口中的onDownloadSize方法得到已经下载文件的数量。
(6)将下载文件的大小和已经下载的数量已经返回给新开启的线程中,通过Handle异步通信实现页面重绘。将文件的下载进度显示在UI界面上。
完成此功能需要解决的技术要点
完成页面UI和布局文件;
数据库中记录各线程已经下载的信息,对各个线程的下载记录进行操作;
构造下载器;
实现下载功能,并同时可以得到此时各个线程的下载数量;
完成下载的进度的实时更新;
得到下载文件的名字;
完成页面的实时更新。


因为多线程文件下载涉及Web服务端和Android客户端,所以需要分别建立这两部分工程。

===============================================================================================================

——————————————————————————————————————————————————————————————————————

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

(1)首先建立服务器段

本服务器段用的是XAMPP,相关搭建工作可以参考此网站

http://www.apachefriends.org/zh_cn/xampp.html

XAMPP网站


(2)Android客户端的建设

什么是多线程下载?
多线程下载其实就是迅雷,BT一些下载原理,通过多个线程同时和服务器连接,那么你就可以榨取到较高的带宽了,大致做法是将文件切割成N块,每块交给单独一个线程去下载,各自下载完成后将文件块组合成一个文件,程序上要完成做切割和组装的小算法
什么是断点续传?
断点续传,就是当我们下载未结束时候,退出保存下载进度,当下次打开继续下载的时接着上次的进度继续下载,不用每次下载都重新开始,那么有关断点续传的原理和实现手段

,可参考我以前的一篇总结http://blog.csdn.net/shimiso/article/details/5956314 里面详细讲解http协议断点续传的原理,务必要看懂,否则你无法真正理解本节代码


怎么完成多线程断点续传?
将两者合二为一需要程序记住每个文件块的下载进度,并保存入库,当下载程序启动时候你需要判断程序是否已经下载过该文件,并取出各个文件块的保存记录,换算出下载进度继续下载,在这里你需要掌握java多线程的基本知识,handler的使用,以及集合,算法,文件操作等基本技能,同时还要解决sqlite数据库的同步问题,因为它是不太怎么支持多线程操作的,控制不好经常会出现库被锁定的异常,同时在android2.3以后就不能activity中直接操作http,否则你将收到系统送上的NetworkOnMainThreadException异常,在UI体验上一定记住要使用异步完成,既然大致思路已经清楚,

下面我们开始分析程序:

1.创建数据库

  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.  }  
package cn.demo.DBHelper;
 
 import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
 
     /**
      * 建立一个数据库帮助类
      */
 public class DBHelper extends SQLiteOpenHelper {
     //download.db-->数据库名
     public DBHelper(Context context) {
         super(context, "download.db", null, 1);
     }
     
     /**
      * 在download.db数据库下创建一个download_info表存储下载信息
      */
     @Override
     public void onCreate(SQLiteDatabase db) {
         db.execSQL("create table download_info(_id integer PRIMARY KEY AUTOINCREMENT, thread_id integer, "
                 + "start_pos integer, end_pos integer, compelete_size integer,url char)");
     }
     @Override
     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
 
     }
 
 }
2.数据库操作要借助单例和同步,来保证线程的执行顺序,以免多个线程争相抢用sqlite资源导致异常出现

  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. }  
package cn.demo.Dao;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import cn.demo.DBHelper.DBHelper;
import cn.demo.entity.DownloadInfo;

/**
 * 
 * 一个业务类
 */
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  SQLiteDatabase getConnection() {
		SQLiteDatabase sqliteDatabase = null;
		try { 
			sqliteDatabase= new DBHelper(context).getReadableDatabase();
		} catch (Exception e) {  
		}
		return sqliteDatabase;
	}

	/**
	 * 查看数据库中是否有数据
	 */
	public synchronized boolean isHasInfors(String urlstr) {
		SQLiteDatabase database = getConnection();
		int count = -1;
		Cursor cursor = null;
		try {
			String sql = "select count(*)  from download_info where url=?";
			cursor = database.rawQuery(sql, new String[] { urlstr });
			if (cursor.moveToFirst()) {
				count = cursor.getInt(0);
			} 
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (null != database) {
				database.close();
			}
			if (null != cursor) {
				cursor.close();
			}
		}
		return count == 0;
	}

	/**
	 * 保存 下载的具体信息
	 */
	public synchronized void saveInfos(List<DownloadInfo> infos) {
		SQLiteDatabase database = getConnection();
		try {
			for (DownloadInfo info : infos) {
				String sql = "insert into download_info(thread_id,start_pos, end_pos,compelete_size,url) values (?,?,?,?,?)";
				Object[] bindArgs = { info.getThreadId(), info.getStartPos(),
						info.getEndPos(), info.getCompeleteSize(),
						info.getUrl() };
				database.execSQL(sql, bindArgs);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (null != database) {
				database.close();
			}
		}
	}

	/**
	 * 得到下载具体信息
	 */
	public synchronized List<DownloadInfo> getInfos(String urlstr) {
		List<DownloadInfo> list = new ArrayList<DownloadInfo>();
		SQLiteDatabase database = getConnection();
		Cursor cursor = null;
		try {
			String sql = "select thread_id, start_pos, end_pos,compelete_size,url from download_info where url=?";
			cursor = database.rawQuery(sql, new String[] { urlstr });
			while (cursor.moveToNext()) {
				DownloadInfo info = new DownloadInfo(cursor.getInt(0),
						cursor.getInt(1), cursor.getInt(2), cursor.getInt(3),
						cursor.getString(4));
				list.add(info);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (null != database) {
				database.close();
			}
			if (null != cursor) {
				cursor.close();
			}
		}
		return list;
	}

	/**
	 * 更新数据库中的下载信息
	 */
	public synchronized void updataInfos(int threadId, int compeleteSize, String urlstr) {
		SQLiteDatabase database = getConnection();
		try {
			String sql = "update download_info set compelete_size=? where thread_id=? and url=?";
			Object[] bindArgs = { compeleteSize, threadId, urlstr };
			database.execSQL(sql, bindArgs);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (null != database) {
				database.close();
			}
		}
	}

	/**
	 * 下载完成后删除数据库中的数据
	 */
	public synchronized void delete(String url) {
		SQLiteDatabase database = getConnection();
		try {
			database.delete("download_info", "url=?", new String[] { url });
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (null != database) {
				database.close();
			}
		}
	}
}

3.Adapter类

  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. }  
package cn.demo.download;

import java.util.List;
import java.util.Map;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.TextView;




public class DownLoadAdapter extends BaseAdapter{
	 
	private LayoutInflater mInflater;
	private List<Map<String, String>> data;
	private Context context;
	private OnClickListener click;
	
	public DownLoadAdapter(Context context,List<Map<String, String>> data) {
		this.context=context;
		mInflater = LayoutInflater.from(context);
		this.data=data;
	}
	public void refresh(List<Map<String, String>> data) {
		this.data=data;
		this.notifyDataSetChanged();
	}
	public void setOnclick(OnClickListener click) {
		 this.click=click;
	}
	
	
	@Override
	public int getCount() {
		return data.size();
	}

	@Override
	public Object getItem(int position) {
		return data.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public View getView(final int position, View convertView, ViewGroup parent) {
		final Map<String, String> bean=data.get(position);
		ViewHolder holder = null;
		if (convertView == null) {
			convertView = mInflater.inflate(R.layout.list_item, null);
			holder = new ViewHolder(); 
			holder.resouceName=(TextView) convertView.findViewById(R.id.tv_resouce_name);
			holder.startDownload=(Button) convertView.findViewById(R.id.btn_start);
			holder.pauseDownload=(Button) convertView.findViewById(R.id.btn_pause);
			convertView.setTag(holder);
		} else {
			holder = (ViewHolder) convertView.getTag();
		} 
	 	holder.resouceName.setText(bean.get("name")); 
		return convertView;
	}
	public OnClickListener getClick() {
		return click;
	}
	public void setClick(OnClickListener click) {
		this.click = click;
	}
	private class ViewHolder { 
        public TextView resouceName;
        public Button startDownload;
        public Button pauseDownload;
      
        
     
    }
	
}

4.注意子线程不要影响主UI线程,灵活运用task和handler,各取所长,保证用户体验,handler通常在主线程中有利于专门负责处理UI的一些工作

  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://10.0.1.163/shine/";  
  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""one.apk");  
  75.          data.add(map);  
  76.          map = new HashMap<String, String>();  
  77.          map.put("name""应用程序安装.apk");  
  78.          data.add(map);  
  79.          map = new HashMap<String, String>();  
  80.          map.put("name""手机闪光灯手电筒 1.0.apk");  
  81.          data.add(map);  
  82.          map = new HashMap<String, String>();  
  83.          map.put("name""随手记(理财记账) 9.0.0.2.apk");  
  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.  }  
package cn.demo.download;
 
 import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.app.ListActivity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ProgressBar; 
import android.widget.TextView;
import android.widget.Toast;
import cn.demo.entity.LoadInfo;
import cn.demo.service.Downloader;

 public class MainActivity extends ListActivity { 
     // 固定下载的资源路径,这里可以设置网络上的地址
     private static final String URL = "http://10.0.1.163/shine/";
     // 固定存放下载的音乐的路径:SD卡目录下
     private static final String SD_PATH = "/mnt/sdcard/";
     // 存放各个下载器
     private Map<String, Downloader> downloaders = new HashMap<String, Downloader>();
     // 存放与下载器对应的进度条
     private Map<String, ProgressBar> ProgressBars = new HashMap<String, ProgressBar>();
     /**
      * 利用消息处理机制适时更新进度条
      */
     private Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
             if (msg.what == 1) {
                 String url = (String) msg.obj;
                 int length = msg.arg1;
                 ProgressBar bar = ProgressBars.get(url);
                 if (bar != null) {
                     // 设置进度条按读取的length长度更新
                     bar.incrementProgressBy(length);
                     if (bar.getProgress() == bar.getMax()) {
                    	 LinearLayout layout = (LinearLayout) bar.getParent();
                    	 TextView resouceName=(TextView)layout.findViewById(R.id.tv_resouce_name);
                         Toast.makeText(MainActivity.this, "["+resouceName.getText()+"]下载完成!", Toast.LENGTH_SHORT).show();
                         // 下载完成后清除进度条并将map中的数据清空
                         layout.removeView(bar);
                         ProgressBars.remove(url);
                         downloaders.get(url).delete(url);
                         downloaders.get(url).reset();
                         downloaders.remove(url);
                         
                         Button btn_start=(Button)layout.findViewById(R.id.btn_start);
                         Button btn_pause=(Button)layout.findViewById(R.id.btn_pause);
                         btn_pause.setVisibility(View.GONE);
                         btn_start.setVisibility(View.GONE);
                     }
                 }
             }
         }
     };
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main); 
         showListView();
     }
     // 显示listView,这里可以随便添加
     private void showListView() {
         List<Map<String, String>> data = new ArrayList<Map<String, String>>();
         Map<String, String> map = new HashMap<String, String>();
         map.put("name", "one.apk");
         data.add(map);
         map = new HashMap<String, String>();
         map.put("name", "应用程序安装.apk");
         data.add(map);
         map = new HashMap<String, String>();
         map.put("name", "手机闪光灯手电筒 1.0.apk");
         data.add(map);
         map = new HashMap<String, String>();
         map.put("name", "随手记(理财记账) 9.0.0.2.apk");
         data.add(map);
         DownLoadAdapter adapter=new DownLoadAdapter(this,data);  
         setListAdapter(adapter);
         
     }
     /**
      * 响应开始下载按钮的点击事件
      */
     public void startDownload(View v) {
         // 得到textView的内容 
         LinearLayout layout = (LinearLayout) v.getParent();
         String resouceName = ((TextView) layout.findViewById(R.id.tv_resouce_name)).getText().toString();
         String urlstr = URL + resouceName;
         String localfile = SD_PATH + resouceName;
         //设置下载线程数为4,这里是我为了方便随便固定的
         String threadcount = "4";
         DownloadTask downloadTask=new DownloadTask(v);
         downloadTask.execute(urlstr,localfile,threadcount);
       
     };
    class DownloadTask extends AsyncTask<String, Integer, LoadInfo>{
    	Downloader downloader=null; 
    	View v=null;
    	String urlstr=null;
    	public DownloadTask(final View v){
    		this.v=v;
    	}  
    	@Override
    	protected void onPreExecute() { 
    		Button btn_start=(Button)((View)v.getParent()).findViewById(R.id.btn_start);
    		Button btn_pause=(Button)((View)v.getParent()).findViewById(R.id.btn_pause);
    		btn_start.setVisibility(View.GONE);
    		btn_pause.setVisibility(View.VISIBLE);
    	}
		@Override
		protected LoadInfo doInBackground(String... params) {
			urlstr=params[0];
			String localfile=params[1];
			int threadcount=Integer.parseInt(params[2]);
			 // 初始化一个downloader下载器
	         downloader = downloaders.get(urlstr);
	         if (downloader == null) {
	             downloader = new Downloader(urlstr, localfile, threadcount, MainActivity.this, mHandler);
	             downloaders.put(urlstr, downloader);
	         }
	         if (downloader.isdownloading())
	        	 return null;
	         // 得到下载信息类的个数组成集合
	         return downloader.getDownloaderInfors(); 
		}
		@Override
		protected void onPostExecute(LoadInfo loadInfo) {
			if(loadInfo!=null){
				 // 显示进度条
		         showProgress(loadInfo, urlstr, v);
		         // 调用方法开始下载
		         downloader.download();
			} 
		}
		 
	 };
     /**
      * 显示进度条
      */
     private void showProgress(LoadInfo loadInfo, String url, View v) {
         ProgressBar bar = ProgressBars.get(url);
         if (bar == null) {
             bar = new ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal);
             bar.setMax(loadInfo.getFileSize());
             bar.setProgress(loadInfo.getComplete());
             ProgressBars.put(url, bar);
             LinearLayout.LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT, 5);
             ((LinearLayout) ((LinearLayout) v.getParent()).getParent()).addView(bar, params);
         }
     }
     /**
      * 响应暂停下载按钮的点击事件
      */
     public void pauseDownload(View v) {
         LinearLayout layout = (LinearLayout) v.getParent();
         String resouceName = ((TextView) layout.findViewById(R.id.tv_resouce_name)).getText().toString();
         String urlstr = URL + resouceName;
         downloaders.get(urlstr).pause();
         Button btn_start=(Button)((View)v.getParent()).findViewById(R.id.btn_start);
 		 Button btn_pause=(Button)((View)v.getParent()).findViewById(R.id.btn_pause);
         btn_pause.setVisibility(View.GONE);
         btn_start.setVisibility(View.VISIBLE);
     }
 }

5.这是一个信息的实体,记录了一些字典信息,可以认为是一个简单bean对象

  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.  }  
package cn.demo.entity;
 /**
  *创建一个下载信息的实体类
  */
 public class DownloadInfo {
     private int threadId;//下载器id
     private int startPos;//开始点
     private int endPos;//结束点
     private int compeleteSize;//完成度
     private String url;//下载器网络标识
     public DownloadInfo(int threadId, int startPos, int endPos,
             int compeleteSize,String url) {
         this.threadId = threadId;
         this.startPos = startPos;
         this.endPos = endPos;
         this.compeleteSize = compeleteSize;
         this.url=url;
     }
     public DownloadInfo() {
     }
     public String getUrl() {
         return url;
     }
     public void setUrl(String url) {
         this.url = url;
     }
     public int getThreadId() {
         return threadId;
     }
     public void setThreadId(int threadId) {
         this.threadId = threadId;
     }
     public int getStartPos() {
         return startPos;
     }
     public void setStartPos(int startPos) {
         this.startPos = startPos;
     }
     public int getEndPos() {
         return endPos;
     }
     public void setEndPos(int endPos) {
         this.endPos = endPos;
     }
     public int getCompeleteSize() {
         return compeleteSize;
     }
     public void setCompeleteSize(int compeleteSize) {
         this.compeleteSize = compeleteSize;
     }
 
     @Override
     public String toString() {
         return "DownloadInfo [threadId=" + threadId
                 + ", startPos=" + startPos + ", endPos=" + endPos
                 + ", compeleteSize=" + compeleteSize +"]";
     }
 }

6.

  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.  }  
package cn.demo.entity;
 /**
  *自定义的一个记载下载器详细信息的类 
  */
 public class LoadInfo {
     public int fileSize;//文件大小
     private int complete;//完成度
     private String urlstring;//下载器标识
     public LoadInfo(int fileSize, int complete, String urlstring) {
         this.fileSize = fileSize;
         this.complete = complete;
         this.urlstring = urlstring;
     }
     public LoadInfo() {
     }
     public int getFileSize() {
         return fileSize;
     }
     public void setFileSize(int fileSize) {
         this.fileSize = fileSize;
     }
     public int getComplete() {
         return complete;
     }
     public void setComplete(int complete) {
         this.complete = complete;
     }
     public String getUrlstring() {
         return urlstring;
     }
     public void setUrlstring(String urlstring) {
         this.urlstring = urlstring;
     }
     @Override
     public String toString() {
         return "LoadInfo [fileSize=" + fileSize + ", complete=" + complete
                 + ", urlstring=" + urlstring + "]";
     }
 }

7.这是一个核心类,专门用来处理下载的

  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.  }  
package cn.demo.service;
 
 import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import cn.demo.Dao.Dao;
import cn.demo.entity.DownloadInfo;
import cn.demo.entity.LoadInfo;
 
 public class Downloader {
     private String urlstr;// 下载的地址
     private String localfile;// 保存路径
     private int threadcount;// 线程数
     private Handler mHandler;// 消息处理器 
     private int fileSize;// 所要下载的文件的大小
     private Context context; 
     private List<DownloadInfo> infos;// 存放下载信息类的集合
     private static final int INIT = 1;//定义三种下载的状态:初始化状态,正在下载状态,暂停状态
     private static final int DOWNLOADING = 2;
     private static final int PAUSE = 3;
     private int state = INIT;
 
     public Downloader(String urlstr, String localfile, int threadcount,
             Context context, Handler mHandler) {
         this.urlstr = urlstr;
         this.localfile = localfile;
         this.threadcount = threadcount;
         this.mHandler = mHandler;
         this.context = context;
     }
     /**
      *判断是否正在下载 
      */
     public boolean isdownloading() {
         return state == DOWNLOADING;
     }
     /**
      * 得到downloader里的信息
      * 首先进行判断是否是第一次下载,如果是第一次就要进行初始化,并将下载器的信息保存到数据库中
      * 如果不是第一次下载,那就要从数据库中读出之前下载的信息(起始位置,结束为止,文件大小等),并将下载信息返回给下载器
      */
     public LoadInfo getDownloaderInfors() {
         if (isFirst(urlstr)) {
             Log.v("TAG", "isFirst");
             init();
             int range = fileSize / threadcount;
             infos = new ArrayList<DownloadInfo>();
             for (int i = 0; i < threadcount - 1; i++) {
                 DownloadInfo info = new DownloadInfo(i, i * range, (i + 1)* range - 1, 0, urlstr);
                 infos.add(info);
             }
             DownloadInfo info = new DownloadInfo(threadcount - 1,(threadcount - 1) * range, fileSize - 1, 0, urlstr);
             infos.add(info);
             //保存infos中的数据到数据库
             Dao.getInstance(context).saveInfos(infos);
             //创建一个LoadInfo对象记载下载器的具体信息
             LoadInfo loadInfo = new LoadInfo(fileSize, 0, urlstr);
             return loadInfo;
         } else {
             //得到数据库中已有的urlstr的下载器的具体信息
             infos = Dao.getInstance(context).getInfos(urlstr);
             Log.v("TAG", "not isFirst size=" + infos.size());
             int size = 0;
             int compeleteSize = 0;
             for (DownloadInfo info : infos) {
                 compeleteSize += info.getCompeleteSize();
                 size += info.getEndPos() - info.getStartPos() + 1;
             }
             return new LoadInfo(size, compeleteSize, urlstr);
         }
     }
 
     /**
      * 初始化
      */
     private void init() {
         try {
             URL url = new URL(urlstr);
             HttpURLConnection connection = (HttpURLConnection) url.openConnection();
             connection.setConnectTimeout(5000);
             connection.setRequestMethod("GET");
             fileSize = connection.getContentLength();
 
             File file = new File(localfile);
             if (!file.exists()) {
                 file.createNewFile();
             }
             // 本地访问文件
             RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");
             accessFile.setLength(fileSize);
             accessFile.close();
             connection.disconnect();
         } catch (Exception e) {
             e.printStackTrace();
         }
     }  
     /**
      * 判断是否是第一次 下载
      */
     private boolean isFirst(String urlstr) {
         return Dao.getInstance(context).isHasInfors(urlstr);
     }
 
     /**
      * 利用线程开始下载数据
      */
     public void download() {
         if (infos != null) {
             if (state == DOWNLOADING)
                 return;
             state = DOWNLOADING;
             for (DownloadInfo info : infos) {
                 new MyThread(info.getThreadId(), info.getStartPos(),
                         info.getEndPos(), info.getCompeleteSize(),
                         info.getUrl()).start();
             }
         }
     }
 
     public class MyThread extends Thread {
         private int threadId;
         private int startPos;
         private int endPos;
         private int compeleteSize;
         private String urlstr;
 
         public MyThread(int threadId, int startPos, int endPos,
                 int compeleteSize, String urlstr) {
             this.threadId = threadId;
             this.startPos = startPos;
             this.endPos = endPos;
             this.compeleteSize = compeleteSize;
             this.urlstr = urlstr;
         }
         @Override
         public void run() {
             HttpURLConnection connection = null;
             RandomAccessFile randomAccessFile = null;
             InputStream is = null;
             try {
                 URL url = new URL(urlstr);
                 connection = (HttpURLConnection) url.openConnection();
                 connection.setConnectTimeout(5000);
                 connection.setRequestMethod("GET");
                 // 设置范围,格式为Range:bytes x-y;
                 connection.setRequestProperty("Range", "bytes="+(startPos + compeleteSize) + "-" + endPos);
 
                 randomAccessFile = new RandomAccessFile(localfile, "rwd");
                 randomAccessFile.seek(startPos + compeleteSize);
                 // 将要下载的文件写到保存在保存路径下的文件中
                 is = connection.getInputStream();
                 byte[] buffer = new byte[4096];
                 int length = -1;
                 while ((length = is.read(buffer)) != -1) {
                     randomAccessFile.write(buffer, 0, length);
                     compeleteSize += length;
                     // 更新数据库中的下载信息
                     Dao.getInstance(context).updataInfos(threadId, compeleteSize, urlstr);
                     // 用消息将下载信息传给进度条,对进度条进行更新
                     Message message = Message.obtain();
                     message.what = 1;
                     message.obj = urlstr;
                     message.arg1 = length;
                     mHandler.sendMessage(message);
                     if (state == PAUSE) {
                         return;
                     }
                 }
             } catch (Exception e) {
                 e.printStackTrace();
             }  
         }
     }
     //删除数据库中urlstr对应的下载器信息
     public void delete(String urlstr) {
    	 Dao.getInstance(context).delete(urlstr);
     }
     //设置暂停
     public void pause() {
         state = PAUSE;
     }
     //重置下载状态
     public void reset() {
         state = INIT;
     }
 }



8.

  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>  
<?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:orientation="vertical"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content">
     <LinearLayout
            android:orientation="horizontal"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="5dip">
         <TextView 
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:id="@+id/tv_resouce_name"/>
         <Button
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:text="下载"
             android:id="@+id/btn_start"
             android:onClick="startDownload"/>
         <Button
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:text="暂停"
             android:visibility="gone"
             android:id="@+id/btn_pause"
             android:onClick="pauseDownload"/>
       </LinearLayout>
 </LinearLayout>

9

  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="1600px"  
  5.      android:layout_height="fill_parent"  
  6.      android:layout_marginTop="200px"  
  7.      android:id="@+id/llRoot">  
  8.      <ListView android:id="@android:id/list"  
  9.          android:layout_width="fill_parent"  
  10.          android:layout_height="fill_parent">  
  11.      </ListView>  
  12.  </LinearLayout>  
<?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
     android:layout_width="1600px"
     android:layout_height="fill_parent"
     android:layout_marginTop="200px"
     android:id="@+id/llRoot">
     <ListView android:id="@android:id/list"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent">
     </ListView>
 </LinearLayout>

10

  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>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="cn.demo.download"
    android:versionCode="1"
    android:versionName="1.0">

    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="8" />
	<uses-permission android:name="android.permission.INTERNET"/> 
	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <application android:label="@string/app_name"
        android:icon="@drawable/ic_launcher"
        android:theme="@style/AppTheme">
		<activity
	            android:name=".MainActivity" 
	            android:label="@string/app_name" >
	            <intent-filter>
	                <action android:name="android.intent.action.MAIN" /> 
	                <category android:name="android.intent.category.LAUNCHER" />
	            </intent-filter>
	        </activity>
    </application>

</manifest>



案例下载地址:

案例下载


参考博客:

(1)细致剖析Android多线程断点续传下载器

(2)Android多线程下载案例

(3)多线程视频介绍




更多 0
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值