Java 在为文件传输分配新字节[]时,如何防止OutOfMemory错误?

Java 在为文件传输分配新字节[]时,如何防止OutOfMemory错误?,java,out-of-memory,wear-os,android-wear-data-api,Java,Out Of Memory,Wear Os,Android Wear Data Api,我正试图读取一个文件,以便通过Wear应用程序发送它,但我遇到了OutOfMemory异常 File file = new File(filePath); final FileInputStream fileInputStream = new FileInputStream(file); byte fileContent[] = new byte[(int) file.length()]; //***BOMBS HERE*** fileInputStream.read(fileContent);

我正试图读取一个文件,以便通过Wear应用程序发送它,但我遇到了OutOfMemory异常

File file = new File(filePath);
final FileInputStream fileInputStream = new FileInputStream(file);
byte fileContent[] = new byte[(int) file.length()]; //***BOMBS HERE***
fileInputStream.read(fileContent);
fileInputStream.close();
Asset programDataAsset = Asset.createFromBytes(fileContent);
例外情况如下:

java.lang.OutOfMemoryError: Failed to allocate a 31150467 byte allocation with 2097152 free bytes and 16MB until OOM
       at com.rithmio.coach.wear.TransferService.sendAssetToMobile(TransferService.java:110)
       at com.rithmio.coach.wear.TransferService.onHandleIntent(TransferService.java:84)
       at com.rithmio.coach.wear.TransferService$1.run(TransferService.java:60)
       at java.lang.Thread.run(Thread.java:818)

您应该使用缓冲区,而不是试图将整个文件存储为数组。将部分文件读入字节数组,然后将其写入流。这也将防止内存较少的设备出现OOM错误

int fileLength = (int) file.length();
while(fileLength > 0){
    byte[] arr;
    if(fileLength > 1024){
        arr = new byte[1024];
    }else{
        arr = new byte[fileLength];
    fileInputStream.read(arr);
    // write to outputStream/file transfer
    fileLength -= arr.length;
}

让我们使用。我们也不必像我在评论中所说的那样担心大块。谷歌超前思考,提出了一种方便的发送文件的方法

私有字符串pickBestNodeId(列表节点){
字符串bestNodeId=null;
//查找附近的节点或任意拾取一个节点
用于(节点:节点){
if(node.isnear()){
返回node.getId();
}
bestNodeId=node.getId();
}
返回最佳节点ID;
}
公共布尔发送(文件f){
GoogleAppClient mgoogleAppClient=新的GoogleAppClient.Builder(此)
//仅请求访问可穿戴API
.addApi(可穿戴的.API)
.build();
mGoogleApiClient.blockingConnect();
Channel Channel=openChannel(mGoogleApiClient,pickBestNodeId(Wearable.NodeApi.getConnectedNodes(mGoogleApiClient.wait()),“/your/arbitral/application/specific/path/”)。wait();//注意路径是任意的,可以是任何你想要的。它不指向任何东西,甚至可以保留我拥有的东西。
boolean didSend=channel.sendFile(mgoogleapclient,f.toURI()).await().issucess();
频道关闭(mGoogleApiClient);
mGoogleApiClient.disconnect();
返回didSend;
}
注意:这使用阻塞方法,不应在ui线程中运行


如果希望调用是非阻塞的,则应省略我对
pendingreult.await()
的使用,并将结果回调设置为
pendingreult
。可以通过设置回调。

我建议您使用
ByteArrayOutputStream
。然后,要读取
输入流
,请使用以下代码:

ByteArrayOutputStream stream = new ByteArrayOutputStream();
int b;
while((b = input_stream.read()) != -1) {
    stream.write(b);
}

然后,您可以使用
for(byte b:stream.toByteArray()){…}

迭代字节。解决方案的可能重复之处是以固定大小的块进行读写。如果他只是增加堆呢?他并不是在试图发送1GB的数据,30MB并不是那么多。我通常发现,如果你的程序需要增加jvm内存分配,你最好使用另一种解决方案。毕竟,这是一个便携性问题——特别是对于可穿戴设备。谁知道消费者真正拥有多少内存?更不用说,这可能是在堆空间有限的移动设备上。你有没有一个例子?我编辑了我的答案。更具体地说,您需要提供有关文件传输的更多详细信息。此代码不是正确的读取循环。你不应该试图猜测文件的长度。当你阅读时,它可能会改变。必须在返回值为正值时读取,并且必须将该返回值存储到变量中。否则,不可能使用正确读取的数据。目前,您假设每次读取都返回1024个“是”。它没有被指定这样做,上一次它肯定不会这样做,除非文件长度是1024的倍数。处理程序将在文件上打开,所以它不应该更改。但是如果它这样做了,它将导致一个与OP的原始代码相同的问题,因此没有理由认为文件会更改。关键是当前编写的复制循环不会遇到这个问题。您的新代码仍然存在这个问题,它仍然存在我提到的第二个问题,并且到处都是字节数组。正确的方法已经在这里发布了数百次。这是四行代码加上两个声明。
ByteArrayOutputStream stream = new ByteArrayOutputStream();
int b;
while((b = input_stream.read()) != -1) {
    stream.write(b);
}