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();
}
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With