android okhttp 断点续传

小心以下的坑:

1、网络是不可靠的,凡是连接网络的部分,都有可能失效;

2、过于频繁的请求连接会出问题;

3、okhttp依赖于okio,从某个版本的okio开始,需要kotlin核心库;

进入正题。

基本思路:利用http head头中的rang可以声明请求的范围。那么,真正下载时可以请求部分资源。

当使用单线程进行断点续传时,第一次下载请求的是全部资源(完整长度)。被中断后,再次下载,此时请求的是上次已经下载成功的部分到资源的末尾,如此一直下去直到下载完成或达到其它结束条件(超时、重试次数等)。

如果进行多线程断点续传,那么先将待下资源划分成多个小块,然后交给多个线程分别下载。

下载的内容写入文件时,一般用RandomAccess方式。对于单线程的情况,实际上退化成了Append方式。

下载类的代码如下:

public class SingleThreadDownader
{
	public static final int TYPE_SUCCESS = 0;

	public static final int TYPE_FAILED = 1;

	public static final int MAX_COUNT = 10;// 重试的最大次数

	private DownloadReceiverInterface receiver;

	private String downloadUrl;
	private Context context;
	private String downloadedFileName;
	long contentLength;

	public SingleThreadDownader(Context _context, String _downloadUrl, String _downloadedFileName, DownloadReceiverInterface listener)
	{
		this.context = _context;
		this.downloadUrl = _downloadUrl;
		this.downloadedFileName = _downloadedFileName;
		this.receiver = listener;
	}

	public void work()
	{
		File f = new File(this.context.getFilesDir() + "/" + this.downloadedFileName);
		contentLength = getContentLength(downloadUrl);
		if (contentLength == 0)
		{
			this.receiver.downloadFailed();
			return;
		}

		for (int i = 0; i < MAX_COUNT; i++)
		{
			int x = download();

			switch (x)
			{
			case TYPE_SUCCESS:
				receiver.downloadFinish();
				return;
			case TYPE_FAILED:
				//SLEEP 15S
				try
				{
					Thread.sleep(15*1000);
				} catch (InterruptedException e)
				{
				}
				continue;
			}
		}
		receiver.downloadFailed();

	}

	protected Integer download()
	{
		File f = new File(this.context.getFilesDir() + "/" + this.downloadedFileName);
		InputStream is = null;
		RandomAccessFile savedFile = null;
		long downloadLength = 0;
		if (f.exists())
		{
			downloadLength = f.length();
		}else
		{
			try
			{
				f.createNewFile();
			} catch (IOException e)
			{
				return TYPE_FAILED;
			}
		}

		if (contentLength == downloadLength)
		{
			return TYPE_SUCCESS;
		}
		OkHttpClient client = new OkHttpClient.Builder().connectTimeout(900, TimeUnit.SECONDS).readTimeout(1800, TimeUnit.SECONDS).writeTimeout(900, TimeUnit.SECONDS).build();
		Request request = new Request.Builder().addHeader("RANGE", "bytes=" + downloadLength + "-"+contentLength).url(downloadUrl).build();
		try
		{
			Response response = client.newCall(request).execute();
			if (response != null)
			{
				is = response.body().byteStream();
				savedFile = new RandomAccessFile(f, "rw");
				savedFile.seek(downloadLength);
				byte[] b = new byte[1024];
				int total = 0;
				int len;
				while ((len = is.read(b)) != -1)
				{
					total += len;
					savedFile.write(b, 0, len);
					try
					{
						Thread.sleep(2);
					} catch (InterruptedException e)
					{
					}
				}
				response.body().close();
				return TYPE_SUCCESS;
			} else
			{
				return TYPE_FAILED;
			}
		} catch (IOException e)
		{
			return TYPE_FAILED;
		} finally
		{
			try
			{
				if (is != null)
				{
					is.close();
				}
				if (savedFile != null)
				{
					savedFile.close();
				}
			} catch (Exception e)
			{
				e.printStackTrace();
			}
		}

	}

	private long getContentLength(String downloadUrl)
	{
		OkHttpClient client = new OkHttpClient();
		Request request = new Request.Builder().url(downloadUrl).build();
		try
		{
			Response response = client.newCall(request).execute();
			if (response != null && response.isSuccessful())
			{
				long contentLength = response.body().contentLength();
				response.body().close();
				return contentLength;
			}
		} catch (IOException e)
		{
		}
		return 0;
	}

}

猜你喜欢

转载自blog.csdn.net/anod/article/details/82849383