android apk更新写法,Android自动更新APK(四)

66b52468c121889b900d4956032f1009.png

8种机械键盘轴体对比

本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?

这一节研究一下断点续传的原理和实现。

Http断点续传原理

关于断点续传,关键点主要有两个:1、客户端知道当前的文件和上一次加载的文件是不是内容发生了变化,如果有变化,需要重新从offset 0 的位置开始下载;

2、客户端记录好上次成功下载到的offset,告诉服务器,服务器支持从特定的offset 开始传输数据。

通常情况下,Web服务器(如Apache)会默认开启对断点续传的支持。验证服务器是否支持断点续传,通过Http分析工具,查看服务器的响应header中是否包含“Accept-Ranges: bytes”(按字节下载)或者查看响应码:HTTP/1.1 200 Ok(不使用断点续传方式)

HTTP/1.1 206 Partial Content(使用断点续传方式)

针对第一个点,我们可以通过Http协议的ETAG方案实现:HTTP 的ETAG来标识是否文件已经修改。从DownloadManager的下载源码中可以看到,DownloadManager也用到了ETAG这种方案。关于ETAG方案:ETAG原理:如果URL上的资源内容改变,一个新的不一样的ETag就会被分配。用这种方法使用ETag即类似于指纹,并且他们能够被快速地被比较,以确定两个版本的资源是否相同。ETag的比较只对同一个URL有意义——不同URL上的资源的ETag值可能相同也可能不同,从他们的ETag的比较中无从推断。

我们要做的就是收到响应的时候存储这个ETag,以便下次下载的时候从断点下载处再把这个ETag发送给服务器。1

2

3

4

5

6

7

8

9

10

11

12

13Request.Builder builder = new Request.Builder();

if (mETag != null) {

builder.header("If-Match", mETag);

}

// 收到响应的时候,获得ETag信息

public void (Call call, Response response){

mETag = response.header("ETag");

}

// 剩下的就是交给服务器去做校验了

针对第二个点,我们可以通过HTTP头Range字段,指定下载文件的某一段大小,及其单位。格式如下:Range: bytes=0-499 下载第0-499字节范围的内容

Range: bytes=500-999 下载第500-999字节范围的内容

Range: bytes=-500 下载最后500字节的内容

Range: bytes=500- 下载从第500字节开始到文件结束部分的内容1

2

3Request.Builder builder = new Request.Builder();

// 下载从 currentBytes 开始到文件结束部分的内容

builder.header("RANGE", "bytes=" + currentBytes + "-");// Http header不区分大小写

在收到服务器的时候,在header信息中会有一个Content-Range字段,形如:Content-Range: bytes 1024-7283259/7283259

客户端发请求时对应的是 Range ,服务器端响应时对应的是 Content-Range

本地文件操作

关于断点下载和服务器的交互,已经能够保证了。接下来要考虑的就是对本地已下载的部分文件的操作。在Android中可以使用RandomAccessFile来随机的操作文件(这块还要考虑磁盘空间是否够用等,此处暂不考虑)。1

2

3

4

5

6

7

8

9

10RandomAccessFile randomAccessFile;

try {

File file = new File(mContext.getExternalCacheDir(),"MFVKeyguard.apk");

randomAccessFile = new RandomAccessFile(file, "rwd");

if (file.exists()) { // 如果文件存在

mCurrentBytes = randomAccessFile.length(); // 获得当前文件大小

}

} catch (Exception e) {

e.printStackTrace();

}

在OkHttp的onResponse回调中,实现如下:1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32public void (Call call, Response response){

int len;

byte[] buf = new byte[2048];

InputStream inputStream = null;

FileChannel channelOut = null;

try {

// mETag = response.header("ETag");

// mLastModified = response.header("Last-Modified");

inputStream = response.body().byteStream();

Log.d(TAG, "ETag= " + mETag + " Last-Modified=" + mLastModified +

" code=" + response.code());

Log.d(TAG, "Content-Range= " + response.header("Content-Range"));

channelOut = randomAccessFile.getChannel();

MappedByteBuffer mappedBuffer = channelOut.map(FileChannel.MapMode.READ_WRITE,

mCurrentBytes, response.body().contentLength());

while ((len = inputStream.read(buf)) != -1) {

mappedBuffer.put(buf, 0, len);

}

} catch (Exception e) {

Log.d(TAG, "Got an exception, download failure");

mListener.onDownloadAppFailed();

} finally {

closeInputStream(inputStream);

closeChannelOut(channelOut);

closeRandomAccessFile(randomAccessFile);

}

}

此处,没有使用randomAccessFile的seek方法来移动到写入位置,对于大文件来说这种速度比较慢,这里采用了NIO包中的FileChannel。通过fileChannel得到的MappedByteBuffer是ByteBuffer的子类,通过MappedByteBuffer的put方法来写入到缓存中:1

2

3

4

5

6int len;

byte[] buf = new byte[2048];

InputStream inputStream = response.body().byteStream();

while ((len = inputStream.read(buf)) != -1) {

mappedBuffer.put(buf, 0, len);

}

IO与NIO直接最大的一个区别就是:IO是面向流的;NIO是面向缓存区的。关于IO与NIO可参考:Java IO与NIO

然后randomAccessFile通过FileChannel从缓存中读取:1

2

3

4

5

6// 将此通道的文件区域直接映射到内存中;

// 必须指明,它是从文件的哪个位置开始映射的【mCurrentBytes】,

// 映射的范围又有多大【response.body().contentLength()】

FileChannel channelOut = randomAccessFile.getChannel();

MappedByteBuffer mappedBuffer = channelOut.map(FileChannel.MapMode.READ_WRITE,

mCurrentBytes, response.body().contentLength());

注意:response.body().contentLength(),下次再从断点出下载时,表示剩余未下载的大小

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值