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

本文实例讲述了Android编程开发实现多线程断点续传下载器。分享给大家供大家参考,具体如下:

使用多线程断点续传下载器在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度,在下载过程中记录每个线程已拷贝数据的数量,如果下载中断,比如无信号断线、电量不足等情况下,这就需要使用到断点续传功能,下次启动时从记录位置继续下载,可避免重复部分的下载。这里采用数据库来记录下载的进度。

效果图:

断点续传

1.断点续传需要在下载过程中记录每条线程的下载进度
2.每次下载开始之前先读取数据库,查询是否有未完成的记录,有就继续下载,没有则创建新记录插入数据库
3.在每次向文件中写入数据之后,在数据库中更新下载进度
4.下载完成之后删除数据库中下载记录

Handler传输数据

这个主要用来记录百分比,每下载一部分数据就通知主线程来记录时间

1.主线程中创建的View只能在主线程中修改,其他线程只能通过和主线程通信,在主线程中改变View数据
2.我们使用Handler可以处理这种需求

主线程中创建Handler,重写handleMessage()方法

新线程中使用Handler发送消息,主线程即可收到消息,并且执行handleMessage()方法

动态生成新View

可实现多任务下载

1.创建XML文件,将要生成的View配置好
2.获取系统服务LayoutInflater,用来生成新的View
复制代码 代码如下:LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
3.使用inflate(int resource, ViewGroup root)方法生成新的View
4.调用当前页面中某个容器的addView,将新创建的View添加进来

示例

进度条样式 download.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" > <LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" > <!--进度条样式默认为圆形进度条,水平进度条需要配置style属性, ?android:attr/progressBarStyleHorizontal --> <ProgressBar android:layout_width="fill_parent" android:layout_height="20dp" style="?android:attr/progressBarStyleHorizontal" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="0%" /> </LinearLayout> <Button android:layout_width="40dp" android:layout_height="40dp" android:onClick="pause" android:text="||" /> </LinearLayout>

顶部样式 main.xml

<?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="fill_parent" android:id="@+id/root" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="请输入下载路径" /> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginBottom="30dp" > <EditText android:id="@+id/path" android:layout_width="fill_parent" android:layout_height="wrap_content" android:singleLine="true" android:layout_weight="1" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="下载" android:onClick="download" /> </LinearLayout> </LinearLayout>

MainActivity.java

public class MainActivity extends Activity { private LayoutInflater inflater; private LinearLayout rootLinearLayout; private EditText pathEditText; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //动态生成新View,获取系统服务LayoutInflater,用来生成新的View inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); rootLinearLayout = (LinearLayout) findViewById(R.id.root); pathEditText = (EditText) findViewById(R.id.path); // 窗体创建之后, 查询数据库是否有未完成任务, 如果有, 创建进度条等组件, 继续下载 List<String> list = new InfoDao(this).queryUndone(); for (String path : list) createDownload(path); } /** * 下载按钮 * @param view */ public void download(View view) { String path = "http://192.168.1.199:8080/14_Web/" + pathEditText.getText().toString(); createDownload(path); } /** * 动态生成新View * 初始化表单数据 * @param path */ private void createDownload(String path) { //获取系统服务LayoutInflater,用来生成新的View LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); LinearLayout linearLayout = (LinearLayout) inflater.inflate(R.layout.download, null); LinearLayout childLinearLayout = (LinearLayout) linearLayout.getChildAt(0); ProgressBar progressBar = (ProgressBar) childLinearLayout.getChildAt(0); TextView textView = (TextView) childLinearLayout.getChildAt(1); Button button = (Button) linearLayout.getChildAt(1); try { button.setOnClickListener(new MyListener(progressBar, textView, path)); //调用当前页面中某个容器的addView,将新创建的View添加进来 rootLinearLayout.addView(linearLayout); } catch (Exception e) { e.printStackTrace(); } } private final class MyListener implements OnClickListener { private ProgressBar progressBar; private TextView textView; private int fileLen; private Downloader downloader; private String name; /** * 执行下载 * @param progressBar //进度条 * @param textView //百分比 * @param path //下载文件路径 */ public MyListener(ProgressBar progressBar, TextView textView, String path) { this.progressBar = progressBar; this.textView = textView; name = path.substring(path.lastIndexOf("/") + 1); downloader = new Downloader(getApplicationContext(), handler); try { downloader.download(path, 3); } catch (Exception e) { e.printStackTrace(); Toast.makeText(getApplicationContext(), "下载过程中出现异常", 0).show(); throw new RuntimeException(e); } } //Handler传输数据 private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case 0: //获取文件的大小 fileLen = msg.getData().getInt("fileLen"); //设置进度条最大刻度:setMax() progressBar.setMax(fileLen); break; case 1: //获取当前下载的总量 int done = msg.getData().getInt("done"); //当前进度的百分比 textView.setText(name + "\t" + done * 100 / fileLen + "%"); //进度条设置当前进度:setProgress() progressBar.setProgress(done); if (done == fileLen) { Toast.makeText(getApplicationContext(), name + " 下载完成", 0).show(); //下载完成后退出进度条 rootLinearLayout.removeView((View) progressBar.getParent().getParent()); } break; } } }; /** * 暂停和继续下载 */ public void onClick(View v) { Button pauseButton = (Button) v; if ("||".equals(pauseButton.getText())) { downloader.pause(); pauseButton.setText(""); } else { downloader.resume(); pauseButton.setText("||"); } } } }

Downloader.java

public class Downloader { private int done; private InfoDao dao; private int fileLen; private Handler handler; private boolean isPause; public Downloader(Context context, Handler handler) { dao = new InfoDao(context); this.handler = handler; } /** * 多线程下载 * @param path 下载路径 * @param thCount 需要开启多少个线程 * @throws Exception */ public void download(String path, int thCount) throws Exception { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //设置超时时间 conn.setConnectTimeout(3000); if (conn.getResponseCode() == 200) { fileLen = conn.getContentLength(); String name = path.substring(path.lastIndexOf("/") + 1); File file = new File(Environment.getExternalStorageDirectory(), name); RandomAccessFile raf = new RandomAccessFile(file, "rws"); raf.setLength(fileLen); raf.close(); //Handler发送消息,主线程接收消息,获取数据的长度 Message msg = new Message(); msg.what = 0; msg.getData().putInt("fileLen", fileLen); handler.sendMessage(msg); //计算每个线程下载的字节数 int partLen = (fileLen + thCount - 1) / thCount; for (int i = 0; i < thCount; i++) new DownloadThread(url, file, partLen, i).start(); } else { throw new IllegalArgumentException("404 path: " + path); } } private final class DownloadThread extends Thread { private URL url; private File file; private int partLen; private int id; public DownloadThread(URL url, File file, int partLen, int id) { this.url = url; this.file = file; this.partLen = partLen; this.id = id; } /** * 写入操作 */ public void run() { // 判断上次是否有未完成任务 Info info = dao.query(url.toString(), id); if (info != null) { // 如果有, 读取当前线程已下载量 done += info.getDone(); } else { // 如果没有, 则创建一个新记录存入 info = new Info(url.toString(), id, 0); dao.insert(info); } int start = id * partLen + info.getDone(); // 开始位置 += 已下载量 int end = (id + 1) * partLen - 1; try { HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(3000); //获取指定位置的数据,Range范围如果超出服务器上数据范围, 会以服务器数据末尾为准 conn.setRequestProperty("Range", "bytes=" + start + "-" + end); RandomAccessFile raf = new RandomAccessFile(file, "rws"); raf.seek(start); //开始读写数据 InputStream in = conn.getInputStream(); byte[] buf = new byte[1024 * 10]; int len; while ((len = in.read(buf)) != -1) { if (isPause) { //使用线程锁锁定该线程 synchronized (dao) { try { dao.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } raf.write(buf, 0, len); done += len; info.setDone(info.getDone() + len); // 记录每个线程已下载的数据量 dao.update(info); //新线程中用Handler发送消息,主线程接收消息 Message msg = new Message(); msg.what = 1; msg.getData().putInt("done", done); handler.sendMessage(msg); } in.close(); raf.close(); // 删除下载记录 dao.deleteAll(info.getPath(), fileLen); } catch (IOException e) { e.printStackTrace(); } } } //暂停下载 public void pause() { isPause = true; } //继续下载 public void resume() { isPause = false; //恢复所有线程 synchronized (dao) { dao.notifyAll(); } } }

Dao:

DBOpenHelper:

public class DBOpenHelper extends SQLiteOpenHelper { public DBOpenHelper(Context context) { super(context, "download.db", null, 1); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE info(path VARCHAR(1024), thid INTEGER, done INTEGER, PRIMARY KEY(path, thid))"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }

InfoDao:

public class InfoDao { private DBOpenHelper helper; public InfoDao(Context context) { helper = new DBOpenHelper(context); } public void insert(Info info) { SQLiteDatabase db = helper.getWritableDatabase(); db.execSQL("INSERT INTO info(path, thid, done) VALUES(?, ?, ?)", new Object[] { info.getPath(), info.getThid(), info.getDone() }); } public void delete(String path, int thid) { SQLiteDatabase db = helper.getWritableDatabase(); db.execSQL("DELETE FROM info WHERE path=? AND thid=?", new Object[] { path, thid }); } public void update(Info info) { SQLiteDatabase db = helper.getWritableDatabase(); db.execSQL("UPDATE info SET done=? WHERE path=? AND thid=?", new Object[] { info.getDone(), info.getPath(), info.getThid() }); } public Info query(String path, int thid) { SQLiteDatabase db = helper.getWritableDatabase(); Cursor c = db.rawQuery("SELECT path, thid, done FROM info WHERE path=? AND thid=?", new String[] { path, String.valueOf(thid) }); Info info = null; if (c.moveToNext()) info = new Info(c.getString(0), c.getInt(1), c.getInt(2)); c.close(); return info; } public void deleteAll(String path, int len) { SQLiteDatabase db = helper.getWritableDatabase(); Cursor c = db.rawQuery("SELECT SUM(done) FROM info WHERE path=?", new String[] { path }); if (c.moveToNext()) { int result = c.getInt(0); if (result == len) db.execSQL("DELETE FROM info WHERE path=? ", new Object[] { path }); } } public List<String> queryUndone() { SQLiteDatabase db = helper.getWritableDatabase(); Cursor c = db.rawQuery("SELECT DISTINCT path FROM info", null); List<String> pathList = new ArrayList<String>(); while (c.moveToNext()) pathList.add(c.getString(0)); c.close(); return pathList; } }

希望本文所述对大家Android程序设计有所帮助。

时间: 2024-08-22 14:21:15

Android编程开发实现多线程断点续传下载器实例的相关文章

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

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

Android编程开发实现带进度条和百分比的多线程下载

本文实例讲述了Android编程开发实现带进度条和百分比的多线程下载.分享给大家供大家参考,具体如下: 继上一篇<java多线程下载实例详解>之后,可以将它移植到我们的安卓中来,下面是具体实现源码: DownActivity.java: package com.example.downloads; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.net.H

android 多线程断点续传下载 三

今天跟大家一起分享下android开发中比较难的一个环节,可能很多人看到这个标题就会感觉头很大,的确如果没有良好的编码能力和逻辑思维,这块是很难搞明白的,前面2次总结中已经为大家分享过有关技术的一些基本要领,我们先一起简单回顾下它的基本原理. http://blog.csdn.net/shimiso/article/details/6763664  android 多线程断点续传下载 一 http://blog.csdn.net/shimiso/article/details/6763986 

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

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

我的Android进阶之旅------&amp;gt;Android基于HTTP协议的多线程断点下载器的实现

一.首先写这篇文章之前,要了解实现该Android多线程断点下载器的几个知识点  1.多线程下载的原理,如下图所示 注意:由于Android移动设备和PC机的处理器还是不能相比,所以开辟的子线程建议不要多于5条.当然现在某些高端机子的处理器能力比较强了,就可以多开辟几条子线程. 2.为了实现断点下载,采用数据库方式记录下载的进度,这样当你将该应用退出后,下次点击下载的时候,程序会去查看该下载链接是否存在下载记录,如果存在下载记录就会判断下载的进度,如何从上次下载的进度继续开始下载. 3.特别注意

android 多线程断点续传下载 二

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

Android编程开发音乐播放器实例_Android

本文实例讲述了Android编程开发音乐播放器,分享给大家供大家参考,具体如下: 音乐播放器中综合了以下内容: SeekBar.ListView.广播接收者(以代码的形式注册Receiver).系统服务.MediaPlayer 实现的功能: 1.暂停/播放.下一首/上一首,点击某一首时播放 2.支持拖动进度条快进 3.列表排序 4.来电话时,停止播放,挂断后继续播放 5.可在后台播放 效果图: 界面: main.xml: <?xml version="1.0" encoding=

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编程开发音乐播放器,分享给大家供大家参考,具体如下: 音乐播放器中综合了以下内容: SeekBar.ListView.广播接收者(以代码的形式注册Receiver).系统服务.MediaPlayer 实现的功能: 1.暂停/播放.下一首/上一首,点击某一首时播放 2.支持拖动进度条快进 3.列表排序 4.来电话时,停止播放,挂断后继续播放 5.可在后台播放 效果图: 界面: main.xml: <?xml version="1.0" encoding=