javaandroidmultithreadingurlconnection

java.lang.NegativeArraySizeException: -1 downloading file on Android with URLConnection


so I've tried to implement https://stackoverflow.com/a/1718140/13592426 this function for downloading file in my app, but I didn't succeed, and want to find out why. Here's the code:

public class Receiver extends BroadcastReceiver {

public static void downloadFile(String url, File outputFile) {
    try {

        URL u = new URL(url);
        URLConnection conn = u.openConnection();
        int contentLength = conn.getContentLength();

        DataInputStream stream = new DataInputStream(u.openStream());

        byte[] buffer = new byte[contentLength];
        stream.readFully(buffer);
        stream.close();

        DataOutputStream fos = new DataOutputStream(new FileOutputStream(outputFile));
        fos.write(buffer);
        fos.flush();
        fos.close();
    } catch(FileNotFoundException e) {
        Log.i("FileNotFoundException", "file not found"); // swallow a 404
    } catch (IOException e) {
        Log.i("IOException", "io exc"); // swallow a 404
    }
}

Handler handler;

@Override
public void onReceive(Context context, Intent intent) {

    final String link = "https://images.app.goo.gl/zjcreNXUrrihcWnD6";
    String path = Environment.getExternalStorageDirectory().toString()+ "/Downloads";
    final File empty_file = new File(path);

    new Thread(new Runnable() {
        @Override
        public void run() {
            downloadFile(link, empty_file);
        }
    }).start();

}

}

And I get these errors:

2020-07-07 14:09:23.142 26313-26371/com.example.downloader E/AndroidRuntime: FATAL EXCEPTION: Thread-2
Process: com.example.downloader, PID: 26313
java.lang.NegativeArraySizeException: -1
    at com.example.downloader.Receiver.downloadFile(Receiver.java:39)
    at com.example.downloader.Receiver$1.run(Receiver.java:66)
    at java.lang.Thread.run(Thread.java:919)

39th line is:

byte[] buffer = new byte[contentLength];

Maybe the main problem is in the wrong usage of Thread.

Honestly, I'm new to Android and quite struggle with solving this issue, maybe you can reccomend some good material or tutorials related to Threads/URLs in Android (I had searched for lot, but it's still difficult). And of course I'll appreciate direct suggestions on what I'm doing wrong.


Solution

  • An HTTP server can send a response with a content length of -1 if it doesn't know ahead of time how large the response will be.

    Instead of allocating a buffer that is the size of the complete file, set it to a reasonable size (for example 8K bytes) and use a loop to stream the file; e.g.

    byte[] buffer = new byte[8192];
    int count;
    while ((count = in.read(buffer)) > 0) {
        out.write(buffer, 0, count);
    }
    

    This approach has a couple of advantages. It works when the content length is -1, and it protects you against OOME problems if the content length is a very large number. (It still makes sense to check the content length though ... to avoid filling the devices file system.)


    I note that your current version has other problems.

    1. You are managing the streams in a way that could lead to file descriptor leaks. I recommend that you learn about ... and use ... JAVA 8 try with resources syntax.

    2. This is dubious:

       Log.i("IOException", "io exc"); // swallow a 404
      

      It is not safe to assume that all IOExceptions caught in that catch block will be due to 404 responses. The comment (at least!) is inaccurate.