先上图
支持断点下载,但是程序退出之后再打开不会继续下载,这只是一个Demo练习,有很多Bug还没有来得及修改和优化。
界面很简单 只看界面都大概知道什么意思了
大概的流程就和下图一样。就分这几个部分。
initEvents 方法完成了界面的三个Button的功能
①开始下载:首先判断该路径下是否有改文件,如果没有就使用Intent启动一个开始下载的服务类DownloadService。使用intent.putExtra(“fileInfo”, fileInfo) 将 fileinfo传递给服务 (下载的时候不能取消下载,毕竟是个Demo还没有完善)。
②暂停下载:和开始下载的功能一样,把服务换成暂停的服务
③删除文件:将该路径下的文件夹删除。
④广播接收器:注册一个广播接收器,用来接收线程中返回的进度信息。
private void initEvents() {
tv_name.setText(getFilename(path));
btn_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//通过Intent传递参数给service
if (!getFileExist(DownloadService.DOWNLOAD_PATH + getFilename(path))) {
Intent intent = new Intent(getActivity(), DownloadService.class);
intent.setAction(DownloadService.ACTION_START);
intent.putExtra("fileInfo", fileInfo);
getActivity().startService(intent);
btn_cenal.setEnabled(false);
} else {
Toast.makeText(getContext(), "文件已存在,不能重复下载", Toast.LENGTH_SHORT).show();
BuShow();
}
}
});
btn_stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), DownloadService.class);
intent.setAction(DownloadService.ACTION_STOP);
intent.putExtra("fileInfo", fileInfo);
getActivity().startService(intent);
}
});
btn_cenal.setOnClickListener(new View.OnClickListener() {
@SuppressLint("SetTextI18n")
@Override
public void onClick(View v) {
File file = new File(DownloadService.DOWNLOAD_PATH);
removeFile(file);
progressBar.setProgress(0);
tv_show.setText("00%");
}
});
//注册广播接收器
IntentFilter filter = new IntentFilter();
filter.addAction(DownloadService.ACTION_UPDATE);
getActivity().registerReceiver(mReceiver, filter);
}
以下几个是完成小功能的几个方法 不再多说
//判断文件是否存在
public boolean getFileExist(String filepath) {
File file = new File(filepath);
if (file.exists()) {
return true;
}
return false;
}
//删除文件
public void removeFile(File file) {
//如果是文件直接删除
if (file.isFile()) {
file.delete();
}
//如果是目录递归判断,如果是空目录直接删除
else if (file.isDirectory()) {
File[] childFile = file.listFiles();
if (childFile == null || childFile.length <= 0) {
file.delete();
} else {
for (File f : childFile) {
removeFile(f);
}
file.delete();
}
}
}
private String getFilename(String path) {
return path.substring(path.lastIndexOf("/") + 1);
}
↓↓↓ 更新UI的广播接收器
/**
* 更新UI的广播接收器
*/
BroadcastReceiver mReceiver = new BroadcastReceiver() {
@SuppressLint("SetTextI18n")
@Override
public void onReceive(Context context, Intent intent) {
if (DownloadService.ACTION_UPDATE.equals(intent.getAction())) {
int finished = intent.getIntExtra("finished", 0);
progressBar.setProgress(finished);
tv_show.setText(finished + "%");
if (progressBar.getProgress() == 100) {
btn_cenal.setEnabled(true);
BuShow();
}
}
}
};
这个类的全部代码
public class Fragment_DownLoad extends Fragment {
private View view;
private String path;
private Button btn_start;
private Button btn_stop;
private TextView tv_name;
private ProgressBar progressBar;
private FileInfo fileInfo;
private TextView tv_show;
private Button btn_cenal;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.activity_download, container, false);
initview();
initEvents();
return view;
}
private void initview() {
path = "http://cdn.llsapp" +
".com/android/LLS-v4.0-595-20160908-143200.apk";
btn_start = view.findViewById(R.id.btn_start);
btn_stop = view.findViewById(R.id.btn_stop);
btn_cenal = view.findViewById(R.id.btn_cenal);
progressBar = view.findViewById(R.id.progress);
tv_show = view.findViewById(R.id.tv_show);
tv_name = view.findViewById(R.id.tv_name);
//tv_add = view.findViewById(R.id.tv_add);
//创建文件信息对象
fileInfo = new FileInfo(0, path, getFilename(path), 0, 0);
progressBar.setMax(100);
if (getFileExist(DownloadService.DOWNLOAD_PATH + getFilename(path))) {
btn_cenal.setEnabled(true);
} else {
btn_cenal.setEnabled(false);
}
}
private void initEvents() {
tv_name.setText(getFilename(path));
btn_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//通过Intent传递参数给service
if (!getFileExist(DownloadService.DOWNLOAD_PATH + getFilename(path))) {
Intent intent = new Intent(getActivity(), DownloadService.class);
intent.setAction(DownloadService.ACTION_START);
intent.putExtra("fileInfo", fileInfo);
getActivity().startService(intent);
btn_cenal.setEnabled(false);
} else {
Toast.makeText(getContext(), "文件已存在,不能重复下载", Toast.LENGTH_SHORT).show();
BuShow();
}
}
});
btn_stop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), DownloadService.class);
intent.setAction(DownloadService.ACTION_STOP);
intent.putExtra("fileInfo", fileInfo);
getActivity().startService(intent);
}
});
btn_cenal.setOnClickListener(new View.OnClickListener() {
@SuppressLint("SetTextI18n")
@Override
public void onClick(View v) {
File file = new File(DownloadService.DOWNLOAD_PATH);
removeFile(file);
progressBar.setProgress(0);
tv_show.setText("00%");
}
});
//注册广播接收器
IntentFilter filter = new IntentFilter();
filter.addAction(DownloadService.ACTION_UPDATE);
getActivity().registerReceiver(mReceiver, filter);
}
@Override
public void onDestroy() {
super.onDestroy();
getActivity().unregisterReceiver(mReceiver);
}
/**
* 更新UI的广播接收器
*/
BroadcastReceiver mReceiver = new BroadcastReceiver() {
@SuppressLint("SetTextI18n")
@Override
public void onReceive(Context context, Intent intent) {
if (DownloadService.ACTION_UPDATE.equals(intent.getAction())) {
int finished = intent.getIntExtra("finished", 0);
progressBar.setProgress(finished);
tv_show.setText(finished + "%");
if (progressBar.getProgress() == 100) {
btn_cenal.setEnabled(true);
BuShow();
}
}
}
};
private void BuShow() {
AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());
builder.setTitle("提示:");
builder.setMessage("下载成功是否打开");
builder.setCancelable(true);
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
File file = new File(DownloadService.DOWNLOAD_PATH + getFilename(path));
try {
OpenFileUtils.openFile(getContext(), file);
} catch (Exception e) {
Toast.makeText(view.getContext(), "错误:" + e.toString(), Toast
.LENGTH_SHORT)
.show();
e.printStackTrace();
}
}
});
builder.show();
}
//判断文件是否存在
public boolean getFileExist(String filepath) {
File file = new File(filepath);
if (file.exists()) {
return true;
}
return false;
}
//删除文件
public void removeFile(File file) {
//如果是文件直接删除
if (file.isFile()) {
file.delete();
}
//如果是目录递归判断,如果是空目录直接删除
else if (file.isDirectory()) {
File[] childFile = file.listFiles();
if (childFile == null || childFile.length <= 0) {
file.delete();
} else {
for (File f : childFile) {
removeFile(f);
}
file.delete();
}
}
Toast.makeText(getContext(), "删除成功", Toast.LENGTH_SHORT).show();
}
private String getFilename(String path) {
return path.substring(path.lastIndexOf("/") + 1);
}
}
Downloadservice类继承了Service
在 onStartCommand方法中根据用户的命令 判断是执行下载还是
暂停下载。
开始下载:启动一个子线程
暂停下载:mTask.isPause = true; 这句话会导致线程执行 return 不再进行下载。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (ACTION_START.equals(intent.getAction())) {
FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
Log.d("测试", "start:" + fileInfo.toString());
//启动初始化线程
new InitThread(fileInfo).start();
} else if (ACTION_STOP.equals(intent.getAction())) {
FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
Log.d("测试", "stop:" + fileInfo.toString());
if (mTask != null) {
mTask.isPause = true;
}
}
return super.onStartCommand(intent, flags, startId);
}
InitThread类继承了Thread
这个类的功能就是在获取网络文件,在本地创建一个和网络文件大小一样的文件,最后启动handler
class InitThread extends Thread {
private FileInfo mFileInfo = null;
private HttpURLConnection conn;
private RandomAccessFile raf;
public InitThread(FileInfo mFileInfo) {
this.mFileInfo = mFileInfo;
}
@Override
public void run() {
try {
//连接网络文件
URL url = new URL(mFileInfo.getUrl());
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(3000);
//下载用GET 其他的用POST
conn.setRequestMethod("GET");
int lenght = -1;
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {//OK
//获取文件长度
lenght = conn.getContentLength();
}
//判断获取到的文件长度是否有问题
if (lenght <= 0) {
return;
}
File dir = new File(DOWNLOAD_PATH);
if (!dir.exists()) {
dir.mkdir();
}
//在本地创建文件
File file = new File(dir, mFileInfo.getFileName());
//权限可读写删
raf = new RandomAccessFile(file, "rwd");
//本地文件长度等于网络文件的长度
raf.setLength(lenght);
mFileInfo.setLength(lenght);
handler.obtainMessage(MSG_INIT, mFileInfo).sendToTarget();
} catch (Exception e) {
e.printStackTrace();
} finally {
conn.disconnect();
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
handler 启动下载任务
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.d("测试", "Init:" + fileInfo);
//启动下载任务
mTask = new DownloadTask(DownloadService.this, fileInfo);
mTask.download();
break;
}
}
};
DownloadService 完整的代码
public class DownloadService extends Service {
public static final String ACTION_START = "ACTION_START";
public static final String ACTION_STOP = "ACTION_STOP";
public static final String ACTION_UPDATE = "ACTION_UPDATE";
//获取SD卡根目录
public static final String DOWNLOAD_PATH = Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/downloads/";
public static final int MSG_INIT = 0;
private DownloadTask mTask = null;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (ACTION_START.equals(intent.getAction())) {
FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
Log.d("测试", "start:" + fileInfo.toString());
//启动初始化线程
new InitThread(fileInfo).start();
} else if (ACTION_STOP.equals(intent.getAction())) {
FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
Log.d("测试", "stop:" + fileInfo.toString());
if (mTask != null) {
mTask.isPause = true;
}
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@SuppressLint("HandlerLeak")
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.d("测试", "Init:" + fileInfo);
//启动下载任务
mTask = new DownloadTask(DownloadService.this, fileInfo);
mTask.download();
break;
}
}
};
/*
初始化子线程
*/
class InitThread extends Thread {
private FileInfo mFileInfo = null;
private HttpURLConnection conn;
private RandomAccessFile raf;
public InitThread(FileInfo mFileInfo) {
this.mFileInfo = mFileInfo;
}
@Override
public void run() {
try {
//连接网络文件
URL url = new URL(mFileInfo.getUrl());
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(3000);
//下载用GET 其他的用POST
conn.setRequestMethod("GET");
int lenght = -1;
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {//OK
//获取文件长度
lenght = conn.getContentLength();
}
//判断获取到的文件长度是否有问题
if (lenght <= 0) {
return;
}
File dir = new File(DOWNLOAD_PATH);
if (!dir.exists()) {
dir.mkdir();
}
//在本地创建文件
File file = new File(dir, mFileInfo.getFileName());
//权限可读写删
raf = new RandomAccessFile(file, "rwd");
//本地文件长度等于网络文件的长度
raf.setLength(lenght);
mFileInfo.setLength(lenght);
handler.obtainMessage(MSG_INIT, mFileInfo).sendToTarget();
} catch (Exception e) {
e.printStackTrace();
} finally {
conn.disconnect();
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
DownloadTask 类 下载任务类
内部类DownloadThread继承Thread
因为牵扯到断点下载,所以需要用到数据库,每次开始下载之前先判断数据库中是否有数据,如果有数据就接着下载,如果没有数据就向数据库插入线程信息。
里边有注释看注释吧
/**
* 下载线程
*/
class DownloadThread extends Thread {
private ThreadInfo mThreadinfo = null;
private HttpURLConnection conn;
private RandomAccessFile raf;
private InputStream input;
public DownloadThread(ThreadInfo mThreadinfo) {
this.mThreadinfo = mThreadinfo;
}
public void run() {
/*
向数据库插入线程信息
*/
if (!mDao.isExists(mThreadinfo.getUrl(), mFileinfo.getId())) {
mDao.insertThread(mThreadinfo);
}
try {
URL url = new URL(mThreadinfo.getUrl());
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(3000);
conn.setRequestMethod("GET");
/*
设置下载位置
*/
//开始位置 = 线程的开始位置+线程的完成位置
int start = mThreadinfo.getStart() + mThreadinfo.getFinished();
//下载的范围
conn.setRequestProperty("Range", "bytes=" + start + "-" + mThreadinfo.getEnd());
/*
设置文件写入位置
*/
File file = new File(DownloadService.DOWNLOAD_PATH, mFileinfo.getFileName());
raf = new RandomAccessFile(file, "rwd");
raf.seek(start);
mFinished += mThreadinfo.getFinished();
/*
开始下载
*/
if (conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL) {
//1.读取数据
input = conn.getInputStream();
byte[] buffer = new byte[1024 * 4];
int len = -1;
long time = System.currentTimeMillis();
while ((len = input.read(buffer)) != -1) {
//2.写入文件
raf.write(buffer, 0, len);
//4.把下载进度发送广播给Activity
mFinished += len;
//每0.5s刷新一次界面
if (System.currentTimeMillis() - time > 500) {
time = System.currentTimeMillis();
Intent intent = new Intent(DownloadService.ACTION_UPDATE);
intent.putExtra("finished", (int) (mFinished * 100 / mFileinfo
.getLength()));
context.sendBroadcast(intent);
}
//3.下载暂停时,把下载进度保存到数据库
if (isPause) {
mDao.updateThread(mThreadinfo.getUrl(), mThreadinfo.getId(), mFinished);
return;
}
}
}
//删除线程信息
mDao.deleteThread(mThreadinfo.getUrl(), mThreadinfo.getId());
Log.d("测试", "删除了");
Intent intent = new Intent(DownloadService.ACTION_UPDATE);
intent.putExtra("finished", 100);
context.sendBroadcast(intent);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
conn.disconnect();
input.close();
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
还有一个download方法 创建下载线程
//创建下载线程
public void download() {
//读取数据库的线程信息
List<ThreadInfo> threadInfos = mDao.getThreads(mFileinfo.getUrl());
ThreadInfo threadInfo = null;
if (threadInfos.size() == 0) {
//初始化线程信息对象
threadInfo = new ThreadInfo(0, mFileinfo.getUrl(), 0, mFileinfo.getLength(), 0);
} else {
threadInfo = threadInfos.get(0);
}
//创建子线程进行下载
new DownloadThread(threadInfo).start();
}
DownloadTask 全部代码
this.context = context;
this.mFileinfo = mFileinfo;
mDao = new ThreadDAOImpl(context);
}
//创建下载线程
public void download() {
//读取数据库的线程信息
List<ThreadInfo> threadInfos = mDao.getThreads(mFileinfo.getUrl());
ThreadInfo threadInfo = null;
if (threadInfos.size() == 0) {
//初始化线程信息对象
threadInfo = new ThreadInfo(0, mFileinfo.getUrl(), 0, mFileinfo.getLength(), 0);
} else {
threadInfo = threadInfos.get(0);
}
//创建子线程进行下载
new DownloadThread(threadInfo).start();
}
/**
* 下载线程
*/
class DownloadThread extends Thread {
private ThreadInfo mThreadinfo = null;
private HttpURLConnection conn;
private RandomAccessFile raf;
private InputStream input;
public DownloadThread(ThreadInfo mThreadinfo) {
this.mThreadinfo = mThreadinfo;
}
public void run() {
/*
向数据库插入线程信息
*/
if (!mDao.isExists(mThreadinfo.getUrl(), mFileinfo.getId())) {
mDao.insertThread(mThreadinfo);
}
try {
URL url = new URL(mThreadinfo.getUrl());
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(3000);
conn.setRequestMethod("GET");
/*
设置下载位置
*/
//开始位置 = 线程的开始位置+线程的完成位置
int start = mThreadinfo.getStart() + mThreadinfo.getFinished();
//下载的范围
conn.setRequestProperty("Range", "bytes=" + start + "-" + mThreadinfo.getEnd());
/*
设置文件写入位置
*/
File file = new File(DownloadService.DOWNLOAD_PATH, mFileinfo.getFileName());
raf = new RandomAccessFile(file, "rwd");
raf.seek(start);
mFinished += mThreadinfo.getFinished();
/*
开始下载
*/
if (conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL) {
//1.读取数据
input = conn.getInputStream();
byte[] buffer = new byte[1024 * 4];
int len = -1;
long time = System.currentTimeMillis();
while ((len = input.read(buffer)) != -1) {
//2.写入文件
raf.write(buffer, 0, len);
//4.把下载进度发送广播给Activity
mFinished += len;
//每0.5s刷新一次界面
if (System.currentTimeMillis() - time > 500) {
time = System.currentTimeMillis();
Intent intent = new Intent(DownloadService.ACTION_UPDATE);
intent.putExtra("finished", (int) (mFinished * 100 / mFileinfo
.getLength()));
context.sendBroadcast(intent);
}
//3.下载暂停时,把下载进度保存到数据库
if (isPause) {
mDao.updateThread(mThreadinfo.getUrl(), mThreadinfo.getId(), mFinished);
return;
}
}
}
//删除线程信息
mDao.deleteThread(mThreadinfo.getUrl(), mThreadinfo.getId());
Log.d("测试", "删除了");
Intent intent = new Intent(DownloadService.ACTION_UPDATE);
intent.putExtra("finished", 100);
context.sendBroadcast(intent);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
conn.disconnect();
input.close();
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
差不多了,主要的就这么多东西,我也是第一次写博客,也不知道怎么写,我觉得自己把写的代码以这种形式再回忆一遍记忆的会更清楚。有些知识点我也不是很理解。慢慢积累。会成为大佬的。
有的代码就是自己知道是干嘛的就是说不出来。以自己理解的方法理解了。 以后尽量写的更加详细。 对了。我这个是跟着慕课网上学习的 网址:https://www.imooc.com/course/list 大家可以去看看,我就是跟着视频写的。改动不大。