效果图
效果图.gif
断点下载的过程
过程图.png
这里是简单的一个下载,下一篇介绍列表的断点下载
1.绘制UI图,并初始化控件
2.创建实体类:文件类、线程类
文件类:
public class FileInfo implements Serializable {
private int id;
private String url;
private String flieName;
private int length;
private int finished;
public FileInfo() {
}
public FileInfo(int id, String url, String flieName, int length, int finished) {
this.id = id;
this.url = url;
this.flieName = flieName;
this.length = length;
this.finished = finished;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getFlieName() {
return flieName;
}
public void setFlieName(String flieName) {
this.flieName = flieName;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int getFinished() {
return finished;
}
public void setFinished(int finished) {
this.finished = finished;
}
@Override
public String toString() {
return "FileInfo{" +
"id=" + id +
", url='" + url + '\'' +
", flieName='" + flieName + '\'' +
", length=" + length +
", finished=" + finished +
'}';
}
}
线程类
public class ThreadInfo {
private int id;
private String url;
private int start;
private int end;
private int finished;
@Override
public String toString() {
return "ThreadInfo{" +
"id=" + id +
", url='" + url + '\'' +
", start=" + start +
", end=" + end +
", finished=" + finished +
'}';
}
public ThreadInfo(int id, String url, int start, int end, int finished) {
this.id = id;
this.url = url;
this.start = start;
this.end = end;
this.finished = finished;
}
public ThreadInfo() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
public int getFinished() {
return finished;
}
public void setFinished(int finished) {
this.finished = finished;
}
}
DownloadService(记得在AndroidManifest.xml文件中注册)
public class DownloadService extends Service {
//下载的路径
public static final String DOWNLOAD_PATH =
Environment.getExternalStorageDirectory().getAbsolutePath() + "/download/";
//开始下载
public static final String ACTION_START = "ACTION_START";
//暂停下载
public static final String ACTION_STOP = "ACTION_STOP";
//更新下载
public static final String ACTION_UPDATE_PROGRESS = "ACTION_UPDATE";
//获取下载信息
public static final String FILE_INFO = "fileInfo";
public static final int MSG_INIT = 0;
//下载任务
private DownloadTask task = null;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (ACTION_START.equals(intent.getAction())) {
FileInfo fileInfo = (FileInfo) intent.getSerializableExtra(FILE_INFO);
Log.e("test", "start:" + fileInfo.toString());
//启动线程--获取文件长度
new InitThread(fileInfo).start();
} else if (ACTION_STOP.equals(intent.getAction())) {
FileInfo fileInfo = (FileInfo) intent.getSerializableExtra(FILE_INFO);
Log.e("test", "stop:" + fileInfo.toString());
//点击暂停下载
if (task != null) {
task.isPause = true;
}
}
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
//初始化信息完毕,开始下载
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MSG_INIT:
FileInfo fileInfo = (FileInfo) msg.obj;
Log.e("test", "Thread:" + fileInfo.toString());
task = new DownloadTask(DownloadService.this, fileInfo);
task.download();
break;
}
}
};
//获取文件长度
class InitThread extends Thread {
private FileInfo fileInfo;
public InitThread(FileInfo fileInfo) {
this.fileInfo = fileInfo;
}
@Override
public void run() {
HttpURLConnection conn = null;
RandomAccessFile raf = null;
try {
//链接网络文件,
URL url = new URL(fileInfo.getUrl());
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(4000);
conn.setRequestMethod("GET");
int length = -1;
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
//获得文件长度
length = conn.getContentLength();
}
if (length <= 0) {
return;
}
File dir = new File(DOWNLOAD_PATH);
if (!dir.exists()) {
dir.mkdir();
}
//在本地创建文件
File file = new File(dir, fileInfo.getFlieName());
raf = new RandomAccessFile(file, "rwd");
//设置文件长度
raf.setLength(length);
fileInfo.setLength(length);
handler.obtainMessage(MSG_INIT, fileInfo).sendToTarget();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.disconnect();
}
if (raf != null) {
raf.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
暂停下载的时候需要将进度保存下来,需要创建一张表,并对这个表进行操作
表的相关信息
public class ThreadTable {
public static final String TABLE_NAME = "thread_table";
public static final String COL_ID = "_id";
public static final String COL_THREAD_ID = "thread_id";
public static final String COL_URL = "url";
public static final String COL_START = "start";
public static final String COL_END = "end";
public static final String COL_FINISHED = "finished";
public static final String CREATE_TABLE = "create table "
+ TABLE_NAME + "("
+ COL_ID + " integer primary key autoincrement,"
+ COL_THREAD_ID + " integer,"
+ COL_URL + " text,"
+ COL_START + " integer,"
+ COL_END + " integer,"
+ COL_FINISHED + " integer);";
public static final String DROP_TABLE = "drop table if exists " + TABLE_NAME;
}
DBHelp
public class DBHelp extends SQLiteOpenHelper {
public DBHelp(Context context) {
super(context, "app_manager", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(ThreadTable.CREATE_TABLE);
Log.e("sql", "table==" + ThreadTable.CREATE_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// db.execSQL(ThreadTable.DROP_TABLE);
// db.execSQL(ThreadTable.CREATE_TABLE);
}
}
ThreadDao
public interface ThreadDao {
/**
* 插入线程
*/
void insertThread(ThreadInfo threadInfo);
/**
* 删除线程
*
* @param url
* @param thread_id
*/
void deleteThread(String url, int thread_id);
/**
* 更新线程进度
*
* @param url
* @param thread_id
* @param finished
*/
void updateThread(String url, int thread_id, int finished);
/**
* 查询文件的线程信息
*
* @param url
* @return
*/
List<ThreadInfo> getThreads(String url);
/**
* 判断线程信息是否存在
*
* @param url
* @param thread_id
* @return
*/
boolean isExists(String url, int thread_id);
}
实现类
public class ThreadDaoImpl implements ThreadDao {
private DBHelp dbHelp = null;
public ThreadDaoImpl(Context context) {
dbHelp = new DBHelp(context);
}
@Override
public void insertThread(ThreadInfo threadInfo) {
SQLiteDatabase db = dbHelp.getReadableDatabase();
String sql = "insert into " + ThreadTable.TABLE_NAME +
"(" + ThreadTable.COL_THREAD_ID + "," + ThreadTable.COL_URL + "," + ThreadTable.COL_START + "," + ThreadTable.COL_END + "," + ThreadTable.COL_FINISHED
+ ") values(?,?,?,?,?)";
Log.e("sql","insert=="+sql);
db.execSQL(sql, new Object[]{threadInfo.getId(), threadInfo.getUrl(), threadInfo.getStart(), threadInfo.getEnd(), threadInfo.getFinished()});
db.close();
}
@Override
public void deleteThread(String url, int thread_id) {
SQLiteDatabase db = dbHelp.getReadableDatabase();
String sql = "delete from " + ThreadTable.TABLE_NAME + " where " + ThreadTable.COL_URL + " = ? and " + ThreadTable.COL_THREAD_ID + " = ?";
db.execSQL(sql, new Object[]{url, thread_id});
db.close();
}
@Override
public void updateThread(String url, int thread_id, int finished) {
SQLiteDatabase db = dbHelp.getReadableDatabase();
String sql = "update " + ThreadTable.TABLE_NAME + " set " + ThreadTable.COL_FINISHED + " = ? where " + ThreadTable.COL_URL + " = ? and " + ThreadTable.COL_THREAD_ID + " = ?";
db.execSQL(sql, new Object[]{finished, url, thread_id});
db.close();
}
@Override
public List<ThreadInfo> getThreads(String url) {
SQLiteDatabase db = dbHelp.getReadableDatabase();
List<ThreadInfo> threadInfoList = new ArrayList<>();
String sql = "select * from " + ThreadTable.TABLE_NAME + " where " + ThreadTable.COL_URL + " = ?";
Cursor cursor = db.rawQuery(sql, new String[]{url});
while (cursor.moveToNext()) {
ThreadInfo info = new ThreadInfo();
info.setId(cursor.getInt(cursor.getColumnIndex(ThreadTable.COL_THREAD_ID)));
info.setUrl(cursor.getString(cursor.getColumnIndex(ThreadTable.COL_URL)));
info.setStart(cursor.getInt(cursor.getColumnIndex(ThreadTable.COL_START)));
info.setEnd(cursor.getInt(cursor.getColumnIndex(ThreadTable.COL_END)));
info.setFinished(cursor.getInt(cursor.getColumnIndex(ThreadTable.COL_FINISHED)));
threadInfoList.add(info);
}
db.close();
return threadInfoList;
}
@Override
public boolean isExists(String url, int thread_id) {
SQLiteDatabase db = dbHelp.getReadableDatabase();
String sql = "select * from " + ThreadTable.TABLE_NAME + " where " + ThreadTable.COL_URL + " = ? and " + ThreadTable.COL_THREAD_ID + " = ?";
Cursor cursor = db.rawQuery(sql, new String[]{url, thread_id + ""});
boolean isExist = cursor.moveToNext();
return isExist;
}
}
下载的具体实现
public class DownloadTask {
private Context context;
private FileInfo fileInfo;
private ThreadDao mDao;
public int mFinished = 0;
public static final String FINISHED = "finished";
public boolean isPause = false;
public DownloadTask(Context context, FileInfo fileInfo) {
this.context = context;
this.fileInfo = fileInfo;
mDao = new ThreadDaoImpl(context);
}
public void download() {
//读取数据库的线程信息
List<ThreadInfo> threads = mDao.getThreads(fileInfo.getUrl());
ThreadInfo threadInfo = null;
if (threads == null || threads.size() == 0) {
threadInfo = new ThreadInfo(0, fileInfo.getUrl(), 0, fileInfo.getLength(), 0);
} else {
threadInfo = threads.get(0);
}
//创建子线程下载
new DownLoadThread(threadInfo).start();
}
class DownLoadThread extends Thread {
private ThreadInfo threadInfo;
public DownLoadThread(ThreadInfo threadInfo) {
this.threadInfo = threadInfo;
}
@Override
public void run() {
//向数据库插入线程信息
if (!mDao.isExists(threadInfo.getUrl(), threadInfo.getId())) {
mDao.insertThread(threadInfo);
}
HttpURLConnection conn = null;
RandomAccessFile raf = null;
InputStream inputStream = null;
try {
URL url = new URL(threadInfo.getUrl());
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(4000);
conn.setRequestMethod("GET");
//设置下载位置
int start = threadInfo.getStart() + threadInfo.getFinished();
conn.setRequestProperty("Range", "bytes=" + start + "-" + threadInfo.getEnd());
//设置文件写入位置
File file = new File(DownloadService.DOWNLOAD_PATH, fileInfo.getFlieName());
raf = new RandomAccessFile(file, "rwd");
raf.seek(start);
mFinished += threadInfo.getFinished();
Intent intent = new Intent(DownloadService.ACTION_UPDATE_PROGRESS);
//开始下载
if (conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL) {
//读取数据
inputStream = conn.getInputStream();
byte[] bytes = new byte[1024 * 4];
int len = -1;
long time = System.currentTimeMillis();
while ((len = inputStream.read()) != -1) {
//写入文件
raf.write(bytes, 0, len);
//把下载进度发送广播给activity
mFinished += len;
if (System.currentTimeMillis() - time > 500) {
intent.putExtra(FINISHED, mFinished * 100 / fileInfo.getLength());
context.sendBroadcast(intent);
}
//在下载暂停时,保存下载进度
if (isPause) {
mDao.updateThread(threadInfo.getUrl(), threadInfo.getId(), mFinished);
return;
}
}
//删除线程信息
mDao.deleteThread(threadInfo.getUrl(), threadInfo.getId());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (raf != null) {
raf.close();
}
if (conn != null) {
conn.disconnect();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Activity接收广播,并更新数据
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(DownloadService.ACTION_UPDATE_PROGRESS)) {
int currentProgress = intent.getIntExtra(DownloadTask.FINISHED, 0);
progressBar.setProgress(currentProgress);
tvProgress.setText(currentProgress + "%");
}
}
};
注册广播
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(DownloadService.ACTION_UPDATE_PROGRESS);
registerReceiver(receiver, intentFilter);
取消注册
@Override
protected void onDestroy() {
if (receiver != null) {
unregisterReceiver(receiver);
}
super.onDestroy();
}
项目下载地址:
https://download.csdn.net/download/diudiu666hf/10701030
网友评论