Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java.lang.OutOfMemoryError: Could not allocate JNI Env

When I first time run Async task its works fine. Actually this error comes unpredictably. I search lots of solutions on this issue but nothing is works for me. Common Solution which i got is, we need to close InputStream/ByteArrayInputStream & I closed all but still app gets crash.

Stacktrace:

java.lang.OutOfMemoryError: Could not allocate JNI Env at java.lang.Thread.nativeCreate(Native Method) at java.lang.Thread.start(Thread.java:730) at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:941) at java.util.concurrent.ThreadPoolExecutor.processWorkerExit(ThreadPoolExecutor.java:1009) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1151) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) at java.lang.Thread.run(Thread.java:761)

Following is AsyncTask;

public class AsyncHttpRequest extends AsyncTask<Void, Void, String> {

    private String UrlString = "";
    private boolean _showProgressDialog = false;
    private CustomCircularLoadingDialog Dialog;
    private Context mContext;
    AppCompatActivity mActivity;
    private IHttpRequestCompletedListener listener;
    private boolean isActivity = true;
    private String _messageText = "Please wait..."; 
    private String type = "get";

    private HttpUtility utility;


    public AsyncHttpRequest(String urlString, Context context, IHttpRequestCompletedListener listener, boolean _showProgressDialog) {
        UrlString = urlString;
        this._showProgressDialog = _showProgressDialog;
        this.mContext = context;
        this.listener = listener;
        Dialog = new CustomCircularLoadingDialog(this.mContext);
        this.utility = new HttpUtility(this.UrlString, mContext);
        Utilities.setCurrentHitURL(mContext, UrlString);
    }

    public void setOnCompletedListener(IHttpRequestCompletedListener listener) {
        this.listener = listener;
    }

    public void setWaitMessage(String msgText) {
        if (!msgText.equals(""))
            msgText = "\n" + msgText;
        this._messageText = _messageText + msgText;
    }

    public void addPostItem(NameValuePair nameValuePair) {
        this.utility.addNameValuePairs(nameValuePair);
    }

    public void addGetHeader(String headerData) {
        this.utility.addGetHeader(headerData);
    }

    public void addPostHeader(String headerData) {
        this.utility.addPostHeader(headerData);
    }

    public void setTypePost() {
        this.type = "post";
    }

    @Override
    protected void onPreExecute() {
        if (_showProgressDialog) {
            Dialog.setMessage(this._messageText);
            Dialog.setCancelable(false);
            Dialog.setCanceledOnTouchOutside(false);
            this.Dialog.getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
            Dialog.show();
        }
    }

    @Override
    protected String doInBackground(Void... params) {

        if (!Utilities.isNetworkAvailable(mContext))
            return "No network available";
        try {
            if (this.type.equals("get"))
                return utility.doGetRequest();
            else
                return utility.doPostRequest();
        } catch (MediCorporateException tex) {

            if (listener != null) {
                if (isActivity) {
                    ((Activity) mContext).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            listener.OnHttpRequestError();
                        }
                    });
                } else {
                    ((GcmIntentService) mContext).runOnUIThread(new Runnable() {
                        @Override
                        public void run() {
                            listener.OnHttpRequestError();
                        }
                    });
                }
            }
            Utilities.callCrashReport(mContext, tex);
        }
        Log.i("Exit ", "doInBackground");

        return "";
    }

    @Override
    protected void onPostExecute(String Result) {
        if (_showProgressDialog)
            this.Dialog.dismiss();
        Log.i("Came in", "onPostExecute");
        if (this.listener != null) {
            if (!Utilities.isNullOrEmpty(Result))
                listener.OnHttpRequestCompleted(Result);
            else {
                logoutUser();
                listener.OnHttpRequestError();
            }
        }
        Log.i("Exit ", "onPostExecute");
    }

}

Following are functions to handle request & responses in HttpUtility class;

public String doGetRequest() throws MediCorporateException {

        String resp = "";
        int responseCode = 0;
        try {
            if (header != null) {
                if (header.length() > 0) {
                    httpURLConnection.setRequestMethod("GET");
                    httpURLConnection.setRequestProperty("Authorization", header);
                }
            }
            responseCode = httpURLConnection.getResponseCode();
            InputStream inputStream = new BufferedInputStream(this.httpURLConnection.getInputStream());
            resp = readResponse(inputStream);
            Log.v("Resp", "" + responseCode + " --- " + resp);
            inputStream.close();
        } catch (IOException ioExc) {
            FileLog.e(getClass().getName(), ioExc);
            resp = ioExc.getMessage();
            throw new MediCorporateException("Http IO Exception...");
        } catch (Exception ex) {
            FileLog.e(getClass().getName(), ex);
            throw new MediCorporateException("Http Error...");
        } finally {

            this.httpURLConnection.disconnect();
            if (responseCode == 401)
                return "" + responseCode;
            if (responseCode != 200)
                return null;
        }

        return resp;
    }

Following is DoPostRequest() :

public String doGetRequest() throws MediCorporateException {

        String resp = "";
        int responseCode = 0;
        try {
            if (header != null) {
                if (header.length() > 0) {
                    httpURLConnection.setRequestMethod("GET");
                    httpURLConnection.setRequestProperty("Authorization", header);
                }
            }
            responseCode = httpURLConnection.getResponseCode();
            InputStream inputStream = new BufferedInputStream(this.httpURLConnection.getInputStream());
            resp = readResponse(inputStream);
            Log.v("Resp", "" + responseCode + " --- " + resp);
            inputStream.close();
        } catch (IOException ioExc) {
            FileLog.e(getClass().getName(), ioExc);
            resp = ioExc.getMessage();
            throw new MediCorporateException("Http IO Exception...");
        } catch (Exception ex) {
            FileLog.e(getClass().getName(), ex);
            throw new MediCorporateException("Http Error...");
        } finally {

            this.httpURLConnection.disconnect();
            if (responseCode == 401)
                return "" + responseCode;
            if (responseCode != 200)
                return null;
        }

        return resp;
    }

following is reading & writing response functions;

private void writePostMethod(OutputStream outputStream) throws Exception {
        if (this.nameValuePairs.size() <= 0)
            throw new Exception("Cannot use post method with no values to post");
        String postStr = "";
        for (NameValuePair item : this.nameValuePairs)
            postStr += URLEncoder.encode(item.getName(), "UTF-8") + "=" + URLEncoder.encode(item.getValue(), "UTF-8") + "&";
        postStr = postStr.substring(0, postStr.length() - 1);
        Log.v("Post Values", postStr);
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8"));
        writer.write(postStr);
        writer.flush();
        writer.close();
    }

    private String readResponse(InputStream inputStream) throws IOException {
        int i;
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        while ((i = inputStream.read()) != -1)
            outputStream.write(i);

        byte[] responseBytes = outputStream.toByteArray();
        ByteArrayInputStream bais = new ByteArrayInputStream(responseBytes);
        InputStreamReader reader;
        if (!this._urlString.contains("token")) {
            GZIPInputStream gzis = new GZIPInputStream(bais);
            reader = new InputStreamReader(gzis);
            gzis.close();
        } else
            reader = new InputStreamReader(bais);

        BufferedReader in = new BufferedReader(reader);
        StringBuilder total = new StringBuilder();
        String readed;
        while ((readed = in.readLine()) != null) {
            total.append(readed);
            bais.close();
        }
        in.close();
        reader.close();
        inputStream.close();
        outputStream.close();
        return total.toString();
    }
like image 432
priyanka kamthe Avatar asked Jul 06 '17 05:07

priyanka kamthe


2 Answers

Each thread costs a lot in terms of memory consumption.

AsyncTask does manage its thread pool, but it is not optimized for network activity. Actually, if you have many HTTP requests to the same server, it is better both in terms of memory consumption and overall performance to keep them on the same thread, and reuse a persistent connection, whenever possible. AsyncTask does not consider such issues.

There are quite a few reliable HTTP clients that provide async requests, e.g. OkHttp or volley, or Android Asynchronous Http Client by J.Smith, or many other projects on GitHub.

It is OK to invent your own HTTP client, but at least it would be wise to study what others have done in this field and what mistakes they made before.

like image 121
Alex Cohn Avatar answered Nov 14 '22 21:11

Alex Cohn


Seems like you're not properly closing input/output stream or buffers while reading data from stream. Please try this following snippet for reading response from InputStream.

Use IOUtils of org.apache.commons.io. You can download jar

private String readResponse(InputStream inputStream) throws IOException {
    if (!this._urlString.contains("token")) {
        GZIPInputStream gzipIn = new GZIPInputStream(inputStream);
        return IOUtils.toString(gzipIn);    
    }else {
        if (inputStream != null) {
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(inputStream));
            StringBuilder sb = new StringBuilder();

            String line = null;
            try {
                while ((line = reader.readLine()) != null) {
                    sb.append(line + "\n");
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return sb.toString();
        }
    }
    return null;
}

Though most of OOM are due to heap size allocated to the application. We can increase the heap size by adding tag in manifest file.

 <application
    android:name="ApplicationLoader"
    android:largeHeap="true">

After going through your repo, I have found out that for every exception you're sending logs and crash reports to FireBase. That's leads to out of memory in your Thread Pool which leads to OOM. Please make sure you send crashes and events in batches.

like image 24
Vikalp Patel Avatar answered Nov 14 '22 21:11

Vikalp Patel