多文件多线程断点续传项目练习总结
来源:互联网 发布:央视评论网络直播乱象 编辑:程序博客网 时间:2024/05/16 07:59
一、练习项目概述
此项目实现多线程下载多个文件,涉及到的知识点有ListView的使用,通知的使用,数据存储,网络连接,数据库的读写,Service的使用,广播的使用,多线程,handler等的使用。
项目流程中数据存储与传递图如下:
2、过程说明
过程1:Activity将保存文件的List数据传递给适配器显示在界面上。
过程2:用户点击下载,将下载文件的数据传递给DownLoadService。
过程3:DownLoadService将收到的指令的文件信息传递给DownLoadTask。
过程4:将文件下载的进度通过广播的发送给Activity,更新UI。
过程5:当用户点击停止时,修改对应下载任务中的所有下载线程读写控制符,并将线程信息保存到数据库。
过程6:当用户点击开始下载时,如果当前下载任务不存在时,读取数据库不存在线程信息时,初始化下载线程,并保存到下载任务和数据库中。
3、编码逻辑
1、界面与Bean类
2、DownloadService类
3、InitThread类
4、DBhelper类
5、Dao接口
6、DaoImpl类
7、DownLoadTask类
8、DownLoadThread类
9、完善Activity类
10、实现通知
11、测试
4、代码
MianActivity类
package app.kuxiao.com.myapplication;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.os.Bundle;import android.support.design.widget.FloatingActionButton;import android.support.design.widget.Snackbar;import android.support.v7.app.AppCompatActivity;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.Button;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.TextView;import android.widget.Toast;import java.io.File;import java.util.ArrayList;import java.util.LinkedHashMap;import java.util.List;import java.util.Map;import adapter.FileAdapter;import bean.FileInfo;import service.DownloadService;public class MainActivity extends AppCompatActivity { private ListView mListView = null; private List<FileInfo> fileInfos =null; private FileAdapter fileAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } private void init() { mListView = (ListView) findViewById(R.id.id_lv_file); fileInfos = new ArrayList<>(); FileInfo fileInfo = new FileInfo(0,0,0,0,"https://qd.myapp.com/myapp/qqteam/AndroidQQ/mobileqq_android.apk",0,"mobileqq_android.apk",3); FileInfo fileInfo1 = new FileInfo(1,0,0,0,"https://itunes.apple.com/cn/app/qq-2011/id444934666?mt=8",0,"qq-2011",3); FileInfo fileInfo2 = new FileInfo(2,0,0,0,"http://dlglobal.qq.com/weixin/Windows/WeChat_C1018.exe",0,"WeChat_C1018.exe",3); FileInfo fileInfo3 = new FileInfo(3,0,0,0,"http://www.imooc.com/mobile/mukewang.apk",0,"mukewang.apk",3); fileInfos.add(fileInfo); fileInfos.add(fileInfo1); fileInfos.add(fileInfo2); fileInfos.add(fileInfo3); IntentFilter intentFilter = new IntentFilter(DownloadService.DOWNLOAD_UPDATE); intentFilter.addAction(DownloadService.DOWNLOAD_FINISHED); registerReceiver(receiver, intentFilter); fileAdapter = new FileAdapter(fileInfos,this); mListView.setAdapter(fileAdapter); } BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(DownloadService.DOWNLOAD_UPDATE)) { int taskKey = intent.getIntExtra("taskKey",4); int finised = (int)intent.getLongExtra("finished",0); fileAdapter.updateProgress(taskKey,finised); }else if(intent.getAction().equals(DownloadService.DOWNLOAD_FINISHED)) { int key = intent.getIntExtra("taskKey",4); FileInfo info = fileInfos.get(key); Intent intent1 = new Intent(context, DownloadService.class); intent1.setAction(DownloadService.DOWNLOAD_FINISHED); intent1.putExtra("taskKey",key); startService(intent1); Toast.makeText(MainActivity.this,info.getFileName() + "下載完成...",Toast.LENGTH_SHORT).show(); } } }; @Override protected void onDestroy() { unregisterReceiver(receiver); super.onDestroy(); }}
2、Bean类
1、FileInfo
package bean;import java.io.Serializable;public class FileInfo implements Serializable{ private String url; private int start; private int end; private int id; private int finished; private int length; private String fileName; private int threadcont; public FileInfo(int id,int start, int end, int finished, String url, int length,String fileName,int thread_cont) { this.id = id; this.end = end; this.start = start; this.finished = finished; this.url = url; this.length = length; this.fileName = fileName; this.threadcont = thread_cont; } public int getId() { return id; } public void setId(int id) { this.id = id; } public FileInfo() { } public int getThreadcont() { return threadcont; } public void setThreadcont(int threadcont) { this.threadcont = threadcont; } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } 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; } public int getLength() { return length; } public void setLength(int length) { this.length = length; } @Override public String toString() { return "FileInfo{" + "url='" + url + '\'' + ", start=" + start + ", end=" + end + ", finished=" + finished + ", length=" + length + '}'; }}
2、ThreadInfo类
package bean;public class ThreadInfo { private int start; private int end; private String url; private int finished; private int id; public ThreadInfo() { } public ThreadInfo(int start, String url, int end, int finished, int id) { this.start = start; this.url = url; this.end = end; this.finished = finished; this.id = id; } 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 String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public int getFinished() { return finished; } public void setFinished(int finished) { this.finished = finished; } public int getId() { return id; } public void setId(int id) { this.id = id; } @Override public String toString() { return "ThreadInfo{" + "start=" + start + ", end=" + end + ", url='" + url + '\'' + ", finished=" + finished + ", id=" + id + '}'; }}
3、数据库
1、DBhelper类
package db;import android.content.Context;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;public class DBhelper extends SQLiteOpenHelper { private static DBhelper mDBhelper = null; private static final String DB_NAME = "download.db"; private static final int DB_VERSION = 1; private static final String DB_CREATE = "create table thread_info(_id integer primary key autoincrement," + FileDao.Thread_id + " integer," + FileDao.Thread_url + " text," + FileDao.Thread_start + " integer," + FileDao.Thread_end + " integer," + FileDao.Thread_finished + " integer" + ")"; private static final String DB_DROP = "drop table if exists thread_info"; private DBhelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } public static DBhelper getInstance(Context context) { if (mDBhelper == null) { synchronized (new Object()) { mDBhelper = new DBhelper(context); } } return mDBhelper; } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(DB_CREATE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(DB_DROP); db.execSQL(DB_CREATE); }}
2、MyDaoService接口
package db;import java.util.List;public interface MyDaoService<T> { //插入多条 public void insertMore(List<T> params); //插入一条 public void insertOne(T params); //删除一条 public void deleteOne(String url); //查询多条 public List<T> getMore(String url); //查询一条 public T getOne(int parmas); //更新一条 public void updateInfo(String url, int id,int finished);}
3、FileDao类(Dao实现类)
package db;import android.content.ContentValues;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.util.Log;import java.util.ArrayList;import java.util.List;import bean.ThreadInfo;public class FileDao implements MyDaoService<ThreadInfo> { public static final String Thread_id = "thread_id"; public static final String Thread_url = "thread_url"; public static final String Thread_start = "thread_start"; public static final String Thread_end = "thread_end"; public static final String Thread_finished = "thread_finished"; private DBhelper mDBhelper = null; public FileDao(Context context) { this.mDBhelper = DBhelper.getInstance(context); } @Override public void insertMore(List<ThreadInfo> params) { } @Override public synchronized void insertOne(ThreadInfo params) { SQLiteDatabase db = mDBhelper.getWritableDatabase(); ContentValues contentValues = new ContentValues(); contentValues.put(Thread_id, params.getId()); contentValues.put(Thread_url, params.getUrl()); contentValues.put(Thread_start, params.getStart()); contentValues.put(Thread_end, params.getEnd()); contentValues.put(Thread_finished, params.getFinished()); db.insert("thread_info", null, contentValues); db.close(); } @Override public synchronized void deleteOne(String thread_url) { SQLiteDatabase db = mDBhelper.getWritableDatabase(); db.delete("thread_info", Thread_url + " = ?", new String[]{thread_url}); db.close(); } @Override public List<ThreadInfo> getMore(String thread_url) { SQLiteDatabase db = mDBhelper.getReadableDatabase(); List<ThreadInfo> list = new ArrayList<>(); Cursor cursor = db.query("thread_info", null, Thread_url + " = ?", new String[]{thread_url}, null, null, null); while (cursor.moveToNext()) { ThreadInfo threadInfo = new ThreadInfo(); threadInfo.setId(cursor.getInt(cursor.getColumnIndex(Thread_id))); threadInfo.setUrl(cursor.getString(cursor.getColumnIndex(Thread_url))); threadInfo.setStart(cursor.getInt(cursor.getColumnIndex(Thread_start))); threadInfo.setEnd(cursor.getInt(cursor.getColumnIndex(Thread_end))); threadInfo.setFinished(cursor.getInt(cursor.getColumnIndex(Thread_finished))); list.add(threadInfo); } cursor.close(); db.close(); return list; } @Override public ThreadInfo getOne(int parmas) { return null; } @Override public synchronized void updateInfo(String url, int id, int finished) { SQLiteDatabase db = mDBhelper.getWritableDatabase(); /* ContentValues values = new ContentValues(); values.put(Thread_finished,finished); db.update("thread_info", values, Thread_url + "= ? and +" + Thread_id + " = ?", new String[]{url, id + ""});*/ db.execSQL("update thread_info set thread_finished = ? where thread_url = ?and thread_id = ?",new Object[]{finished,url,id}); Log.i("DownloadTask", "数据库更新操作的finished" + finished); db.close(); }}
4、Service类
1、DownLoadService类
package service;import android.app.Service;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.os.Environment;import android.os.Handler;import android.os.IBinder;import android.os.Message;import android.util.Log;import java.io.File;import java.io.IOException;import java.io.RandomAccessFile;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import java.security.PublicKey;import java.util.ArrayList;import java.util.LinkedHashMap;import java.util.List;import java.util.Map;import bean.FileInfo;import bean.ThreadInfo;import db.FileDao;public class DownloadService extends Service { public static final String DOWNLOAD_STOP = "ATION_STOP"; public static final String DOWNLOAD_SART = "ATION_START"; public static final String DOWNLOAD_UPDATE = "ATION_UPDATA"; public static final String DOWNLOAD_PATH = Environment.getExternalStorageDirectory() + "/download/"; public static final int msg_what = 1; public static final String DOWNLOAD_FINISHED = "ATION_FINISHED"; private Map<Integer, DownLoadTask> taskMap = null; @Override public void onCreate() { taskMap = new LinkedHashMap<>(); super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("DownloadService","Intent是否为null" + (intent == null) ); if (null != intent) { if (intent.getAction().equals(DOWNLOAD_SART)) { FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileinfo"); InitThread initThread = null; DownLoadTask downLoadTask = taskMap.get(fileInfo.getId()); if (downLoadTask == null) { initThread = new InitThread(fileInfo); DownLoadTask.mExecutorService.execute(initThread); } else if (downLoadTask.getDownloadThreads().get(0).getIsPause()) { List<DownLoadTask.DownloadThread> downloadThreads = downLoadTask.getDownloadThreads(); for (DownLoadTask.DownloadThread thread : downloadThreads) { thread.setIsPause(false); } taskMap.get(fileInfo.getId()).download(); } } else if (intent.getAction().equals(DOWNLOAD_STOP)) { FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileinfo"); if (taskMap.get(fileInfo.getId()) != null) { Log.i("DownLoadService", "暂停" + fileInfo.getFileName() + "文件的下载..."); DownLoadTask downLoadTask = taskMap.get(fileInfo.getId()); List<DownLoadTask.DownloadThread> downloadThreads = downLoadTask.getDownloadThreads(); for (DownLoadTask.DownloadThread thread : downloadThreads) { thread.setIsPause(true); } } } else if (intent.getAction().equals(DOWNLOAD_FINISHED)) { int key = intent.getIntExtra("taskKey", 4); Log.i("DownloadService", "第" + key + "个文件下载完成"); taskMap.remove(key); } return super.onStartCommand(intent, flags, startId); } return super.onStartCommand(intent, flags, startId); } Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == msg_what) { FileInfo fileInfo = (FileInfo) msg.obj; DownLoadTask downLoadTask = new DownLoadTask(DownloadService.this, fileInfo); downLoadTask.download(); taskMap.put(fileInfo.getId(), downLoadTask); Log.i("DownLoadTask", "執行到了這裡5"); } } }; /** * 初始化线程类 */ class InitThread extends Thread { private FileInfo mFileInfo; public InitThread(FileInfo fileInfo) { this.mFileInfo = fileInfo; } @Override public void run() { try { URL url = new URL(mFileInfo.getUrl()); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(3000); int length = -1; if (conn.getResponseCode() == 200) { length = conn.getContentLength(); } if (length <= 0) { return; } File dir = new File(DOWNLOAD_PATH); if (!dir.exists()) { dir.mkdirs(); } File file = new File(dir, mFileInfo.getFileName()); RandomAccessFile raf = new RandomAccessFile(file, "rwd"); raf.setLength(length); mFileInfo.setLength(length); mHandler.obtainMessage(msg_what, mFileInfo).sendToTarget(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } @Override public void onDestroy() { taskMap = null; super.onDestroy(); } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. throw new UnsupportedOperationException("Not yet implemented"); }}
2、DownLoadTask类
package service;import android.content.Context;import android.content.Intent;import android.util.Log;import java.io.IOException;import java.io.InputStream;import java.io.RandomAccessFile;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import java.util.ArrayList;import java.util.List;import java.util.concurrent.Executor;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import bean.FileInfo;import bean.ThreadInfo;import db.FileDao;public class DownLoadTask { //访问数据库对象 private FileDao mFileDao = null; //保存所有的线程信息 private List<ThreadInfo> threads = null; //上下文 private Context context; //下载任务的文件信息 private FileInfo mFileInfo = null; //保存下载任务的所有的下载线程对象 private List<DownloadThread> DownloadThreads = null; //线程池对象 public static ExecutorService mExecutorService = Executors.newCachedThreadPool(); public DownLoadTask(Context context, FileInfo fileInfo) { this.context = context; mFileDao = new FileDao(context); this.mFileInfo = fileInfo; threads = new ArrayList<>(); DownloadThreads = new ArrayList<>(); } public List<DownloadThread> getDownloadThreads() { return DownloadThreads; } /** * 检查是否完成任务 */ public synchronized void checkIsFinished() { int finished = 0; for (ThreadInfo info :threads) { finished += info.getFinished(); } if (finished == mFileInfo.getLength()) { Intent intent = new Intent(DownloadService.DOWNLOAD_FINISHED); intent.putExtra("taskKey",mFileInfo.getId()); context.sendBroadcast(intent); mFileDao.deleteOne(mFileInfo.getUrl()); } } /** * 下载方法 */ public void download() { List<ThreadInfo> list = mFileDao.getMore(mFileInfo.getUrl()); Log.i("DownLoadTask", "数据库取出来的线程信息为数据为" + list.toString()); if (list.size() == 0) { //每一个线程下载的长度 int size = mFileInfo.getLength() / mFileInfo.getThreadcont(); for (int i = 0; i < mFileInfo.getThreadcont(); i++) { ThreadInfo threadInfo = new ThreadInfo(i * size, mFileInfo.getUrl(), (i + 1) * size - 1, 0, i); if (i == mFileInfo.getThreadcont() - 1) { threadInfo.setEnd(mFileInfo.getLength()); } threads.add(threadInfo); //将线程信息保存到数据库 mFileDao.insertOne(threadInfo); } }else { threads.clear(); threads.addAll(list); } this.DownloadThreads.clear(); for (ThreadInfo info : threads) { DownloadThread downloadThread = new DownloadThread(info); mExecutorService.execute(downloadThread); this.DownloadThreads.add(downloadThread); } Log.i("DownloadTask" ,"下载线程的数量为" + getDownloadThreads().size()); } /** * 下载线程类 */ class DownloadThread extends Thread { //当前下载线程的信息 private ThreadInfo threadInfo; public DownloadThread(ThreadInfo threadInfo) { this.threadInfo = threadInfo; } //下载线程停止控制符 private boolean isPause = false; public void setIsPause(boolean status) { this.isPause = status; } public boolean getIsPause() { return this.isPause; } @Override public void run() { HttpURLConnection conn = null; InputStream is = null; RandomAccessFile raf = null; try { URL url = new URL(threadInfo.getUrl()); conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(3000); int start = threadInfo.getStart() + threadInfo.getFinished(); conn.setRequestProperty("range", "bytes= " + start + "-" + threadInfo.getEnd()); if (conn.getResponseCode() == 206) { is = conn.getInputStream(); int length = -1; byte[] bytes = new byte[4 * 1024]; long time = System.currentTimeMillis(); raf = new RandomAccessFile(DownloadService.DOWNLOAD_PATH + mFileInfo.getFileName(), "rwd"); raf.seek(start); while ((length = is.read(bytes, 0, bytes.length)) != -1) { raf.write(bytes, 0, length); threadInfo.setFinished(threadInfo.getFinished() + length); if (System.currentTimeMillis() - time > 3000) { Intent intent = new Intent(DownloadService.DOWNLOAD_UPDATE); intent.putExtra("taskKey", mFileInfo.getId()); time = System.currentTimeMillis(); int finishedadd = 0; synchronized (new Object()) { for (ThreadInfo info : threads) { finishedadd += info.getFinished(); } } long progressMath = (long)finishedadd*100/mFileInfo.getLength(); intent.putExtra("finished",progressMath); Log.i("DownLoadTask", "正在下載....,finishedadd的值为 " + finishedadd); Log.i("DownLoadTask", "正在下載....,文件的长度为 " + mFileInfo.getLength()); Log.i("DownLoadTask","正在下載....,进度条的值 "+ progressMath); context.sendBroadcast(intent); } if (isPause) { mFileDao.updateInfo(threadInfo.getUrl(), threadInfo.getId(), threadInfo.getFinished()); return; } } //每一个线程完成时检查下是否下载完成 checkIsFinished(); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { conn.disconnect(); is.close(); raf.close(); } catch (Exception e) { e.printStackTrace(); } } } }}
5、测试结果
特别说明:此人自学Android开发,有何不足之处多谢指教。
- 多文件多线程断点续传项目练习总结
- android 多文件多线程断点续传下载
- 多线程文件断点续传
- 安卓中多线程断点续传文件下载核心代码总结
- Android多线程断点续传实现总结
- 多线程断点续传文件下载原理
- Android 文件多线程断点续传下载
- java 多线程文件下载,断点续传
- 多线程多任务断点续传
- lftp 多线程断点续传 下载文件 wget
- 《FTP、HTTP 多线程断点续传下载文件》 FlashGet
- Android多线程断点续传下载文件类设计
- Android多线程断点续传下载文件类设计
- java SE 文件多线程下载,断点续传 原理
- 多线程断点续传(文件保存进度方式)
- Aandroid 多线程断点续传同时下载多个大文件
- C#实现http多线程断点续传下载文件
- Android 多线程多任务断点续传
- Nginx 性能调优
- 数字三角形
- angular表单验证及 提交功能实现
- 使用Vgrant安装Homestead构建Laravel环境
- 安卓Andriod使用入门(二十七)【点赞动画】
- 多文件多线程断点续传项目练习总结
- Java利用Socket进行远程过程调用
- 转载 同龄人2016年的一篇博文 用来自励
- 数学建模学习笔记
- 利用 pwntools 编写 socket 脚本
- C++ 构造函数使用 ":成员变量(形参)" 的形式给类里面成员变量赋值,如果成员变量和形参是指针,那么需要注意的事项
- 最大公约数与最小公倍数
- 工作项目 17年2月1日 关于迭代法的应用
- 40. Combination Sum II