General AsyncTask, use one AsyncTask in multiple contexts - android

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

Related

Memory Leak with AsyncTask

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

Finish procedure before starting a new one

I am beginner in Android and I need some help. So, I have a procedure with sub-procedures inside. How can I finish one before starting a new one. Here is a code to better understand:
public void onCellLocationChanged(CellLocation lokacija) {
super.onCellLocationChanged(lokacija);
location = (GsmCellLocation) Phone.getCellLocation();
textCellId.setText(String.valueOf(location.getCid() % 65536));
textCellLac.setText(String.valueOf(location.getLac()));
String JSON_URL_string=JSON_URL + "?cellid=" + String.valueOf(location.getCid()%65636);
getJSON(JSON_URL_string);
myJSONString = textCellNameSakriven.getText().toString();
ParseJSON(myJSONString);
}
Problem is that myJSONString is empty, cause textCEllNameSkriven is also empty. That textView textCellNameSkriven is made when getJSON(JSON_URL_string) is finished. If I run debugger and go step by step, app goes directly from getJSON(JSON_URL_string) row to the next one and the next etc
Edit: Maybe the problem is that onPostExecute is not finished before starting ParseJSON. Here is also a code for getJSON:
private void getJSON(String url) {
class GetJSON extends AsyncTask<String, Void, String> {
ProgressDialog loading;
#Override
protected void onPreExecute() {
super.onPreExecute();
loading = ProgressDialog.show(MainActivity.this, "Please Wait...", null, true, true);
}
#Override
protected String doInBackground(String... params) {
String uri = params[0];
BufferedReader bufferedReader = null;
try {
URL url = new URL(uri);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
StringBuilder sb = new StringBuilder();
bufferedReader = new BufferedReader(new InputStreamReader(con.getInputStream()));
String json;
while ((json = bufferedReader.readLine()) != null) {
sb.append(json + "\n");
}
return sb.toString().trim();
} catch (Exception e) {
return null;
}
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
loading.dismiss();
textCellNameSakriven.setText(s);
}
}
GetJSON gj = new GetJSON();
gj.execute(url);
}
Since you are running a async task it practically runs on a different thread, so your getJson method need not wait for post execute and can return after starting the async task, so you can never be sure in this way that parseJson gets executed after textView is populated. You are running into classic race condition issue.
Your issue could be easily solved, if you have a callback which is called after postExecute is done, and you can handle parseJson there
So, something simple like , create interface MyCallback
public interface MyCallback {
public OnReadJsonDone();
}
Let your activity implement this MyCallback
public MainActivity implements MyCallback
{
...........
#Override
public OnReadJsonDone(){
parseJson();
}
Now change signature of getJson to
getJSON(string json, final MyCallback callback) {
Now in onpostexecute
//call OnReadJsonDone
callback.OnReadJsonDone()
So, all you now need is while calling getJSon pass this as second param
getJSON(JSON_URL_string,this);
Haven't tested this but you get the idea

Android Newbie : couldn't connect to OpenWeatherMap properly

I am following the tutorials from Tutplus and youtube on Android WeatherApp creation. Please find my code below. I am trying to connect to OpenWeatherMap and get JSON weather data. Here are my problems:
Its not working.
Is this the right way to create the URL for accessing OpenWeatherMap.
When I registered with the OpenWeatherMap, it gave me a KEY. I am not sure what to do with that. I used it in my code, after getting the httpurlconnection from the server for setting "x-api-key". Don't know if its needed or am doing it
when i get the inputstream, the reader is null and the application hangs after that.
Here is the code:
public class WeatherGrabber {
private static final String TAG = "WeatherGrabber";
private static final String CURRENT_WEATHER_URL =
"http://api.openweathermap.org/data/2.5/weather?q=%s&mode=json";
private static BufferedReader reader;
private static final String my_key = "307ec986e69c22c9a24a1bcf9edd21ea";
public static String loadCurrentWeather(Context context, String city) {
String data = null;
try{
URL web_url = new URL(String.format(CURRENT_WEATHER_URL, city) );
HttpURLConnection conn = (HttpURLConnection)
web_url.openConnection();
conn.addRequestProperty("x-api-key", my_key);
reader = new BufferedReader(new
InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuffer json = new StringBuffer();
while((inputLine = reader.readLine() )!= null){
json.append(inputLine).append("\n");
}
data = json.toString();
}catch (IOException e){
e.printStackTrace();
}finally {
try {
if (in != null)
in.close();
}catch (Exception e) {
e.printStackTrace();
}
}
return data;
}// end of loadCurrentWeather() method
}
You can't run network requests directly on the main thread. You need to use AsyncTask class to do this.
Create a class in you activity
private class LoadCurrentWeatherAsync extends AsyncTask<Void, Void, String>{
#Override
protected String doInBackground(Void... params) {
try {
//Call to your function here
return loadCurrentWeather(MainActivity.this, "newyork");
} catch (Exception e){
return e.toString();
}
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
//Do what ever you what with this output
Log.d("data", s);
}
}
Then call it inside your activity onCreate method
new LoadCurrentWeatherAsync().execute();
Also make sure that you have enabled the internet permissions in AndroidManifest.xml . Inside the manifest tag insert
<uses-permission android:name="android.permission.INTERNET" />
It's ok
They use the api key to track whether request is from free or paid plan.
I suspect that's because you are sending the network request on main thread. Try the above code

Android: Use AsyncTask in multiple Activitys

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........

Android httpurlconnection and basicauthentication

I'm having a really tough nut to crack with a bug. Api being used is v11, honeycomb 3.0
I have a asynctask inside a fragment downloading from a XML api with basic authentication. It works perfectly even when i change the parameters from the fragment within with the edittexts etc. But when i try to mutate a autocompletetextview from outside the fragment, suddenly i get a "no element at line 1. column 0" exception. I tried the androidhttpclient, fiddled with systemprop(http.keepalive), and completly narrowed it down to this method.
public void setStations(String a, String b){
AutoCompleteTextView fromET = (AutoCompleteTextView ) getView().findViewById(R.id.from);
fromET.setText(a);
AutoCompleteTextView toET = (AutoCompleteTextView) getView().findViewById(R.id.to);
toET.setText(b);
}
When this method executes it botches up my downloadtask somewhere. If i manually edit these textview it works fine.
class LoadDataTask extends AsyncTask<String, Integer, ArrayList<Reisadvies>> {
private Exception ex;
private ProgressDialog pd;
protected void onPreExecute() {
//loadprogressdialog
}
protected ArrayList<Reisadvies> doInBackground(String... params) {
try{
ex = null;
return new APIreader().getRA(params[0], params[1], params[2],params[3],params[4],params[5], params[6]);
}catch (Exception e){
cancel(true);
pd.dismiss();
ex = e;
return null;
}
}
protected void onPostExecute(ArrayList<Reisadvies> ra){
//send list to activity
}
protected void onCancelled() {
super.onCancelled();
showError(ex);
}
}
};
public ArrayList<Reisadvies> getRA(String fromStation, String toStation, String viaStation, String dateTime, String departure, String hslAllowed, String yearCard) throws APIException{
try{
String uri = url(fromStation, toStation, viaStation, dateTime, departure, hslAllowed,yearCard);
URL url = new URL(uri);
HttpURLConnection uc = (HttpURLConnection) url.openConnection();
if (!url.getHost().equals(uc.getURL().getHost())) {
throw new APIException("HotspotForwadingActive");
}
String basicAuth = "Basic " + "username:password"; //base64 encoded
uc.setRequestProperty ("Authorization", basicAuth);
uc.connect();
BufferedReader in = new BufferedReader(new InputStreamReader(uc.getInputStream(), "UTF-8"));
try{
return (ArrayList<Reisadvies>) new XMLParser().parseRP(in);
}finally{
uc.connect();
}
}catch (Exception e){
e.printStackTrace();
throw new APIException(e.getMessage());
}
}
I think there is a problem in doInBackground:
pd.dismiss();
You can do operations on UI element only in UI Thread. It means that you can do this in onPostExecute method, or, if you want, you can use runOnUiThread method:
runOnUiThread(new Runnable() {
public void run() {
pd.dismiss();
}
});
I hope this is helpful...
You are right about that too, but the problem was different. Just found out that it was to urlencoding. Should have figured that out right away but was throw off by the fact that it worked sometimes with a space in it :)

Categories

Resources