无法使用 LibGDX Net 在 Android 上下载 100mb 文件
Posted
技术标签:
【中文标题】无法使用 LibGDX Net 在 Android 上下载 100mb 文件【英文标题】:Can't download a 100mb file on Android with LibGDX Net 【发布时间】:2017-01-24 17:45:08 【问题描述】:我有一个简单的代码,它向我的外部服务器发送 Http 请求以下载大小为 100mb 的 .txt 文件。较小的文件(如 40mb)可以正常工作,但较大的文件存在一些问题。让我给你看一些代码:
Net.HttpRequest request = new Net.HttpRequest(Net.HttpMethods.GET);
request.setTimeOut(2500);
String assetsUrl = "http://111.111.111.111/100mb.txt";
request.setUrl(assetsUrl);
// Send the request, listen for the response
// Asynchronously
Gdx.net.sendHttpRequest(request, new Net.HttpResponseListener()
@Override
public void handleHttpResponse (Net.HttpResponse httpResponse)
InputStream is = httpResponse.getResultAsStream();
OutputStream os = Gdx.files.local("100mb.txt").write(false);
byte[] bytes = new byte[1024];
int count = -1;
try
while ((count = is.read(bytes, 0, bytes.length)) != -1)
os.write(bytes, 0, count);
catch (IOException e)
e.printStackTrace();
还有一些代码可以显示进度,但这并不重要。 问题是文件的大小是 100mb,但是 android 在下载时神奇地分配了 400mb+ 的 RAM 并出现错误:
等待阻塞的 GC Alloc
WaitForGcToComplete 因 Alloc 原因阻塞 12.906 毫秒
启动阻塞 GC Alloc
启动阻塞 GC Alloc
暂停所有线程耗时:35.332ms
Alloc 部分并发标记扫描 GC 释放 214(21KB) AllocSpace 对象,1(200MB) LOS 对象,6% 空闲,216MB/232MB,暂停 1.076ms 总计 130.115ms
暂停所有线程耗时:205.400ms
后台粘性并发标记扫描 GC 释放 364(10KB) AllocSpace objects, 0(0B) LOS objects, 0% free, 416MB/416MB, paused 10.448ms 总计 304.325ms
启动阻塞 GC Alloc 启动阻塞 GC Alloc
Alloc 部分并发标记扫描 GC 释放 113(3KB) AllocSpace objects, 0(0B) LOS objects, 3% free, 416MB/432MB, paused 290us total 17.611ms
启动阻塞 GC Alloc
Alloc 粘性并发标记扫描 GC 释放 31(912B) AllocSpace objects, 0(0B) LOS objects, 3% free, 416MB/432MB, paused 268us total 6.474毫秒
启动阻塞 GC Alloc
Alloc 并发标记扫描 GC 释放了 43(13KB) AllocSpace 对象, 0(0B) 个 LOS 对象,3% 空闲,415MB/431MB,暂停 268us 共 15.008ms
强制为 300MB 分配收集 SoftReferences
启动阻塞 GC Alloc
Alloc 并发标记扫描 GC 释放了 42(1256B) 个 AllocSpace 对象, 0(0B) 个 LOS 对象,3% 空闲,415MB/431MB,暂停 286us 共 12.426ms
抛出 OutOfMemoryError "未能分配 314572860 字节 分配 16770608 个空闲字节和 96MB 直到 OOM"
当我运行下载过程时,我可以在我的设备上看到我分配了 1 GB(按系统)和 600 mb 空闲空间,而应用程序使用 20-30 mb。几秒钟后,我的应用程序开始分配越来越多的内存,我可以看到,它使用了 400mb+ 并且崩溃发生在你在日志中看到的最大值时。
也许我不明白,但它不应该只分配所需的 100mb 的 RAM 来存储数据块吗? 我几乎 100% 确定我的应用程序没有泄漏 - 下载过程正在消耗内存(肯定只调用一次)。
【问题讨论】:
【参考方案1】:你所做的一切看起来都不奇怪,所以我看了一下 LibGDX,尤其是 getResultAsString;最终你会回到 com.badlogic.gdx.utils.StreamUtils.copyStreamToString
public static String copyStreamToString (InputStream input, int estimatedSize, String charset) throws IOException
InputStreamReader reader = charset == null ? new InputStreamReader(input) : new InputStreamReader(input, charset);
StringWriter writer = new StringWriter(Math.max(0, estimatedSize));
char[] buffer = new char[DEFAULT_BUFFER_SIZE];
int charsRead;
while ((charsRead = reader.read(buffer)) != -1)
writer.write(buffer, 0, charsRead);
return writer.toString();
这看起来并不可怕,但我的猜测是 StringWriter 出了问题,导致内部重复重新分配数组。看着this answer 似乎证实了我的怀疑。
StringWriter 在内部写入 StringBuffer。 StringBuffer 基本上是一个 char 数组的包装器。那阵有一定的容量。当该容量不足时,StringBuffer 将分配一个新的更大的 char 数组并复制前一个的内容。最后在 StringWriter 上调用 toString(),它会再次将 char 数组的内容复制到结果 String 的 char 数组中。
因此,简而言之,您可能应该找到另一种下载文件的方法。 This question 可能有一些更强大的解决方案。
【讨论】:
以上是关于无法使用 LibGDX Net 在 Android 上下载 100mb 文件的主要内容,如果未能解决你的问题,请参考以下文章
无法在 Android Studio (libGDX) 中运行项目
在 Android 上使用来自 libGDX 的 SQLite