Android断点续传详解和实例
来源:互联网 发布:怎么咨询网络服务商 编辑:程序博客网 时间:2024/05/22 16:49
前言
作为一个拖延者晚期的人,真的是计划跟行动力很难直接挂上钩,最近又有了新的打算,所以博客还是需要好好整理一下
在写越来越多的代码,不,准确说是改别人的代码的时候,发现作为程序员,创作性精神真的是不能少,我们现在能用代码构建的一切,都是建立在前人提前制定好规则的基础上,所以现在的大神也是在理解前人的思想的基础上,加以自己的想法去融会贯通,才能做出来好的产品,我自己现在的弊端就是着急,太着急,从android的应用到破解到游戏,看别人的思想,却有点迷失了.所以,打算沉下心来,好好去弄明白一类问题,来日方长,我还有时间去慢慢学习.废话了太多,也算是自我检醒,希望看到这篇博客的童鞋勿怪.
这次主要是运用的RandomAccessFile类来做 断点续传这件事,我一贯喜欢代码简洁清晰,所以尽可能用最少的代码解决问题
断点续传原理及使用
断点续传想必大家都不陌生,其中最常见的是视频的断点续传,在其他的文件下载中,我们有时候为了保证下载文件的完整性也会用到RandomAccessFile类。
RandomAccessFile主要是记录开始的位置,从而进行插入操作,以下摘自百度百科,其实是原官方的翻译
RandomAccessFile是不属于InputStream和OutputStream类系的。实际上,除了实现DataInput和DataOutput接口之外(DataInputStream和DataOutputStream也实现了这两个接口),它和这两个类系毫不相干,甚至都没有用InputStream和OutputStream已经准备好的功能;它是一个完全独立的类,所有方法(绝大多数都只属于它自己)都是从零开始写的。这可能是因为RandomAccessFile能在文件里面前后移动,所以它的行为与其它的I/O类有些根本性的不同。总而言之,它是一个直接继承Object的,独立的类。
附:RandomAccessFile类的api的地址
RandomAccessFile类分析
- 构造方法
- RandomAccessFile(File file, String mode)
- RandomAccessFile(String name, String mode)
- 常用方法
- 流关闭
- close()
- 获取文件的长度
- length()
- 设置文件的长度
- setLength(long newLength)
- 设置文件指针的开始偏移位置
- seek(long pos)
- 读操作
- read()
- read(byte[] b)
- read(byte[] b, int off, int len)
- 写操作
- write(byte[] b)
- write(byte[] b, int off, int len)
- 流关闭
代码实例
在写实例之前,说一下现在要实现的功能以及实现的基本思路
- 功能
- http下载文件,允许开始下载,暂停下载,恢复下载和取消下载
- 实现思路
- 需要先获取该文件的大小,创建一个空的文件,然后向该文件中依次写入数据
实现类 HttpDownLoadFile
(已测试过,功能正常,测试机型:Meizu M1 Metal Android 5.1)
import android.content.Context;import android.content.SharedPreferences;import android.os.Handler;import android.os.Message;import java.io.InputStream;import java.io.RandomAccessFile;import java.net.HttpURLConnection;import java.net.URL;/** * 多线程下载文件 */public class HttpDownLoadFile { private static final String SP_NAME = "http_download_file"; private static final String CURRENT_LENGTH = "current_length"; //以下为线程状态 private static final String DOWNLOAD_INIT = "1";//初始 private static final String DOWNLOAD_ING = "2";//运行 private static final String DOWNLOAD_PAUSE = "3";//暂停 private String loadUrl;//网络获取的url private String filePath;//下载到本地的path private int fileLength;//文件总大小 //使用volatile防止多线程不安全 private volatile int currentLength;//当前总共下载的大小 private Thread thread; private String stateDownload = DOWNLOAD_INIT;//当前线程状态 private OnHttpDownLoadListener httpDownLoadListener; private final int SUCCESS = 0x00000101; private final int FAILURE = 0x00000102; private SharedPreferences sp; public void setOnHttpDownLoadListener(OnHttpDownLoadListener httpDownLoadListener) { this.httpDownLoadListener = httpDownLoadListener; } public interface OnHttpDownLoadListener { //返回当前下载进度的百分比 void onProgress(int progress); void onComplete(); void onFailure(); } public HttpDownLoadFile(Context mContext, String loadUrl, String filePath) { this(mContext, loadUrl, filePath, null); } public HttpDownLoadFile(Context mContext, String loadUrl, String filePath, OnHttpDownLoadListener mDownLoadListener) { this.loadUrl = loadUrl; this.filePath = filePath; this.httpDownLoadListener = mDownLoadListener; this.sp = mContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE); } /** * 开始下载 */ public void onStart() { //在线程中运行,防止anr new Thread(new Runnable() { @Override public void run() { try { //建立连接请求 URL url = new URL(loadUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); int code = conn.getResponseCode();//获取返回码 LogUtil.e("","code>>>"+code); if (code == 200) {//请求成功,根据文件大小开始分多线程下载 LogUtil.e("","开始执行下载"); fileLength = conn.getContentLength(); RandomAccessFile raf = new RandomAccessFile(filePath, "rwd"); raf.setLength(fileLength); //获取上次的下载位置 currentLength = sp.getInt(CURRENT_LENGTH, 0); //开始位置,获取上次取消下载的进度 int startPosition = sp.getInt(SP_NAME , 0); //结束位置 int endPosition = fileLength - 1; thread = new DownThread(startPosition, endPosition); thread.start(); raf.close(); } else { handler.sendEmptyMessage(FAILURE); } } catch (Exception e) { e.printStackTrace(); LogUtil.e("","code>>> e.printStackTrace()"); handler.sendEmptyMessage(FAILURE); } } }).start(); } /** * 取消下载 */ public void onCancle() { if (thread != null) { //若线程处于等待状态,则while循环处于阻塞状态,无法跳出循环,必须先唤醒线程,才能执行取消任务 if (stateDownload.equals(DOWNLOAD_PAUSE)) onStart(); ((DownThread) thread).cancel(); } } /** * 暂停下载 */ public void onPause() { if (thread != null) stateDownload = DOWNLOAD_PAUSE; } /** * 继续下载 */ public void onResume() { if (thread != null) synchronized (DOWNLOAD_PAUSE) { stateDownload = DOWNLOAD_ING; DOWNLOAD_PAUSE.notifyAll(); } } /** * 结束下载 */ public void onDestroy() { if (thread != null) thread = null; } private class DownThread extends Thread { private boolean isGoOn = true;//是否继续下载 private int startPosition;//开始下载点 private int endPosition;//结束下载点 private int currPosition;//当前线程的下载进度 private DownThread(int startPosition, int endPosition) { this.startPosition = startPosition; currPosition = startPosition; this.endPosition = endPosition; } @Override public void run() { InputStream is = null; RandomAccessFile raf = null; try { URL url = new URL(loadUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition); //若请求头加上Range这个参数,则返回状态码为206,而不是200 if (conn.getResponseCode() == 206) { is = conn.getInputStream(); raf = new RandomAccessFile(filePath, "rwd"); raf.seek(startPosition);//跳到指定位置开始写数据 int len; byte[] buffer = new byte[1024]; while ((len = is.read(buffer)) != -1) { //是否继续下载 if (!isGoOn) break; //回调当前进度 if (httpDownLoadListener != null) { currentLength += len; int progress = (int) ((float) currentLength / (float) fileLength * 100); handler.sendEmptyMessage(progress); } raf.write(buffer, 0, len); //写完后将当前指针后移,为取消下载时保存当前进度做准备 currPosition += len; synchronized (DOWNLOAD_PAUSE) { if (stateDownload.equals(DOWNLOAD_PAUSE)) { DOWNLOAD_PAUSE.wait(); } } } //若取消下载,则直接返回 if (!isGoOn) { if (currPosition < endPosition) { sp.edit().putInt(SP_NAME, currPosition).apply(); sp.edit().putInt(CURRENT_LENGTH, currentLength).apply(); } return; } handler.sendEmptyMessage(SUCCESS); onDestroy(); } else { handler.sendEmptyMessage(FAILURE); } } catch (Exception e) { e.printStackTrace(); handler.sendEmptyMessage(FAILURE); }finally { try{ sp.edit().clear().apply(); if(null != is) is.close(); if(null != raf) raf.close(); }catch (Exception e){ e.printStackTrace(); } } } public void cancel() { isGoOn = false; } } private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (httpDownLoadListener != null) { if (msg.what == SUCCESS) { httpDownLoadListener.onComplete(); } else if (msg.what == FAILURE) { httpDownLoadListener.onFailure(); } else { httpDownLoadListener.onProgress(msg.what); } } } };}
使用方法应该不用我多写了,相信大家都看得懂,坑已填,好困,碎觉
有什么不对的地方,欢迎指正,谢谢
- Android断点续传详解和实例
- android实用方法- - - -断点续传详解
- android 之断点续传详解三部曲之[二] → 断点续传下载
- android多线程下载和断点续传
- Android多线程下载和断点续传
- android 支持多线程和断点续传
- Android多线程断点续传详解与案例
- Android Intent和Bundle机制实例详解
- Android Intent和Bundle机制实例详解
- Android Intent和Bundle机制实例详解
- Android SoundPool播放实例和方法详解
- Android Intent和Bundle机制实例详解
- Android-自定义preference和PreferenceFragment实例详解
- android实现断点续传和多线程下载
- android 实现多线程下载和断点续传
- Android断点续传的原理剖析与实例讲解
- Android断点续传
- android断点续传
- 笔试之网易秋招
- 矩阵乘法
- 线程相关
- SpringMVC
- FBI序列
- Android断点续传详解和实例
- 精品软件分享
- 各大公司Java后端开发面试题总结
- linux批量管理推送工具mussh和pssh介绍
- hdu 6082 度度熊与邪恶大魔王(完全背包)
- nRF52832 — DFU升级
- Greenplum 临时表年龄问题
- 风口的猪-中国牛市(动态规划)
- SOAP在PHP中的使用