Search code examples
javaout-of-memorywear-osandroid-wear-data-api

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


I am trying to read a file in order to send it via Wear App, but I get an OutOfMemory exception.

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);

The exception states the following:

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)

Solution

  • Let's solve this using the 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;
    }
    

    NOTE: this uses blocking methods, and should not be run in the ui thread.

    If you would like the calls to be nonblocking, you should omit my usage of PendingResult.await(), and set the result callback of PendingResult. The callback can be set via setResultCallback(ResultCallback callback).