断点续传和多线程下载(一)

最近在学习Android中的 断点续传 和 多线程下载 这方面的知识,一边从公司中的项目开始学习,一边也从网上查阅这方面的知识,然后发现公司中的项目代码关于这块都封装的比较好,比较大,不太容易看懂,所以准备参照网上的Demo,自己仿照着写一个。

首先我觉得学习一个比较复杂的知识点,不应当在一个demo里把这个所有复杂的知识点全都实现出来,这样对于初学者来说比较困难,从而不能完全的掌握知识点,所以我觉得应该先对这个知识点有个整体的把握,然后把这些东西给分成一个模块一个模块的,然后把这些局部的知识一个一个实现出来,这是比较简单的,最后将这些局部的知识再组装成一个整体的功能,这样一个比较复杂的功能就比较好实习出来了,就拿断点续床和多线程下载来说,一般涉及到下载的项目,都会用到这部分的知识,所以按照我的思路就是,如果之前没有接触过这块,那么先做一个简单的下载功能,能够下载,然后在UI界面上进行进度更新,然后在加上断点续传的功能,这块没问题了之后加上多线程下载。具体里面涉及到哪些东西和别的知识点,在写的过程中,去查阅。

我分以下几步来慢慢实现这个功能:

1、简单的下载一个APK,实现断点续传,能够开始和暂停

2、加上多线程下载功能,将一个下载的内容分给几个线程来下载

3、加入ListView,显示多个应用,点击下载,然后切换到下载界面,进行进度更新


这篇我先做一第一部分,简单实现一个断点续传功能。关于断点续传,其实就是在用HttpURLConnection得到输入流,一点一点往文件里写入数据的时候,把这个完成度给记录下来,放在数据库中,下次再下载的时候,先从数据库中得到之前下载的完成度,然后根据这个完成度,在设置HttpURLConnection的请求头里的属性时,从得到的断点处开始请求数据到文件的长度,然后再去从输入流中读取数据到文件里,这样就基本实现了断点续传的功能了。

下面贴一下部分代码:

布局很简单,一个按钮和进度条



main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.shulf.downloadtest.MainActivity" >

    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="24dp"
        android:layout_marginTop="18dp"
        android:text="新浪微博"
        android:textSize="20sp" />

    <Button
        android:id="@+id/startBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignTop="@+id/name"
        android:text="开始" />

    <TextView
        android:id="@+id/progressText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/name"
        android:layout_below="@+id/startBtn" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/progressText"
        android:layout_marginTop="15dp" />

</RelativeLayout>

MainActivity.java

package com.shulf.downloadtest;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity implements OnClickListener {

	public final static String URL = "http://gdown.baidu.com/data/wisegame/f58a940533df3890/weibo_1477.apk";
	private Button startBtn;
	private TextView progressText;
	private ProgressBar progressBar;
	private MyHander handler;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		startBtn = (Button) findViewById(R.id.startBtn);
		startBtn.setOnClickListener(this);

		progressText = (TextView) findViewById(R.id.progressText);
		progressBar = (ProgressBar) findViewById(R.id.progressBar);

		handler = new MyHander(progressText, progressBar);
	}

	@Override
	public void onClick(View view) {

		if (startBtn.getText().equals("开始") || startBtn.getText().equals("继续")) {
			startBtn.setText("暂停");
			download();
		} else {
			startBtn.setText("继续");
			pause();
		}
	}

	public void download() {
		DownloadAsyncTask task = new DownloadAsyncTask(
				this.getApplicationContext(), handler);
		task.execute(URL);
	}

	public void pause() {
		DownloadTask task = DownloadManager.getInstance(
				this.getApplicationContext()).getTask(URL);
		task.STATE = 2;
	}

	/**
	 * 负责接收DownTask下载线程中的传来的消息,更新UI线程中的进度条和下载进度
	 * @author Administrator
	 *
	 */
	
	private class MyHander extends Handler {
		private TextView tv;
		private ProgressBar pb;

		public MyHander(TextView tv, ProgressBar pb) {
			this.tv = tv;
			this.pb = pb;
		}

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			if (msg.what == 1) {
				tv.setText(msg.arg1 * 100 / msg.arg2 + "%");
				pb.setMax(msg.arg2);
				pb.setProgress(msg.arg1);
			}
		}
	}
}

DownloadAsyncTask.java
package com.shulf.downloadtest;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;

/**
 * 下载之前的准备,从DB中得到下载实体,或者创建下载实体
 * @author Administrator
 *
 */

public class DownloadAsyncTask extends AsyncTask<String, Void, DownloadInfo> {

	private Context context;
	private Handler handler;
	private DownloadManager dm;

	public DownloadAsyncTask(Context context, Handler handler) {
		this.handler = handler;
		this.context = context;
		dm = DownloadManager.getInstance(context);
	}

	@Override
	protected DownloadInfo doInBackground(String... args) {
		DownloadInfo info = DownloadDao.getInstance(context).getDownloadInof(
				args[0]);
		if (info == null) {
			int fileSize = 0;
			try {
				URL url = new URL(args[0]);
				HttpURLConnection conn = (HttpURLConnection) url
						.openConnection();
				conn.setReadTimeout(5000);
				conn.setRequestMethod("GET");
				fileSize = conn.getContentLength();

			} catch (MalformedURLException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			info = new DownloadInfo();
			info.completeSize = 0;
			info.fileSize = fileSize;
			info.url = args[0];
			DownloadDao.getInstance(context).saveDownloadInfo(info);

			return info;
		}
		return info;
	}

	@Override
	protected void onPostExecute(DownloadInfo info) {
		super.onPostExecute(info);

		DownloadTask task = new DownloadTask(context, info, handler);
		task.STATE=1;
		task.start();
		return;

	}

}

DownloadTask.java

package com.shulf.downloadtest;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;

import android.content.Context;
import android.os.Handler;
import android.os.Message;

/**
 * 下载线程类,得到下载实体中的信息,开启线程进行下载
 * @author Administrator
 *
 */

public class DownloadTask extends Thread {

	private String urlStr;
	private Handler handler;
	private int fileSize;
	private Context context;
	private DownloadInfo info;
	public static int STATE = 1;// 1表示未下载状态,2表示暂停状态

	public DownloadTask(Context context, DownloadInfo info, Handler handler) {
		this.info = info;
		this.context = context;
		this.urlStr = info.url;
		this.fileSize = info.fileSize;
		this.handler = handler;
	}

	@Override
	public void run() {
		HttpURLConnection conn = null;
		InputStream in = null;
		RandomAccessFile randomAccessFile = null;
		File saveFile = new File("/mnt/sdcard/", "test.apk");
		int completeSize = info.completeSize;
		try {
			URL url = new URL(urlStr);
			conn = (HttpURLConnection) url.openConnection();
			conn.setConnectTimeout(5000);
			conn.setRequestMethod("GET");
			/**设置请求头的属性,从断点处开始到文件的长度*/
			conn.setRequestProperty("Range", "bytes=" + completeSize + "-"
					+ fileSize);

			randomAccessFile = new RandomAccessFile(saveFile, "rwd");
			randomAccessFile.seek(completeSize);

			in = conn.getInputStream();
			byte[] buffer = new byte[4096];
			int length = -1;
			while ((length = in.read(buffer)) != -1) {
				randomAccessFile.write(buffer, 0, length);
				completeSize += length;
				DownloadDao.getInstance(context).updateComplete(urlStr,
						completeSize);
				Message msg = Message.obtain();
				msg.what = 1;
				msg.arg1 = completeSize;
				msg.arg2 = fileSize;
				handler.sendMessage(msg);
				if (STATE == 2) {
					return;
				}

			}
		} catch (ProtocolException e) {
			e.printStackTrace();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try{
				if (in != null) {
					in.close();
				}
				if (randomAccessFile != null) {
					randomAccessFile.close();
				}
			}catch(Exception e){
				e.printStackTrace();
			}

		}
	}

}

DownloadDao.java

package com.shulf.downloadtest;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

/**
 * 数据访问类
 * @author Administrator
 *
 */

public class DownloadDao {
	private static DownloadDao dao;
	private Context context;

	private DownloadDao(Context context) {
		this.context = context;
	}

	public static DownloadDao getInstance(Context context) {
		if (dao == null) {
			dao = new DownloadDao(context);
		}
		return dao;
	}

	public SQLiteDatabase getConnection() {
		SQLiteDatabase db = null;
		db = new DBHelper(context).getReadableDatabase();
		return db;
	}

	/**
	 * 得到下载的完成进度
	 * @param url
	 * @return
	 */
	public synchronized int getComplete(String url) {
		int complete = 0;
		SQLiteDatabase db = getConnection();
		String sql = "select compele_size from download_info where url = ?";
		Cursor cursor = null;
		try {
			cursor = db.rawQuery(sql, new String[] { url });
			while (cursor.moveToNext()) {
				complete = cursor.getInt(0);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (db != null) {
				db.close();
			}
			if (cursor != null) {
				cursor.close();
			}
		}
		return complete;
	}

	/**
	 *更新表中的下载进度
	 * @param url
	 * @param complete
	 */
	public synchronized void updateComplete(String url, int complete) {
		SQLiteDatabase db = getConnection();
		try {
			String sql = "update download_info set compelete_size = ? where url = ?";
			Object[] bindArgs = { complete, url };
			db.execSQL(sql, bindArgs);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (db != null) {
				db.close();
			}
		}
	}

	/**
	 * 从数据库中得到下载实体信息,然后转换成实体返回
	 * @param url
	 * @return
	 */
	public synchronized DownloadInfo getDownloadInof(String url) {
		SQLiteDatabase db = getConnection();
		Cursor cursor = null;
		DownloadInfo info = null;
		String sql = "select * from download_info where url = ?";
		try {
			cursor = db.rawQuery(sql, new String[] { url });
			while (cursor.moveToNext()) {
				info = new DownloadInfo();
				info.completeSize = cursor.getInt(5);
				info.fileSize = cursor.getInt(6);
				info.url = cursor.getString(2);
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (db != null) {
				db.close();
			}
			if (cursor != null) {
				cursor.close();
			}
		}
		return info;
	}

	/**
	 * 保存下载实体的信息
	 * @param info
	 */
	public synchronized void saveDownloadInfo(DownloadInfo info) {
		SQLiteDatabase db = getConnection();
		try {
			String sql = "insert into download_info(thread_id,url,start_pos,end_pos,compelete_size,file_size) values (?,?,?,?,?,?)";
			Object[] bindArgs = { 0, info.url, 0, 0, info.completeSize,
					info.fileSize };
			db.execSQL(sql, bindArgs);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (db != null) {
				db.close();
			}
		}
	}

}

DownloadInfo.java

package com.shulf.downloadtest;

/**
 * 下载内容的实体
 * @author Administrator
 *
 */

public class DownloadInfo {

	public int fileSize;

	public int completeSize;

	public String url;
	
	public DownloadInfo() {
	}

	public DownloadInfo(int fileSize, int completeSize, String url) {
		super();
		this.fileSize = fileSize;
		this.completeSize = completeSize;
		this.url = url;
	}

	public int getFileSize() {
		return fileSize;
	}

	public void setFileSize(int fileSize) {
		this.fileSize = fileSize;
	}

	public int getCompleteSize() {
		return completeSize;
	}

	public void setCompleteSize(int completeSize) {
		this.completeSize = completeSize;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	@Override
	public String toString() {
		return "DownloadInfo [fileSize=" + fileSize + ", completeSize="
				+ completeSize + ", url=" + url + "]";
	}

}

DBHelper.java

package com.shulf.downloadtest;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DBHelper extends SQLiteOpenHelper {

	public DBHelper(Context context) {
		super(context, "download.db", null, 1);
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL("create table download_info (_id integer PRIMARY KEY AUTOINCREMENT, thread_id integer,"
				+ "url char,start_pos integer , end_pos integer, compelete_size integer,file_size integer)");
	}

	@Override
	public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {

	}

}

这部分简单的实现了断线续传的功能,能够下载暂停了,实现的比较粗糙,很多细节的东西都还没有注意

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值