I am new to Android. I have an AsyncTask that is downloading the content of a URL.
I didn't want the AsyncTask to manipulate the UI directly and want it to have it as a reusable peice of code so I have put it in a file of its own and it returns a string.
The problem is that the the return happens before the AsyncTask is finished (even though I am using the .get() of the .excecute()), so I get nothing back.
Here is waht I have at the moment:
package com.example.mypackage;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutionException;
import android.os.AsyncTask;
public class URLContent {
private String content = "default value";
public String getContent(String URL){
try {
new getAsyncContent().execute(URL).get();
} catch (InterruptedException e) {
content = e.getMessage();
} catch (ExecutionException e) {
content = e.getMessage();
}
return content;
}
private class getAsyncContent extends AsyncTask<String, Integer, String>
{
#Override
protected void onPostExecute(String result) {
content = result;
}
#Override
protected String doInBackground(String... urls) {
try{
return URLResponse(urls[0]);
} catch (Exception e){
return e.getMessage();
}
}
}
private String IStoString(InputStream stream) throws IOException, UnsupportedEncodingException {
try {
return new java.util.Scanner(stream, "UTF-8").useDelimiter("\\A").next();
} catch (java.util.NoSuchElementException e) {
return "";
}
}
private String URLResponse(String URLToget) throws IOException {
InputStream is = null;
try {
URL url = new URL(URLToget);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("GET");
conn.setDoInput(true);
conn.connect();
is = conn.getInputStream();
// Convert the InputStream into a string
String contentAsString = IStoString(is);
return contentAsString;
} finally {
if (is != null) {
is.close();
}
}
}
}
What would be the best way to solve that so that my main thread somehow gets back the results?
I have come accross some articles mentioning events and callbacks. Is that the best way..?
Don't do get(). Simply have whatever logic deals with result of your URL execution right inside doInBackground method. From which you don't have to return anything. In your particular case the problem is that get() is executed right away before the result is delivered
This is asynchronous execution and you can't apply linear logic to it
Thanks for all your help. I will try the solution for extending the asyncTask class by Alex, sounds like a clean solution.
I managed to do what I was trying to by using an interface in the class with the AsyncTask and adding an event listener on the main class.
So my class that gets the content now looks like this:
package com.example.test;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import android.os.AsyncTask;
public class URLContent {
public void getContent(String URL){
new getAsyncContent().execute(URL);
}
public interface OnContentDownloadedListener{
public abstract void onContentDownloaded(String content);
}
private OnContentDownloadedListener contentDownloadedListener = null;
public void setOnContentDownloadedListener(OnContentDownloadedListener content){
contentDownloadedListener = content;
}
private class getAsyncContent extends AsyncTask<String, Integer, String>
{
#Override
protected void onPostExecute(String result) {
contentDownloadedListener.onContentDownloaded(result);
}
#Override
protected String doInBackground(String... urls) {
try{
return URLResponse(urls[0]);
} catch (Exception e){
return e.getMessage();
}
}
}
private String IStoString(InputStream stream) throws IOException, UnsupportedEncodingException {
try {
return new java.util.Scanner(stream, "UTF-8").useDelimiter("\\A").next();
} catch (java.util.NoSuchElementException e) {
return "";
}
}
private String URLResponse(String URLToget) throws IOException {
InputStream is = null;
try {
URL url = new URL(URLToget);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("GET");
conn.setDoInput(true);
conn.connect();
is = conn.getInputStream();
// Convert the InputStream into a string
String contentAsString = IStoString(is);
return contentAsString;
} finally {
if (is != null) {
is.close();
}
}
}
}
And then in my main class I just have an event listener that handles the update of the UI
urlContent.setOnContentDownloadedListener(new URLContent.OnContentDownloadedListener(){
public void onContentDownloaded(String content){
//update UI or do something with the data
}
Updating UI with the results of AsyncTask is the main purpose of onPostExecute(). To keep the implementation and the way it's used in UI separate, your Activity can extend your getAsyncContent class and override the onPostExecute() method. This method will be executed on UI thread.
If You do not need UI updates from Your asynchronous task, just create a simple thread to do the things. When complete, just send a broadcast.
new Thread(new Runnable() {
#Override
public void run() {
// Do the download here...
....
//
sendBroadcast(some_intent);
}).start();
Related
I have an application with 3 or more AsycTask that are called sequentially. Because these asyntasks are all similar, I created a separated class and it works properly.
Now I would like to add a progress bar in order to show something when these asynctasks ask and process the result...but not work.
My application work as follow:
I open my camera and with ZXing library I decode a qrCode
using HttpRequest I ask to my server some informations and my application processes these informations
The point is that during the processing my application shows a black screen with the tipical viewfinder of ZXing library (I think that you understand what I mean). How can replace this view with another block with a progress bar?
I already tried to modified the progress bar visibility, on the event onPreExecute and onPostExecute, also I tried to use the event onProgressUpdate, but nothig is change. The viewfinder remains on the screen until the asyncTask is not finish.
Follow my code for execute the AsyncTask:
response = asynkTaskDeleteMissionQueue.execute().get();
and my AsyncTask class
package com.klainrobotics.lucalombardi.krmir;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.util.List;
import java.util.Vector;
/**
* Created by Luca Lombardi on 27/11/2017.
*/
public class MiRCall extends AsyncTask <String, Void, List<Object> >{
public AsyncResponse delegate = null;
private String url;
private String method;
private ProgressBar progress;
public MiRCall(Context v, ProgressBar prg, String...params){
method = params[0];
url = params[1];
progress = prg;
}
#Override
protected void onPreExecute(){
}
#Override
protected void onProgressUpdate(Void... v) {
super.onProgressUpdate(v);
}
#Override
protected List<Object> doInBackground(String... arg0) {
int result = -1;
publishProgress();
List<Object> response = new Vector<Object>();
String jsonResponse = "";
BufferedReader br;
try {
URL urlMissionQueue = new URL(url);
HttpURLConnection connection = (HttpURLConnection) urlMissionQueue.openConnection();
if (connection != null) {
connection.setRequestMethod(method);
connection.setRequestProperty(Costanti.headers, Costanti.StringaHeader());
connection.setRequestProperty(Costanti.contentType, Costanti.contentTypeJSon);
customBody(connection);
connection.connect();
result = connection.getResponseCode();
response.add(result);
if (200 <= result && result <= 299) {
br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
} else {
br = new BufferedReader(new InputStreamReader(connection.getErrorStream()));
}
for (String line; (line = br.readLine()) != null; jsonResponse += line);
response.add(jsonResponse);
}else {
Log.e("Url", "Connection is null");
}
} catch (Exception ex) {
Log.e("MiRCall", "doInBackgound: " + ex.toString());
}
return response;
}
#Override
protected void onPostExecute(List<Object> result) {
if (delegate != null) {
delegate.processFinish(result);
} else {
Log.e("MiRCall", "You have not assigned AsyncTask delegate");
}
}
public void customBody(HttpURLConnection connection) throws ProtocolException {
// Do nothing
}
}
Thanks in advance
response = asynkTaskDeleteMissionQueue.execute().get();
Pretty bad to use the .get() function on it. Do away with get(). Only start the task.
asynkTaskDeleteMissionQueue.execute();
Then in onPostExecute() handle the response.
Thanks for your suggestion. I execute
response = asynkTaskDeleteMissionQueue.execute().get();
because my asyncTask returns a list of object, so I don't know another way....
And about the original question? Any suggestions?
Thanks in any case for your time
I'm developing an app and now I have to pass a parameter to a RESTful Service's URL. I'm using AsyncTask, and I need to pass a text from a list view as a parameter to the URL, for example: the URL is http://ip:7001/product?product_name=PARAM I need to get the text from the selected item from my list view, and pass as a parameter in PARAM, using AsyncTask. I've already got the text from the item in the listView, now I just need to pass it as a parameter.
This is my AsycTask class:
package com.tumta.henrique.teste;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import com.tumta.henrique.teste.ProdutoFragment;
/**
* Created by Henrique on 18/05/2015.
*/
public class FiltraProduto extends AsyncTask<String, Void, List<String>> {
private ConsultaConcluidaFiltroProdutoListener listener;
public static String URL_STRING = "http://192.168.0.20:7001/com.henrique.rest/api/v1/status/pro_filtro?pro_nome=";
public FiltraProduto(ConsultaConcluidaFiltroProdutoListener listener) {
this.listener = listener;
}
private List<String> InterpretaResultado(String resultado) throws JSONException {
JSONObject object = new JSONObject(resultado);
JSONArray jsonArray = object.getJSONArray("produto");
//JSONObject jsonProduto = jsonArray.getJSONObject(0);
// String id = jsonProduto.getString("pro_id");
//proId = id;
List<Object> listaNomes = new ArrayList<>();
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonProdutoInfo = jsonArray.getJSONObject(i);
String proNome= jsonProdutoInfo.getString("pro_nome");
double proPreco = jsonProdutoInfo.getDouble("pro_preco");
double proSdAtual = jsonProdutoInfo.getDouble("pro_sdAtual");
listaNomes.add(i, proNome);
listaNomes.add(i, proPreco);
listaNomes.add(i, proSdAtual);
}
List<String> strings = new ArrayList<String>();
for (Object o : listaNomes) {
strings.add(o != null ? o.toString() : null);
}
return strings;
}
private String ConsultaServidor() throws IOException {
InputStream is = null;
try {
URL url = new URL(URL_STRING);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(10000);
conn.setReadTimeout(15000);
conn.setRequestMethod("GET");
conn.setDoInput(true);
conn.connect();
conn.getResponseCode();
is = conn.getInputStream();
Reader reader = null;
reader = new InputStreamReader(is);
char[] buffer = new char[2048];
reader.read(buffer);
return new String(buffer);
} finally {
if (is != null) {
is.close();
}
}
}
#Override
protected List<String> doInBackground(String... params) {
try {
String resultado = ConsultaServidor();
return InterpretaResultado(resultado);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(List<String> result) {
listener.onConsultaConcluida(result);
super.onPostExecute(result);
}
public interface ConsultaConcluidaFiltroProdutoListener {
void onConsultaConcluida(List<String> result);
}
}
In the URL_STRING I need to pass the param at pro_nome=?
Here I get the item text. This is in my Fragment that has the List View:
public String retornaParam(String param){
return param;
}
#Override
public void onConsultaConcluida(List<String> result) {
final ListView listaProdutos = (ListView) getView().findViewById(R.id.listaprodutos);
ArrayAdapter arrayAdapter = new ArrayAdapter<>(getView().getContext(),android.R.layout.simple_list_item_1, result);
listaProdutos.setAdapter(arrayAdapter);
listaProdutos.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parentAdapter, View view, int position,
long id) {
String nomeProduto = listaProdutos.getItemAtPosition(position).toString();
retornaParam(nomeProduto);
Intent intent = new Intent(getActivity(), DetalhesProdutoActivity.class);
//intent.putExtra("pro_nome", listaProdutos.getItemAtPosition(position).toString());
startActivity(intent);
}
});
}
I get the text and store it in param from the retornaParam method.
Does somebody know how to do it?
If you need more information, just let me know.
You pass in params to an AsyncTask using:
YourAsyncTask.execute(yourview.getText(), "and", "more", "params");
You can then access them in
#Override
protected String doInBackground(String... params) {
URL_STRING += params[0];
...
Just add the following code before sending executing your httpClient:
URL_STRING + = textInsideYourTextView;
It should work, just avoid to manipulate your ui elements outside your UI thread.
I recover my text file distant, my text contains number one "1". I tried to convert my text "1"(char)to int, but it is giving error. I used method Integer.parseInt(String)
this is my code:
MainActivity.java
package mypackage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import android.app.Activity;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends Activity {
PackageInfo pinfo;
String contentFichier;
TextView txt;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recoverContentTextFile();
txt = (TextView) findViewById(R.id.txt);
try {
pinfo = getPackageManager().getPackageInfo(getPackageName(), 0);
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Here error
int i = Integer.parseInt(contentFichier);
}
public void recoverContentTextFile() {
new Thread() {
#Override
public void run() {
String path ="my_url_text_file";
URL u = null;
try {
u = new URL(path);
HttpURLConnection c = (HttpURLConnection) u.openConnection();
c.setRequestMethod("GET");
c.connect();
InputStream in = c.getInputStream();
final ByteArrayOutputStream bo = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
in.read(buffer); // Read from Buffer.
bo.write(buffer); // Write Into Buffer.
runOnUiThread(new Runnable() {
#Override
public void run() {
contentFichier = bo.toString();
try {
bo.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
thank you in advance.
First of all it's not a good idea at all to use threads in the way you're implementing in your method recoverContextTextFile. What happens if the user rotate the device and the petition takes 8 minutes to complete? You have created a memory leak!
The second thing as you have created a thread the variable contentFichier will be sometimes null (because recoverContextTextFile does create a thread) and calling the Integer.parseInt(contentFichier);will raise an Exception.
For this case I think that it's better to use an AsyncTask (which I highly dislike and taking care of not leaking the activity when rotation occurs), do the petition in the onBackground method and in the method onPostExecute call the Integer.parseInt(contentFichier);.
I recommend reading this tutorial by Lars Vogel as it explains a lot about background processing.
The problem here is probably that you are trying to convert the String before the thread has finished. And also, Android has a better way than Threads to handle most (simple) background tasks, the AsyncTask. You could do something like this:
public class MainActivity extends Activity {
PackageInfo pinfo;
String contentFichier;
TextView txt;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Setup your activity here
new ContentFetcher().execute("http://......");
}
private class ContentFetcher extends AsyncTask<String, Void, String>{
#Override
protected String doInBackground(String... params) {
String stringResponse = null;
try{
HttpResponse httpResponse = new DefaultHttpClient().execute(new HttpGet(params[0]));
stringResponse = EntityUtils.toString(httpResponse.getEntity());
} catch (IOException ignored) {
}
return stringResponse;
}
#Override
protected void onPostExecute(String s) {
//DO something with the response here - maybe test if the s variable is indeed an integer
int i = Integer.parseInt(s);
}
}
}
To execute the task run:
new ContentFetcher().execute("http://whatever.com/mytext.txt");
Add log statement to show the value of the contentFichier:
Log.i("contentFichier", "["+contentFichier+"]"
You will see which content is being parsed. Maybe there is a whitespace on the beginning or the end. Also the server may be returning wrong value or empty string.
I'm trying to develop a simple application that reads rss feeds from a certain URL and then displays the results in a list view.
Here is my rss reader, which is the main thing in the app:
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
public class RssReader {
private String title = null;
private String link = null;
private String description = null;
private ArrayList<RssItem> posts = new ArrayList<RssItem>();
private Thread thread;
private String urlString = null;
private XmlPullParserFactory xmlFactoryObject;
public volatile boolean parsingComplete = true;
public RssReader(String url) {
this.urlString = url;
}
public boolean getParsingComplete() {
return this.parsingComplete;
}
public ArrayList<RssItem> getPosts() {
return posts;
}
public void parseXML(XmlPullParser parser) {
int event;
try {
event = parser.getEventType();
while (event != XmlPullParser.END_DOCUMENT) {
String name = parser.getName();
switch (event) {
case XmlPullParser.START_TAG:
break;
case XmlPullParser.END_TAG:
if (name.equals("title")) {
title = parser.getText();
} else if (name.equals("link")) {
link = parser.getText();
} else if (name.equals("description")) {
description = parser.getText();
}
break;
}
if(title != null && link != null && description != null) {
RssItem item = new RssItem(this.title,this.description,this.link);
posts.add(item);
this.title = this.description = this.link = null;
}
event = parser.next();
}
parsingComplete = false;
} catch (Exception e) {
e.printStackTrace();
}
}
public void fetchXML() {
thread = new Thread(new Runnable() {
#Override
public void run() {
try {
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("GET");
conn.setDoInput(true);
// Starts the query
conn.connect();
InputStream stream = conn.getInputStream();
xmlFactoryObject = XmlPullParserFactory.newInstance();
XmlPullParser myparser = xmlFactoryObject.newPullParser();
myparser.setFeature(
XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
myparser.setInput(stream, null);
parseXML(myparser);
stream.close();
} catch (Exception e) {
} finally {
parsingComplete = true;
}
}
});
thread.start();
}
And here is my MainActivity:
package com.example.ynetrssproject;
import java.util.ArrayList;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.widget.ListView;
import android.os.Bundle;
public class MainActivity extends ActionBarActivity {
private ListView news;
private String rssUrl = "http://www.themarker.com/cmlink/1.144";
private ArrayList<RssItem> list;
private RssItemAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
news = (ListView) findViewById(R.id.news);
RssReader reader = new RssReader(rssUrl);
reader.fetchXML();
while(true) {
Log.d("Runnning", "Run");
if(reader.getParsingComplete()) {
list = reader.getPosts();
break;
}
}
adapter = new RssItemAdapter(this, R.layout.post_item_list, list);
news.setAdapter(adapter);
}
}
The problem is that everytime I call fetchXML, eventually it returns me an empty ArrayList. Therefore, my listview keeps being empty.
My adapter isn't such a big deal. It works fine. The problem is that I keep getting an empty array list from the object RssReader. I know this because I performed a little if statement at the end of the code just to check if the ArrayList is empty.
P.S I have tried with multiple RSS urls but none of them works. Also, I added the permission of Internet in my manifest.
What's wrong with my code?
Your approach is a classic java approach. With android, you should use an IntentService or an AsyncTask that perform the RSS-fetch and then sends the read items to your activity.
http://developer.android.com/reference/android/app/IntentService.html
http://developer.android.com/reference/android/os/AsyncTask.html
Don't use while(true) to poll if the reader is ready.
Be aware that, when the user rotates the screen, onCreate is executed again.
For some reason the onPreExecute isn't being called. Code:
protected void onPreExcecute() {
hook.createDialog(ticker);
}
Entire class:
package com.evandarwin.finance;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import android.content.Context;
import android.os.AsyncTask;
import android.widget.Toast;
public class GetTicker extends AsyncTask<String, Integer, String>{
private Context ctx;
private String ticker;
private SimpleFinanceActivity hook;
public GetTicker(Context ctx, String ticker, SimpleFinanceActivity hook) {
this.ctx = ctx;
this.ticker = ticker.toUpperCase();
this.hook = hook;
}
protected void onPreExcecute() {
hook.createDialog(ticker);
}
#SuppressWarnings("unused")
#Override
protected String doInBackground(String... params) {
try {
URL url = new URL("http://finance.yahoo.com/d/quotes.csv?s="+ticker+"&f=a");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(true);
urlConnection.connect();
InputStream is = urlConnection.getInputStream();
StringBuffer str = new StringBuffer();
int bufferLength = 0; //used to store a temporary size of the buffer
byte[] stream = new byte[1024];
while ( (bufferLength = is.read(stream)) > 0 ) {
str.append(stream);
}
return str.toString();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
return null;
}
protected void onPostExecute(String result) {
hook.destroyDialog();
Toast.makeText(ctx, result, Toast.LENGTH_LONG).show();
}
}
This is giving me a NullPointerException, I know I've had this problem before but I don't remember what I did to fix it. Please help! :P
The reason the #Override is causing a problem in Eclipse (and the reason the method isn't being called) is that you have made a typing error.
You are calling it onPreExcecute (note the 'c' after the 'x' shouldn't be there). Correct it to be onPreExecute and use #Override for that.