多线程断点下载器

现在我开始介绍我的多线程断点下载器..

这是项目的目录


先看看项目运行的截图






从我的项目工程目录来看.
我是采用DAO操作类来对数据库进行增删改查的操作.
然后通过实体类ThreadInfo像DAO操作类传送对象.

我们先来看看数据库帮助类的代码.
从代码中可以看出..我在数据库中定义了 thread_id代表线程的ID url代表下载路径 startPos代表开始位置 endPos代表结束位置 finished代表实时下载的大小
1.数据库帮助类
  1. package com.hgq.download.data;

  2. import android.content.Context;
  3. import android.database.sqlite.SQLiteDatabase;
  4. import android.database.sqlite.SQLiteOpenHelper;

  5. public class MyDatabaseHepler extends SQLiteOpenHelper
  6. {

  7.         public static final String DB_NAME = "download.db";
  8.         public static final int DB_VERSION = 1;
  9.         public static final String TABLE_NAME = "thread_info";
  10.         private static final String CREATE_TABLE = "CREATE TABLE "+ TABLE_NAME 
  11.                         + " ( "
  12.                         + "_id INTEGER PRIMARY KEY,"
  13.                         + "thread_id INTEGER,"
  14.                         + "url TEXT,"
  15.                         + "startPos INTEGER,"
  16.                         + "endPos INTEGER,"
  17.                         + "finished INTEGER"
  18.                         + " ) ";
  19.         private static final String DROP_TABLE = 
  20.                         "DROP TABLE IF EXISTS " + TABLE_NAME;

  21.         public MyDatabaseHepler(Context context)
  22.         {
  23.                 super(context, DB_NAME, null, DB_VERSION);
  24.         }

  25.         @Override
  26.         public void onCreate(SQLiteDatabase db)
  27.         {
  28.                 db.execSQL(CREATE_TABLE);
  29.         }

  30.         @Override
  31.         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
  32.         {
  33.                 db.execSQL(DROP_TABLE);
  34.                 onCreate(db);
  35.         }

  36. }
复制代码
为了操作数据库方便.
我写了一个实体类
2.实体类
  1. package com.hgq.download.entities;

  2. /**
  3. * 线程下载记录 实体类
  4. */
  5. public class ThreadInfo
  6. {
  7.         private int thread_id;
  8.         private String url;
  9.         private int startPos;
  10.         private int endPos;
  11.         private int finished;

  12.         public ThreadInfo()
  13.         {
  14.                 super();
  15.         };

  16.         public ThreadInfo(int threadId, String url, int startPos, int endPos,
  17.                         int finished)
  18.         {
  19.                 super();
  20.                 this.thread_id = threadId;
  21.                 this.url = url;
  22.                 this.startPos = startPos;
  23.                 this.endPos = endPos;
  24.                 this.finished = finished;

  25.         };

  26.         public int getThread_id()
  27.         {
  28.                 return thread_id;
  29.         }

  30.         public void setThread_id(int thread_id)
  31.         {
  32.                 this.thread_id = thread_id;
  33.         }

  34.         public String getUrl()
  35.         {
  36.                 return url;
  37.         }

  38.         public void setUrl(String url)
  39.         {
  40.                 this.url = url;
  41.         }

  42.         public int getStartPos()
  43.         {
  44.                 return startPos;
  45.         }

  46.         public void setStartPos(int startPos)
  47.         {
  48.                 this.startPos = startPos;
  49.         }

  50.         public int getEndPos()
  51.         {
  52.                 return endPos;
  53.         }

  54.         public void setEndPos(int endPos)
  55.         {
  56.                 this.endPos = endPos;
  57.         }

  58.         public int getFinished()
  59.         {
  60.                 return finished;
  61.         }

  62.         public void setFinished(int finished)
  63.         {
  64.                 this.finished = finished;
  65.         }

  66.         @Override
  67.         public String toString()
  68.         {
  69.                 return "ThreadInfo [thread_id=" + thread_id + ", url=" + url
  70.                                 + ", startPos=" + startPos + ", endPos=" + endPos
  71.                                 + ", finished=" + finished + "]";
  72.         }

  73. }
复制代码
3.DAO操作类
  1. package com.hgq.download.data;

  2. import java.util.ArrayList;
  3. import java.util.List;

  4. import android.content.ContentValues;
  5. import android.content.Context;
  6. import android.database.Cursor;
  7. import android.database.sqlite.SQLiteDatabase;
  8. import android.util.Log;

  9. import com.hgq.download.entities.ThreadInfo;

  10. /**
  11. * 操作类
  12. */
  13. public class ThreadDAO
  14. {
  15.         private MyDatabaseHepler dbHeple;
  16.         private SQLiteDatabase db;

  17.         public ThreadDAO(Context context)
  18.         {
  19.                 dbHeple = new MyDatabaseHepler(context);
  20.                 db = dbHeple.getWritableDatabase();
  21.         }

  22.         // 插入
  23.         public void insert(ThreadInfo info)
  24.         {
  25.                 System.out.println(info);
  26.                 System.out.println("thread_id" + info.getThread_id());

  27.                 ContentValues values = new ContentValues();
  28.                 values.put("thread_id", info.getThread_id());
  29.                 values.put("url", info.getUrl());
  30.                 values.put("startPos", info.getStartPos());
  31.                 values.put("endPos", info.getEndPos());
  32.                 values.put("finished", info.getFinished());

  33.                 db.insert(MyDatabaseHepler.TABLE_NAME, null, values);
  34.         }

  35.         // 更新
  36.         public void update(int len,String url,int id){
  37.                 db.execSQL(
  38.                                 "UPDATE thread_info SET finished = finished+? WHERE url=? AND thread_id=?",
  39.                                 new Object[]{len,url,id});
  40.         }

  41.         // 删除
  42.         public void delete(String url)
  43.         {
  44.                 Log.i("ThreadDao", url);
  45.                 db.delete(MyDatabaseHepler.TABLE_NAME, "url=?", new String[]
  46.                 { url });
  47.         }

  48.         // 查询
  49.         public List<ThreadInfo> getThreadInfos(String url)
  50.         {
  51.                 List<ThreadInfo> list = new ArrayList<ThreadInfo>();
  52.                 Cursor cursor = db.query(MyDatabaseHepler.TABLE_NAME, null, "url=?",
  53.                                 new String[]
  54.                                 { url }, null, null, null);

  55.                 while (cursor.moveToNext())
  56.                 {
  57.                         ThreadInfo info = new ThreadInfo();
  58.                         info.setThread_id(cursor.getInt(cursor.getColumnIndex("thread_id")));
  59.                         info.setUrl(cursor.getString(cursor.getColumnIndex("url")));
  60.                         info.setStartPos(cursor.getInt(cursor.getColumnIndex("startPos")));
  61.                         info.setEndPos(cursor.getInt(cursor.getColumnIndex("endPos")));
  62.                         info.setFinished(cursor.getInt(cursor.getColumnIndex("finished")));
  63.                         list.add(info);
  64.                 }
  65.                 cursor.close();
  66.                 return list;
  67.         }
  68. }
复制代码
构建下载器是整个项目的最核心部分.
4.下载器类
  1. package com.hgq.download.http;

  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.io.RandomAccessFile;
  6. import java.net.HttpURLConnection;
  7. import java.net.MalformedURLException;
  8. import java.net.URL;
  9. import java.util.List;

  10. import android.content.Context;
  11. import android.os.Handler;
  12. import android.util.Log;

  13. import com.hgq.download.data.ThreadDAO;
  14. import com.hgq.download.entities.ThreadInfo;

  15. /**
  16. * 下载器

  17. * @author 洪国强

  18. */
  19. public class Downloader
  20. {
  21.         public static final int INIT = 1;
  22.         public static final int DOWNLOADING = 2;
  23.         public static final int FINISHED = 3;
  24.         public static final int STOP = 4;

  25.         // 下载路径
  26.         private String urlPath = null;
  27.         // 保存文件路径
  28.         private String filePath = null;
  29.         // 线程数
  30.         private int threadNum;
  31.         // 保存文件大小
  32.         private int fileSize;
  33.         // 实例化操作类
  34.         private ThreadDAO dao;
  35.         // 下载状态
  36.         public int state;
  37.         // 消息
  38.         public Handler handler = null;

  39.         public Downloader(Context context, String urlPath, String filePath,
  40.                         int threadNum, Handler handler)
  41.         {
  42.                 this.urlPath = urlPath;
  43.                 this.filePath = filePath;
  44.                 this.threadNum = threadNum;
  45.                 this.handler = handler;
  46.                 dao = new ThreadDAO(context);
  47.         }

  48.         /**
  49.          * 获取网络文件长度,设置本地文件长度
  50.          */
  51.         public void start()
  52.         {
  53.                 // 下载进度
  54.                 int finished = 0;
  55.                 List<ThreadInfo> threads = dao.getThreadInfos(urlPath);

  56.                 // 第一次下载就初始化文件
  57.                 if (threads.size() == 0)
  58.                 {
  59.                         try
  60.                         {
  61.                                 // 创建URL
  62.                                 URL url = new URL(urlPath);
  63.                                 // 创建HttpURLConnection连接
  64.                                 HttpURLConnection conn = (HttpURLConnection) url
  65.                                                 .openConnection();
  66.                                 // 设置连接超时
  67.                                 conn.setConnectTimeout(1000 * 5);
  68.                                 // 设置连接方式
  69.                                 conn.setRequestMethod("GET");
  70.                                 // 获取文件大小
  71.                                 fileSize = conn.getContentLength();
  72.                                 Log.i("info", "fileSize----------->" + fileSize);

  73.                                 // 创建本地文件
  74.                                 File file = new File(filePath);
  75.                                 Log.i("info", "file----------->" + file);

  76.                                 Log.i("info", "!file.exists()-------->" + !file.exists());
  77.                                 if (!file.exists())
  78.                                         file.createNewFile();

  79.                                 RandomAccessFile raf = new RandomAccessFile(file, "rwd");
  80.                                 Log.i("info", "start() raf" + raf);

  81.                                 // 设置本地文件长度
  82.                                 raf.setLength(fileSize);
  83.                                 raf.close();
  84.                                 conn.disconnect();

  85.                                 /**
  86.                                  * 计算每个线程开始位置和结束位置
  87.                                  */
  88.                                 // 每个线程的长度
  89.                                 int len = fileSize / threadNum + 1;
  90.                                 for (int i = 0; i < threadNum; i++)
  91.                                 {
  92.                                         int startPos = i * len;
  93.                                         int endPos = startPos + len;
  94.                                         if (i == threadNum - 1)
  95.                                         {
  96.                                                 endPos = fileSize;
  97.                                         }

  98.                                         Log.i("info", "Downloader.start()---------------->i:" + i
  99.                                                         + ",startPos" + startPos + ",endPos" + endPos);
  100.                                         // 启动下载线程
  101.                                         new Thread(new DownloadThread(i, startPos, endPos)).start();
  102.                                         // 将下载线程加入到数据库中
  103.                                         ThreadInfo info = new ThreadInfo(i, urlPath, startPos,
  104.                                                         endPos, 0);
  105.                                         dao.insert(info);
  106.                                 }

  107.                         } catch (MalformedURLException e)
  108.                         {
  109.                                 e.printStackTrace();
  110.                         } catch (IOException e)
  111.                         {
  112.                                 e.printStackTrace();
  113.                         }
  114.                 } else
  115.                 {
  116.                         // 启动所有线程
  117.                         for (ThreadInfo info : threads)
  118.                         {
  119.                                 fileSize += info.getEndPos() - info.getStartPos();
  120.                                 finished += info.getFinished();
  121.                                 new Thread(new DownloadThread(info.getThread_id(),
  122.                                                 info.getStartPos(), info.getEndPos())).start();
  123.                         }
  124.                 }
  125.                 // 发送消息
  126.                 handler.obtainMessage(INIT, fileSize, finished).sendToTarget();
  127.         }

  128.         /**
  129.          * 下载线程
  130.          */
  131.         public class DownloadThread implements Runnable
  132.         {
  133.                 int id;
  134.                 int startPos = 0;
  135.                 int endPos = 0;

  136.                 public DownloadThread(int id, int startPos, int endPos)
  137.                 {
  138.                         this.id = id;
  139.                         this.startPos = startPos;
  140.                         this.endPos = endPos;
  141.                 }

  142.                 @Override
  143.                 public void run()
  144.                 {
  145.                         RandomAccessFile raf = null;
  146.                         InputStream is = null;
  147.                         try
  148.                         {
  149.                                 // 获取url
  150.                                 URL url = new URL(urlPath);
  151.                                 Log.i("info", "url------------->" + url);

  152.                                 HttpURLConnection conn = (HttpURLConnection) url
  153.                                                 .openConnection();
  154.                                 // 设置超时
  155.                                 conn.setConnectTimeout(1000 * 5);
  156.                                 // 设置连接类型
  157.                                 conn.setRequestMethod("GET");
  158.                                 // 设置下载范围
  159.                                 conn.setRequestProperty("Range", "btyes=" + startPos + "-"
  160.                                                 + endPos);
  161.                                 raf = new RandomAccessFile(new File(filePath), "rwd");
  162.                                 Log.i("info", "raf------------->" + raf);

  163.                                 // 设置写入位置
  164.                                 raf.seek(startPos);
  165.                                 // 获取输入流
  166.                                 is = conn.getInputStream();

  167.                                 byte[] buffer = new byte[1024];
  168.                                 int len;
  169.                                 while ((len = is.read(buffer)) != -1)
  170.                                 {
  171.                                         raf.write(buffer, 0, len);
  172.                                         // 更新下载进度条到数据库
  173.                                         dao.update(len, urlPath, id);
  174.                                         // 发送消息
  175.                                         handler.obtainMessage(DOWNLOADING, len, 0).sendToTarget();
  176.                                         if (state == STOP)
  177.                                         {
  178.                                                 break;
  179.                                         }
  180.                                 }

  181.                         } catch (MalformedURLException e)
  182.                         {
  183.                                 e.printStackTrace();
  184.                         } catch (IOException e)
  185.                         {
  186.                                 e.printStackTrace();
  187.                         }
  188.                 }
  189.         }

  190. }
复制代码
5.Activity类
  1. package com.hgq.download;

  2. import java.io.File;

  3. import android.app.Activity;
  4. import android.os.Bundle;
  5. import android.os.Environment;
  6. import android.os.Handler;
  7. import android.os.Message;
  8. import android.util.Log;
  9. import android.view.View;
  10. import android.view.View.OnClickListener;
  11. import android.widget.ArrayAdapter;
  12. import android.widget.Button;
  13. import android.widget.EditText;
  14. import android.widget.ProgressBar;
  15. import android.widget.Spinner;
  16. import android.widget.TextView;
  17. import android.widget.Toast;

  18. import com.hgq.download.data.ThreadDAO;
  19. import com.hgq.download.http.Downloader;

  20. public class MainActivity extends Activity implements OnClickListener
  21. {
  22.         private static final String SD_PATH = Environment
  23.                         .getExternalStorageDirectory().getAbsolutePath() + File.separator;

  24.         private Button butDown = null;
  25.         private Button butPause = null;
  26.         private EditText editPath = null;
  27.         private EditText editName = null;
  28.         private Spinner spinner = null;
  29.         private ProgressBar bar = null;
  30.         private TextView tvResult = null;

  31.         String[] arr = new String[]
  32.         { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };

  33.         private Downloader mDownloader = null;
  34.         private ThreadDAO mDao = null;
  35.         // 线程数
  36.         int threadNum = 0;

  37.         @Override
  38.         public void onCreate(Bundle savedInstanceState)
  39.         {
  40.                 super.onCreate(savedInstanceState);
  41.                 setContentView(R.layout.activity_main);

  42.                 // 实例化操作类
  43.                 mDao = new ThreadDAO(getApplicationContext());

  44.                 butDown = (Button) findViewById(R.id.butDown);
  45.                 butPause = (Button) findViewById(R.id.butPause);
  46.                 editPath = (EditText) findViewById(R.id.editPath);
  47.                 editName = (EditText) findViewById(R.id.editName);
  48.                 spinner = (Spinner) findViewById(R.id.spinner);
  49.                 bar = (ProgressBar) findViewById(R.id.bar);
  50.                 tvResult = (TextView) findViewById(R.id.tvResult);

  51.                 // 获取保存文件名
  52.                 String path = editPath.getText().toString();
  53.                 String name = path.substring(path.lastIndexOf("/") + 1);
  54.                 editName.setText(name);

  55.                 ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
  56.                                 android.R.layout.simple_spinner_item, arr);
  57.                 // 设置下拉列表显示风格
  58.                 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
  59.                 spinner.setAdapter(adapter);

  60.                 butDown.setOnClickListener(this);
  61.                 butPause.setOnClickListener(this);
  62.         }

  63.         @Override
  64.         public void onClick(View v)
  65.         {
  66.                 switch (v.getId())
  67.                 {
  68.                 case R.id.butDown:
  69.                         Log.i("info", "单击下载按钮");

  70.                         threadNum = spinner.getSelectedItemPosition() + 1;
  71.                         Log.i("info", "threadNum------------->" + threadNum);

  72.                         // 创建下载器,开始下载
  73.                         if (mDownloader == null)
  74.                         {
  75.                                 mDownloader = new Downloader(this, editPath.getText()
  76.                                                 .toString(), SD_PATH + editName.getText().toString(),
  77.                                                 threadNum, mHandler);
  78.                         }
  79.                         // 将状态设置成开始状态
  80.                         mDownloader.state = Downloader.DOWNLOADING;
  81.                         mDownloader.start();

  82.                         break;
  83.                 case R.id.butPause:
  84.                         if (mDownloader != null)
  85.                         {
  86.                                 mDownloader.state = Downloader.STOP;
  87.                         }
  88.                         break;
  89.                 default:
  90.                         break;
  91.                 }
  92.         }

  93.         @Override
  94.         protected void onDestroy()
  95.         {
  96.                 super.onDestroy();
  97.                 mDownloader.state = Downloader.STOP;
  98.         }

  99.         // 线程消息处理
  100.         public Handler mHandler = new Handler()
  101.         {
  102.                 public void handleMessage(Message msg)
  103.                 {
  104.                         switch (msg.what)
  105.                         {
  106.                         case Downloader.INIT:
  107.                                 bar.setMax(msg.arg1);
  108.                                 bar.setProgress(msg.arg2);
  109.                                 bar.setVisibility(View.VISIBLE);
  110.                                 tvResult.setVisibility(View.VISIBLE);
  111.                                 break;
  112.                         case Downloader.DOWNLOADING:
  113.                                 bar.incrementProgressBy(msg.arg1);
  114.                                 float result1 = (float) bar.getProgress()
  115.                                                 / (float) bar.getMax();
  116.                                 int p = (int) (result1 * 100);
  117.                                 tvResult.setText(p + "%");

  118.                                 if (bar.getProgress() >= bar.getMax())
  119.                                 {
  120.                                         Toast.makeText(MainActivity.this, "文件下载完成", 1).show();
  121.                                         bar.setProgress(0);
  122.                                         bar.setVisibility(View.GONE);
  123.                                         tvResult.setVisibility(View.GONE);
  124.                                         mDownloader = null;
  125.                                         // 删除文件记录
  126.                                         mDao.delete(editPath.getText().toString());
  127.                                 }
  128.                                 break;
  129.                         }
  130.                 };
  131.         };
  132. }
复制代码
在做这个项目之前.我看了传智播客的多线程断点下载器的教程..
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值