i appear to have a memory leak with the following asyncTask, what possible cause are there?
/**
* Async task class to get json by making HTTP call
* */
public class PostLocation extends AsyncTask<String, Void, String>
{
#Override
protected void onPreExecute()
{
}
#Override
protected String doInBackground(String... params)
{
try
{
String response = "";
URL url = new URL(BASE_URL + "receiveLocation.php");
myConnection = (HttpURLConnection) url.openConnection();
myConnection.setRequestMethod("POST");
myConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(myConnection.getOutputStream());
wr.writeBytes(params[0]);
int responseCode = myConnection.getResponseCode();
if (responseCode == HttpsURLConnection.HTTP_OK)
{
String line;
BufferedReader br = new BufferedReader(new InputStreamReader(myConnection.getInputStream()));
while ((line = br.readLine()) != null)
{
response += line;
}
}
else
{
return response = "";
}
return response;
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
return "false";
}
}
#Override
protected void onPostExecute(String result)
{
if(result.equals("success"))
{
myConnection.disconnect();
}
}
}
this is simply posting some data to my server. What could be causing memory leaks, note this task is called in the background from a broadcast receiver using goAsync
The problem here is in your class structure.
A nested class should be declared static whenever possible, otherwise it will keep the enclosing class in memory, leading to memory leaks.
Change:
public class PostLocation extends AsyncTask<String, Void, String>
to:
public static class PostLocation extends AsyncTask<String, Void, String>
Here is an exercise:
Put a field called boolean test in your BroadcastReceiver class
Now in your nested class, try to access this field, e.g. test == true
If the nested class is not static, you will be able to access the field. This is because the instance of the class keeps the reference to the enclosing class. But if you make PostLocation class static, the field will not be available.
Yes you need to close the inputstream first and close httpconnection next. As per javadoc.
Each HttpURLConnection instance is used to make a single request but
the underlying network connection to the HTTP server may be
transparently shared by other instances. Calling the close() methods
on the InputStream or OutputStream of an HttpURLConnection after a
request may free network resources associated with this instance but
has no effect on any shared persistent connection. Calling the
disconnect() method may close the underlying socket if a persistent
connection is otherwise idle at that time.
Source
Related
I'm a new Android developer and I have a question. After users login, I need to get some data from an external URL and display them on one of my activities. But I've been reading and found this:
When your activity comes back to the foreground from the stopped state, it receives a call to onRestart(). The system also calls the
onStart() method, which happens every time your activity becomes
visible (whether being restarted or created for the first time).
And this
Caution: Your activity will be destroyed and recreated each time the
user rotates the screen. When the screen changes orientation, the
system destroys and recreates the foreground activity because the
screen configuration has changed and your activity might need to load
alternative resources (such as the layout).
Here and here.
So, it looks like I should not get the data I need on the onCreate method. Then where? In a previous activity and saving the data in the phone memory? That doesn't sound good to me.
Thanks in advance.
edit
I'm using AsyncTask as suggested, but everytime I switch the phone orientation, onCreate method is called.
My MainActivity:
public class MainActivity extends Activity implements AsyncResponse {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ReadJSONTask jsonTask = new ReadJSONTask();
jsonTask.delegate = this;
jsonTask.execute("http://www.myweb.com/myscript.php");
}
#Override
public void processFinish(String output) {
Toast.makeText(getApplicationContext(), output, Toast.LENGTH_LONG).show();
}
}
ReadJSONTask class:
public class ReadJSONTask extends AsyncTask<String, Void, String> {
public AsyncResponse delegate = null;
public String res;
public Boolean finish = false;
#Override
protected String doInBackground(String... url) {
String response = null;
String adres = url[0];
URL url_HTTPRequest = null;
try {
url_HTTPRequest = new URL(adres);
response = transfer(url_HTTPRequest);
} catch (MalformedURLException e) {
Log.e("URL ERROR", "MalformedURLException");
} catch (Exception e) {
Log.e("URL ERROR", "exc");
}
return response;
}
#Override
protected void onPostExecute(String result) {
Log.d("mylog", "result= " + result);
delegate.processFinish(result);
}
public String transfer(URL url) throws IOException {
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
// inputStream = url.openStream();
InputStream inputStream = urlConnection.getInputStream();
BufferedReader bin = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
String line = bin.readLine();
StringBuffer sb = new StringBuffer();
while (line != null) {
sb.append(line);
sb.append("\r\n");
line = bin.readLine();
}
inputStream.close();
return sb.toString();
}
}
And the interface is just:
public interface AsyncResponse {
void processFinish(String output);
}
As I said, everytime I switch the phone orientation all the async process is performed (and the Toast shows up). That's exactly what I wanted to avoid in the first place.
you dont need Stop/Resume activities you can use AsyncTask class and doInBackGround method when get data from external url and show to user process Dialog for waiting
Never ever try to get the data from URL on Main thread. Always use AsyncTask for getting the data from URL
You can write in the same activity, but not in the main thread. Maybe AsyncTask will help.
I am currently wondering how to use AsyncTask in multiple Activitys without copying it.
I used this Guide to do it in one Activity and that worked just fine. But to load and use this information in more than one Activity seems to me like to much work. I tried to put my LoadUrl function into another Class and just pass my Textfield I want to be edited. But my App crashes when i start it. (I am not sure if this is the right approach )
public class LoadFromUrl {
public void loadAccountInfo(String key) {
if( key != null ) {
new DownloadWebpageTask().execute();
}
}
private class DownloadWebpageTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
// params comes from the execute() call: params[0] is the url.
try {
return downloadUrl(urls[0]);
} catch (IOException e) {
return "Unable to retrieve web page. URL may be invalid.";
}
}
// onPostExecute displays the results of the AsyncTask.
#Override
protected void onPostExecute(String result) {
//textView.setText(result);
}
// Given a URL, establishes an HttpUrlConnection and retrieves
// the web page content as a InputStream, which it returns as
// a string.
private String downloadUrl(String myurl) throws IOException {
InputStream is = null;
// Only display the first 500 characters of the retrieved
// web page content.
int len = 500;
try {
URL url = new URL(myurl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("GET");
conn.setDoInput(true);
// Starts the query
conn.connect();
int response = conn.getResponseCode();
//Log.d(DEBUG_TAG, "The response is: " + response);
is = conn.getInputStream();
// Convert the InputStream into a string
String contentAsString = readIt(is, len);
return contentAsString;
// Makes sure that the InputStream is closed after the app is
// finished using it.
} finally {
if (is != null) {
is.close();
}
}
}
// Reads an InputStream and converts it to a String.
public String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException {
Reader reader = null;
reader = new InputStreamReader(stream, "UTF-8");
char[] buffer = new char[len];
reader.read(buffer);
return new String(buffer);
}
}
}
and calling it in:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
selectedAccount.setKey("google.com");
loadUrl.loadAccountInfo(selectedAccount.getKey());
}
All I want to do is, load the information of an URL and use this to fill my activitys (like multiple TextViews). Every activity uses different urls and structures.
Create DownloadWebPageTask in a separate file, as a public class. Then override its constructor to pass anything you need (a textfield, key, etc).
Put DownloadWebPageTask in a separate class. Then in onPostExecute, run a callback to the activity or fragment that will update its UI. This is done by having an activity implement a callback which is an inner interface inside the the DownloadWebpageTask (doesn't have to be an inner interface!). As you can see, the inner interface I put with your code is WebpageCallbacks.
This is your asynctask in another class (the spacing isn't perfect sorry...):
public class DownloadWebpageTask extends AsyncTask<String, Void, String> {
/**
* Any activity or fragment that implements WebPageCallbacks
*/
private WebPageCallbacks callbacks;
//start by referencing your activity to call onURLLoaded() for onPostExecute()
public DownloadWebpageTask(WebPageCallbacks callbacks) {
this.callbacks = callbacks; //note: I think weak references are preferred though
}
//no changes here
#Override
protected String doInBackground(String... urls) {
// params comes from the execute() call: params[0] is the url.
try {
return downloadUrl(urls[0]);
} catch (IOException e) {
return "Unable to retrieve web page. URL may be invalid.";
}
}
// onPostExecute displays the results of the AsyncTask by callback's onURLLoaded()
#Override
protected void onPostExecute(String result) {
//each activity or fragment will has a method to change their UI
callbacks.onURLLoaded(result);
}
// Given a URL, establishes an HttpUrlConnection and retrieves
// the web page content as a InputStream, which it returns as
// a string. No changes here
private String downloadUrl(String myurl) throws IOException {
InputStream is = null;
// Only display the first 500 characters of the retrieved
// web page content.
int len = 500;
try {
URL url = new URL(myurl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("GET");
conn.setDoInput(true);
// Starts the query
conn.connect();
int response = conn.getResponseCode();
//Log.d(DEBUG_TAG, "The response is: " + response);
is = conn.getInputStream();
// Convert the InputStream into a string
String contentAsString = readIt(is, len);
return contentAsString;
// Makes sure that the InputStream is closed after the app is
// finished using it.
} finally {
if (is != null) {
is.close();
}
}
/**
* Any Activity or fragment that implements this will have
* onURLLoaded() method to update its own UI.
*/
public interface WebpageCallbacks {
void onURLLoaded(String result);
}
}
I would then add implements DownloadWebpageTask.WebpageCallBacks to all your fragments and activities that will use this asynctask.
Here is your activity:
public class ExampleActivity extends AppCompatActivity implements DownloadWebpageTask.WebpageCallBacks {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
selectedAccount.setKey("google.com");
//changed your oncreate at line below to run your accountInfo
runAccountInfo(selectedAccount.getKey());
}
.......................
//runs the asynctask to load url info from account info like your old loadURLInfo()
public void runAccountInfo(String key) {
if( key != null ) {
//get url with getURL(key)
new DownloadWebpageTask(this).execute(getURL(key));
}
}
//this will be run from onPostExecute from the asynctask
#Override
public void onURLLoaded(String result) {
textView.setText(result);
}
}
If you have the time though, I suggest to not use AsyncTask at all and look into other libraries like rxJava/rxAndroid. I hope this code is ok........
public String urltobody(String createdurl){
URL url = new URL (createdurl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
int responseCode = connection.getResponseCode();//fails here
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream is = connection.getInputStream();
String content = convertInputStream(is, "UTF-8");
is.close();
return content;
}
}
The above function works if I call it inside of doInBackground but does not workin onPostExecute. I really need to call this function independent to pre, background, post prdocedure.
This is my class definition private class FetchTask extends AsyncTask < Void, Void, String>
Please let me know if you can help me with one of the below questions.
Do you know how I can call my function outside of doInbackground?
Do you know what happens when I call new FetchTask().execute()?
Any other solution?
Thank you in advance
The reason is that doInBackground is run in a background thread while onPreExectute onPostExecute are run in the UI-thread.
==> Url Connections should not be done from UI thread, (because it may freeze the UI while it is loading) that is why Android throws an NetworkOnMainThreadException whenever you try to do that.
==> This is the reason why you should establish network connections only asynchronously from the background thread not UI thread. That is why it only works in doInBackground.
You can try this code
private class FetchTask extends AsyncTask<Void, Void, Boolean>
instead of
private class FetchTask extenends AsyncTask < Void, Void, String>
May be solve your problem.
also for example for your understanding
private class ResponseLocDepts extends AsyncTask<Void, Void, Boolean> {
#Override
protected Boolean doInBackground(Void... params) {
URL url = new URL (createdurl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
int responseCode = connection.getResponseCode();//fails here
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream is = connection.getInputStream();
String content = convertInputStream(is, "UTF-8");
is.close();
return true;
}
else
{
return false;
}
}
#Override
protected void onPostExecute(final Boolean success) {
// somethings
}
}
String content should be globally diclare.
Please try it.
I'm using AsyncTask and all the examples I found about an AsyncTask is inside an activity.
I'm trying to make an application with a lot of activity and some must download a HTML page. I don't really want to copy-paste the same code all the time in every activity. I find this to be dirty.
So I need to do it as a special class Async HttpGet and pass the function with an argument. I will execute after the doinbackground (different for every activity).
Is this possible or do I need to copy-paste my code in every activity and change the do in background to do what I need after downloading the HTML page?
Here's an AsyncTask that will download data from a url and update the calling activity.
Make sure your calling activity implements the interface DownloadDataTask.DownloadCompleteHandler and that it passes itself as parameter to the DownloadDataTask constructor.
public class DownloadDataTask extends AsyncTask<String, Integer, String> {
public interface DownloadCompleteHandler
{
public void handleDownloadComplete(String result);
}
private DownloadCompleteHandler handler;
private String url;
public DownloadDataTask(DownloadCompleteHandler handler, String url) {
this.handler = handler;
this.url = url;
}
/* AsyncTask methods */
#Override
protected String[] doInBackground(String... empty) {
return downloadData(url);
}
#Override
protected void onPostExecute(String result) {
handler.handleDownloadComplete(result);
}
/* Downloading Data */
private String downloadData(String urlStr) {
InputStream is = null;
String result = new String();
try {
is = getInputStream(urlStr);
BufferedReader in = new BufferedReader(new InputStreamReader(is));
String inputLine;
while ((inputLine = in.readLine()) != null) {
result += inputLine;
} catch (MalformedURLException ex) {
return "Malformed URL: " + ex.getMessage();
} catch (SocketTimeoutException ex) {
return "Connection timed out";
} catch (IOException ex) {
return "IOException: " + ex.getMessage();
}
finally {
if (is != null)
is.close();
}
return result;
}
private InputStream getInputStream(String urlStr) throws IOException
{
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(7000);
conn.setConnectTimeout(7000);
conn.setRequestMethod("GET");
conn.setDoInput(true);
conn.connect();
return conn.getInputStream();
}
}
Just create a class that extends AsyncTask that you can reuse.
public abstract class MyAsyncTask extends AsyncTask<Void, Void, String> {
private final String url;
public MyAsyncTask(String url){
this.url = url;
}
#Override
protected String doInBackground(Void... params){
// get data from url.
return null;
}
}
And then to call it, just create an instance of that class.
new MyAsyncTask("http://www.google.com"){
public void onPostExecute(String result){
// update your views.
}
}.execute();
Well what you can do is create an listener for AsyncTask completion, which listens when your AsyncTask is completed and return you the data. I had created an example to execute database queries in background thread and then returning the data to the Activity. Just check it and you can create similar AsyncTask for your problem.
UPDATE:-
Also you can use BroadCastReceiver as a Listener when your AsyncTask is completed and return the value.
Interface is another option for creating a Listener for AsyncTask.
Here is a demo from my github
I'm trying to use the HTTPClient to get the html code of a website using an AsyncTask and passing back the code to my main Activity.
I used this code in order to (try to) do that :
public class AsyncTaskGet extends AsyncTask<String, String, String>{
String result;
InputStream in;
protected String doInBackground(String... params) {
try
{
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpRequest = new HttpGet("http://www.google.fr");
HttpResponse response = httpClient.execute(httpRequest);
try{
BufferedReader reader = new BufferedReader(new InputStreamReader(null, result), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
if (response != null) {
in = response.getEntity().getContent();
}
in.close();
result = sb.toString();
}catch (Exception e) {
Log.e("log_tag", "Error converting result " + e.toString());
}
}catch(IOException e){
e.printStackTrace();
}
return result;
}
#Override
protected void onPostExecute(String result) {
//Here is one of my problem, I don't know how to get the returned value result as a string.
}
}
This is my AsyncTaskGet, since I tried lots of fruitless attempts, I erased all my code in the main activity except this part.
new AsyncTaskGet().execute();
So basically, What i'd like to do is getting the result back on my main activity as a string to display it in a TextView.
For now I want my app to be simple so I can understand the basics of using AsyncTasks, I didn't check for internet conectivity or stuff like that, I add the permission on the manifest to use internet (obviously
(Please be a little indulgent since it is one of my first apps)
One last thing, please excuse my poor english, i'm unfortunately not a native english speaker as you must have seen.
In advance, thanks a lot !
Sincerely :)
The easiest thing is to pass the calling activity via a constructor to your AsyncTask and create a method that will take the result as a parameter in the activity itself.
private Activity mActivity;
public AsyncTaskGet(Activity activity) {
mActivity = activity;
}
protected void onPostExecute(String result) {
mActivity.takeItBack(result);
}
There are some limitations: the current code just works with a 1 to 1 relationship between AsyncTask and Activity, means the private member variable type must be the activity from which you call it. If you don't want that you need to implement an interface:
// interface
public interface AsyncTaskCallback {
void takeItBack(String result);
}
// activity
public MyActivity implements AsyncTaskCallback {
#Override
public void takeItBack(String result) {
// do something
}
}
// AsyncTask
public class AsyncTaskGet extends AsyncTask<String, String, String> {
private AsyncTaskCallback mCallback;
public AsyncTaskGet(AsyncTaskCallback callback) {
mCallback= callback;
}
protected void onPostExecute(String result) {
mCallback.takeItBack(result);
}
}
You are already passing result to onPostExecute() as a String. If your AsyncTask is an inner class of your Activity then you can set result to a field variable in your Activity. Otherwise, you can call a function in your Activity from onPostExecute() to do what you want with the data.
Also, your poor English is really not bad at all :)