为文件传输分配新字节 [] 时如何防止内存不足错误?

How do I prevent OutOfMemory Error when allocating new byte[] for File transfer?

我正在尝试读取文件以便通过 Wear App 发送它,但我遇到了 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.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;
}

让我们使用 ChannelApi. We also do not have to worry about chunks like I said in my comment. Google thought ahead and made a convenience method for sending files. public abstract PendingResult sendFile (GoogleApiClient client, Uri uri)

来解决这个问题
private String pickBestNodeId(List<Node> nodes) {
    String bestNodeId = null;
    // Find a nearby node or pick one arbitrarily
    for (Node node : nodes) {
        if (node.isNearby()) {
            return node.getId();
         }
         bestNodeId = node.getId();
    }
    return bestNodeId;
}

public boolean send(File f) {
    GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this)
    // Request access only to the Wearable API
        .addApi(Wearable.API)
        .build();
    mGoogleApiClient.blockingConnect();
    Channel channel = openChannel(mGoogleApiClient, pickBestNodeId(Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await()), "/your/arbitrary/application/specific/path/").await(); //NOTE THE PATH IS ARBITRARY, IT CAN BE WHATEVER YOU WANT. IT DOES NOT POINT TO ANYTHING, AND CAN EVEN BE LEFT WITH WHAT I HAVE.
    boolean didSend = channel.sendFile(mGoogleApiClient, f.toURI()).await().isSuccess();
    channel.close(mGoogleApiClient);
    mGoogleApiClient.disconnect();
    return didSend;
}

注意: 这使用了阻塞方法,不应在 ui 线程中 运行。

如果你希望调用是非阻塞的,你应该省略我对 PendingResult.await() 的用法,并设置 PendingResult 的结果回调。可以通过 setResultCallback(ResultCallback callback).

设置回调

我建议您使用 ByteArrayOutputStream。然后从 InputStream 中读取,使用此代码:

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

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

遍历字节