I want to ask if this that looks like an issue to me is problem.
I have a class of AsyncTask to get data from a json file and a doInBackground method with pre-execute and post-execute methods.
At onCreate method of my MainActivity I call the class of AsyncTask with name.execute(). The problem is that the program stuck into the post execute method, is that a problem? There is a way to return to the OnCreate method or should I continue with my code from post execute method?
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new LoadAllProducts().execute();
txtView=(TextView) findViewById(R.id.txtV);
}
class LoadAllProducts extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(MainActivity.this);
pDialog.setMessage("Loading questions. Please wait...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(false);
pDialog.show();
}
/**
* getting All products from url
*/
protected String doInBackground(String... args) {
// Building Parameters
List<NameValuePair> params = new ArrayList<NameValuePair>();
// getting JSON string from URL
JSONObject json = jParser.makeHttpRequest(url_all_products, "GET", params);
// Check your log cat for JSON reponse
Log.d("All questions: ", json.toString());
try {
// Checking for SUCCESS TAG
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
// products found
// Getting Array of Products
questions = json.getJSONArray(TAG_QUESTIONS);
// looping through All Products
for (int i = 0; i < questions.length(); i++) {
JSONObject c = questions.getJSONObject(i);
// Storing each json item in variable
int id = c.getInt(TAG_QID);
String questionPron = c.getString(TAG_QUESTION);
String answer1 = c.getString(TAG_ANSWER_1);
String answer2 = c.getString(TAG_ANSWER_2);
String answer3 = c.getString(TAG_ANSWER_3);
String answer4 = c.getString(TAG_ANSWER_4);
int level = c.getInt(TAG_LEVEL);
int correctIs = c.getInt(TAG_CORRECT_IS);
// String updatedAt = c.getString(TAG_UPDATED_AT);
dokimi = questionPron;
// creating new HashMap
HashMap<String, String> map = new HashMap<String, String>();
//ArrayList<eachQuestion> qArray = new ArrayList<eachQuestion>();
eachQuestion ea = new eachQuestion();
ea.setId(id);
ea.setQuestionPron(questionPron);
ea.setAnswer1(answer1);
ea.setAnswer2(answer2);
ea.setAnswer3(answer3);
ea.setAnswer4(answer4);
ea.setLevel(level);
ea.setCorrectIs(correctIs);
// adding each child node to HashMap key => value
//map.put(TAG_PID, id);
//map.put(TAG_NAME, name);
qArray.add(ea);
}
}
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(String file_url) {
// dismiss the dialog after getting all products
pDialog.dismiss();
// updating UI from Background Thread
runOnUiThread(new Runnable() {
public void run() {
}
});
}
}
"The problem is that the program stuck into the post execute method, is that a problem?" I can only guess what that was supposed to mean but I will try to answer your question. The reason why AsyncTask even exists is that its code is run on a separate thread (cpu). The main thread (cpu) is making another cpu execute given code.
That is why method call of execute() returns almost immediately, most probably even before any line of given code for another cpu executes. You are not able to predict when exactly (if ever) this code will execute. This depends on your OS's scheduler and current runtime session, in computer science we describe such behaviour as undeterministic.
Related
In my android application, I am trying to get an artist's top ten tracks. I'm using a wrapper for the Spotify Web API. To get the artist's top ten tracks you have to provide the getArtistTopTrack(id) method with the artist's id.
Here's my code that is meant to do that:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_top_ten);
Intent intent = getIntent();
String artistID = "";
artistID = intent.getExtras().getString("ID");
FetchTopTenTracksTask topTenTracksTask = new FetchTopTenTracksTask();
topTenTracksTask.execute(artistID);
}
private class FetchTopTenTracksTask extends AsyncTask<String, Void, Tracks> {
#Override
protected Tracks doInBackground(String... params) {
SpotifyApi api = new SpotifyApi();
SpotifyService spotify = api.getService();
return spotify.getArtistTopTrack(params[0]);
}
The error that I am getting is
E/AndroidRuntime(2882): FATAL EXCEPTION: AsyncTask #2
E/AndroidRuntime(2882): java.lang.RuntimeException: An error occured while executing doInBackground()
and it was caused by doInBackground. My thoughts are the bad request is caused by this line of code because I need to specify a country code for the search (the API requires this) but I don't know how to.
return spotify.getArtistTopTrack(params[0]);
The correct endpoint for getting an artist's top tracks is
GET https://api.spotify.com/v1/artists/{the artists id}/top-tracks?country={ISO 3166-1 alpha 2 country code}
But when I checked the LogCat to see what endpoint it was using it turned to be an incorrect one
D/Retrofit(2882): <--- HTTP 400 https://api.spotify.com/v1/artists/{the artist id}%3Fcountry%3DIE/top-tracks (59ms)
As you can see, the country code which is "IE" is appended to the artist id, like this: "%3Fcountry%3DIE". This is what is causing the error, because the request could not be understood by the server, due to malformed syntax.
Can someone show me how I can append "?country={country code}" to the endpoint, instead of having it right after the artist id?
To provide a solution to my own question:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_top_ten);
listView = (ListView) findViewById(R.id.listView);
Intent intent = getIntent();
String artistID = "";
artistID = intent.getExtras().getString("ID");
FetchTopTenTracksTask topTenTracksTask = new FetchTopTenTracksTask();
topTenTracksTask.execute(artistID);
}
private class FetchTopTenTracksTask extends AsyncTask<String, Void, Tracks> {
#Override
protected Tracks doInBackground(String... params) {
try {
String artistID = params[0];
Map<String, Object> options = new Hashtable<String, Object>();
options.put("country", "IE"); //Can be any country code here
SpotifyApi api = new SpotifyApi();
SpotifyService spotify = api.getService();
return spotify.getArtistTopTrack(artistID, options);
}
catch (RetrofitError retrofitError) {
Log.d("Retrofit Error: ", retrofitError.toString());
}
return null;
}
Here is where I got the solution.
You can use,
import com.neovisionaries.i18n.CountryCode;
try{
....
CountryCode country = CountryCode.DE;
GetArtistsTopTracksRequest request = SpotifyApi.getInstance().getArtistsTopTracks(artistId, country).build();
I'm a Newbie, so, there are a lot of things that I don't know.
I'm building my first application, using PHP and Java, and my
Database is allocated on Phpmyadmin.
To get the data that I want, I have to create a PHP Query, and output
everything using Json. To get the Json Data I use Java.
I have been searching about my question for hours, and unfortunately,
I couldn't find a specific way to what I want, in my situation. I
would like to update my ListView automatically when a new record is
inserted into my Database.
Lets imagine that you are watching a Listview, and when the owner
from the application inserts a new record on the table that shows the
Listview data, automatically the Listview shows a new record.
My problem is that I am using Json to get the Data, and I can't
change that. And I would like to find a solution for that.
So, I would like to know if it is possible or not to do that. I will
post my full code here:
Json Tags - Calling the URL and the Json Tags here:
public class ListUsersActivity extends ListActivity {
private ProgressDialog pDialog;
private ImageView img;
// URL to get contacts JSON
private static String url = "http://192.168.1.67/example/db_config.php";
// JSON Node names
private static final String TAG_CONTACTS = "contacts";
private static final String TAG_ID = "id";
private static final String TAG_NAME = "name";
private static final String TAG_IMAGE = "pic_url";
private static final String TAG_USERID = "user_id";
private static final String TAG_BIRTH_DATE = "birth_date";
// contacts JSONArray
JSONArray contacts = null;
// Hashmap for ListView
ArrayList<HashMap<String, String>> contactList;
OnCreate - Calling the Asynctask function, and creating a new arraylist for my Contact list
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lobby);
contactList = new ArrayList<HashMap<String, String>>();
ListView lv = getListView();
// Calling async task to get json
new GetContacts().execute();
}
Asynctask - Getting all the Data json
private class GetContacts extends AsyncTask {
#Override
protected void onPreExecute() {
super.onPreExecute();
// Showing progress dialog
pDialog = new ProgressDialog(ListUsersActivity.this);
pDialog.setMessage("Please wait...");
pDialog.setCancelable(false);
pDialog.show();
}
#Override
protected Void doInBackground(Void... arg0) {
// Creating service handler class instance
NewSession app = (NewSession) getApplication();
String username = app.getUsername();
ServiceHandler sh = new ServiceHandler();
// Making a request to url and getting response
String jsonStr = sh.makeServiceCall(url + "?id=" + username, ServiceHandler.GET);
Log.d("Response: ", "> " + jsonStr);
if (jsonStr != null) {
try {
JSONObject jsonObj = new JSONObject(jsonStr);
// Getting JSON Array node
contacts = jsonObj.getJSONArray(TAG_CONTACTS);
// looping through All Contacts
for (int i = 0; i < contacts.length(); i++) {
JSONObject c = contacts.getJSONObject(i);
String id = c.getString(TAG_ID);
String name = c.getString(TAG_NAME);
String user_id = c.getString(TAG_USERID);
String birth_date = c.getString(TAG_BIRTH_DATE);
String pic_url_get = c.getString(TAG_IMAGE);
HashMap<String, String> contact = new HashMap<String, String>();
// adding each child node to HashMap key => value
contact.put(TAG_ID, id);
contact.put(TAG_USERID, user_id);
contact.put(TAG_BIRTH_DATE, getAge(birth_date) + " anos");
contact.put(TAG_IMAGE, "http://*****/images/" + user_id + "/" + pic_url_get);
// adding contact to contact list
contactList.add(contact);
}
} catch (JSONException e) {
e.printStackTrace();
}
} else {
Log.e("ServiceHandler", "Couldn't get any data from the url");
}
return null;
}
Calling the adapter
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// Dismiss the progress dialog
if (pDialog.isShowing())
pDialog.dismiss();
/**
* Updating parsed JSON data into ListView
* */
costumeadapter adapter = new costumeadapter(
ListUsersActivity.this,
contactList,
R.layout.list_row,
new String[]{ TAG_NAME, TAG_BIRTH_DATE, TAG_USERID},
new int[]{ R.id.name,R.id.email, R.id.id });
setListAdapter(adapter);
}
}
I tried so far
To build a Timer and refresh my GetContacts calling class every
second, but it doesn't work because the user is not able to scroll, and the application crashes
I tried to use notifyDataSetChanged but I didn't work because I dont know exactly how can i implement that.
I hope you guys can help me.
Thanks.
Of course it is possible.
To build a Timer and refresh my GetContacts calling class every
second, but it doesn't work because the user is not able to scroll,
and the application crashes
Because you're doing it on the main thread. Do it in an AsyncTask or a separate thread, that should work.
I tried to use notifyDataSetChanged but I didn't work because I dont
know exactly how can i implement that.
It's simple, you call it in the adapter: adapter.notifydatasetchanged() after you update it (more detail here).
Just use
adapter.notifydatasetchanged()
You have to add it after setListAdapter() like:
setListAdapter(adapter);
adapter.notifydatasetchanged();
AsyncTask question
I've followed some tutorials but it still isn't clear to me. Here's the code I currently have with some questions below the code. MainActivity calls SomeClassWithHTTPNeeds, which then calls the JSONParser (AsyncTask<>)
MainActivity:
String station = SomeClassWithHTTPNeeds.getInstance().getStation(123);
SomeClassWithHTTPNeeds:
getStation {
JSONParser = new JSONParser();
JSONObject station = parser.getJSONFromUrl("https://api....");
return JSONObject.getString("station");
}
JSONParser (AsyncTask< String, Void, String >)
protected String doInBackground(); ==> Seperate thread
protected void onPostExecute(); ==> On GUI thread
I was thinking:
--- Put the HTTPRequest in doInBackground();
Problem is I'm not sure how to:
get the JSONParser to return the JSONObject to the getStation method?
What I need to know
=> Where should I return the JSONObject: in background or execute?
=> How do I use the JSONParser once it's an AsyncTask? Will the execute() function return the value?
=> AsyncTask< String, Void, String > ==> How does this work? It's the return type?
Thanks a lot!
FAQs and general explaination of the usage of AsyncTask
=> Where should I do network operations? Where should I return my aquired values?
In general, you should do Network Operations in a Seperate Thread -> doInBackground(); since you do not want your UI to freeze when a Network Operation takes its time. So you should connect to your Service or .php script or wherever you get the Data from inside the doInBackground() method. Then you could also parse the data there and return the parsed data from the doInBackground() method by specifying the return type of doInBackground() to your desires, more about that down there. The onPostExecute() method will then receive your returned values from doInBackground() and represent them using the UI.
=> AsyncTask< String, Integer, Long> ==> How does this work?
In general, the AsyncTask class looks like this, which is nothing more than a generic class with 3 different generic types:
AsyncTask<Params, Progress, Result>
You can specify the type of Parameter the AsyncTask takes, the Type of the Progress indicator and the type of the result (the return type
of doInBackGround()).
Here is an Example of an AsyncTask looking like this:
AsyncTask<String, Integer, Long>
We have type String for the Parameters, Type Integer for the Progress and Type Long for the Result (return type of doInBackground()). You can use any type you want for Params, Progress and Result.
private class DownloadFilesTask extends AsyncTask<String, Integer, Long> {
// these Strings / or String are / is the parameters of the task, that can be handed over via the excecute(params) method of AsyncTask
protected Long doInBackground(String... params) {
String param1 = params[0];
String param2 = params[1];
// and so on...
// do something with the parameters...
// be careful, this can easily result in a ArrayIndexOutOfBounds exception
// if you try to access more parameters than you handed over
long someLong;
int someInt;
// do something here with params
// the params could for example contain an url and you could download stuff using this url here
// the Integer variable is used for progress
publishProgress(someInt);
// once the data is downloaded (for example JSON data)
// parse the data and return it to the onPostExecute() method
// in this example the return data is simply a long value
// this could also be a list of your custom-objects, ...
return someLong;
}
// this is called whenever you call puhlishProgress(Integer), for example when updating a progressbar when downloading stuff
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
// the onPostexecute method receives the return type of doInBackGround()
protected void onPostExecute(Long result) {
// do something with the result, for example display the received Data in a ListView
// in this case, "result" would contain the "someLong" variable returned by doInBackground();
}
}
=> How to use AsyncTask? How can I "call" it? How can I "execute" it?
In this case, the AsyncTask takes a String or String Array as a Parameter which will look like this once the AsyncTask is called: (The specified parameter is used in the execute(param) method of AsyncTask).
new DownloadFilesTask().execute("Somestring"); // some String as param
Be aware, that this call does not have a return value, the only return value you should use is the one returned from doInBackground(). Use the onPostExecute() method do make use of the returned value.
Also be careful with this line of code: (this execution will actually have a return value)
long myLong = new DownloadFilesTask().execute("somestring").get();
The .get() call causes the UI thread to be blocked (so the UI freezes if the operation takes longer than a few millisecons) while the AsyncTask is executing, because the execution does not take place in a separate thread. If you remove the call to .get() it will perform asynchronously.
=> What does this notation "execute(String... params)" mean?
This is a method with a so called "varargs" (variable arguments) parameter. To keep it simple, I will just say that it means that the actual number of values you can pass on to the method via this parameter is not specified, and any amount of values you hand to the method will be treated as an array inside the method. So this call could for example look like this:
execute("param1");
but it could however also look like this:
execute("param1", "param2");
or even more parameters. Assuming we are still talking about AsyncTask, the parameters can be accessed in this way in the doInBackground(String... params) method:
protected Long doInBackground(String... params) {
String str1 = params[0];
String str2 = params[1]; // be careful here, you can easily get an ArrayOutOfBoundsException
// do other stuff
}
You can read more about AsyncTask here: http://developer.android.com/reference/android/os/AsyncTask.html
Also take a look at this AsyncTask example: https://stackoverflow.com/a/9671602/1590502
package com.example.jsontest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.zip.GZIPInputStream;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONObject;
import android.util.Log;
public class HttpClient {
private static final String TAG = "HttpClient";
public static JSONObject SendHttpPost(String URL, JSONObject jsonObjSend) {
try {
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpPost httpPostRequest = new HttpPost(URL);
StringEntity se;
se = new StringEntity(jsonObjSend.toString());
httpPostRequest.setEntity(se);
httpPostRequest.setHeader("Accept", "application/json");
httpPostRequest.setHeader("Content-type", "application/json");
httpPostRequest.setHeader("Accept-Encoding", "gzip");
long t = System.currentTimeMillis();
HttpResponse response = (HttpResponse) httpclient.execute(httpPostRequest);
Log.i(TAG, "HTTPResponse received in [" + (System.currentTimeMillis()-t) + "ms]");
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
Header contentEncoding = response.getFirstHeader("Content-Encoding");
if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
instream = new GZIPInputStream(instream);
}
String resultString= convertStreamToString(instream);
instream.close();
resultString = resultString.substring(0,resultString.length()-1);
JSONObject jsonObjRecv = new JSONObject(resultString);
Log.i(TAG,"<JSONObject>\n"+jsonObjRecv.toString()+"\n</JSONObject>");
return jsonObjRecv;
}
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
private static String convertStreamToString(InputStream is) {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
Log.e("JSON", ""+line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
}
Asynctask:
public class callCarWeb extends AsyncTask {
#Override
protected void onPreExecute() {
mDialog = new ProgressDialog(MainActivity.this);
mDialog.setMessage("Please wait...");
mDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mDialog.setIndeterminate(true);
mDialog.setCancelable(false);
mDialog.show();
}
#Override
protected Void doInBackground(Void... params) {
try {
JSONObject jsonObjSend = new JSONObject();
jsonObjSend.put("username", username);
jsonObjSend.put("password", passwd);
Log.e("SEND", jsonObjSend.toString());
JSONObject json = HttpClient.SendHttpPost("http://10.0.2.2/json/login.php", jsonObjSend);
String status = json.getString("status");
if(status.equalsIgnoreCase("pass")){
String id = json.getString("user_id");
Log.e("id", id);
String name = json.getString("name");
Log.e("name", name);
}
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
mDialog.cancel();
}
}
## Heading ##
I think you can execute your HTTPRequest in your doInBackground of the Async task. And JSONParser at onPostExecute.
do read some generics.
now, when you write your async task JSONParser here params is of type String, progress is of type Void and result is of type String. Read this.
generally people overrides two methods doInBackground() and onPostExecute(), the first one takes params and returns a result and second one takes that result. These are protected methods you can't call em directly. Then you might ask how to send param to doInBackground(), look at execute() API.
doInBackground() runs on background thread, its not a blocking call!!
don't do this,
JSONParser = new JSONParser();
JSONObject station = parser.getJSONFromUrl("https://api....");
return JSONObject.getString("station");
instead write on interface in JSONParser or somewhere else like,
public interface OnParseCompleteListener {
void onParseComplete(JSONObject obj);
}
now your JSONParser class will something like,
public class JSONParser extends AsyncTask<String, Void, String> {
private OnParseCompleteListener mOnParseCompleteListener;
public void setOnParseCompleteListener(OnParseCompleteListener listener) {
mOnParseCompleteListener = listener;
}
protected String doInBackground(String... params) {
/*
* do http request and return a result
*/
}
protected void onPostExecute(String... result) {
/*
* parse the resulting json string or you can parse same string in
* doInBackground and can send JSONObject as a result directly.
* at this stage say you have a JSONObject obj, follow
*/
if (mOnParseCompleteListener != null) {
mOnParseCompleteListener.onParseComplete(obj);
}
}
}
when you create an object of JSONParser set OnParseCompleteListener.
JSONParser parser = new JSONParser();
parser.setOnParseCompleteListener(listener);
parse.execute("may be an url");
now you decide from where to pass or create your own listener.
So I'm trying to make a youtube application by retrieving the json data from a youtube channel. Problem is I'm great at writing single-threaded applications. But when it comes to multithreading I always lose my insight.
I haven't learned it in school yet and most tutorials are rubbish, or atleast I find that.
So what I want to do is introduce a thread to my "GetVideos" class so it doesn't slow down my application while retrieving the videos. I know I'll have to use a handler and thread but everytime I try to use them my application crashes. Can you guys help?
public class GetVideos { //class that retrieves JSON data based on youtube username
private String channelName;
private HttpClient client; //client that gets info
private VideoLibrary lib;
public GetVideos(String channelName) {
this.channelName = channelName;
client = new DefaultHttpClient();
lib = new VideoLibrary();
fillData();
}
private void fillData() {
try {
final String URL = "http://gdata.youtube.com/feeds/api/users/" + channelName + "/uploads?v=2&alt=jsonc";
HttpGet get = new HttpGet(URL);
HttpResponse response = client.execute(get);
String jsonString = EntityUtils.toString(response.getEntity());
JSONObject json = new JSONObject(jsonString);
JSONArray jsonArray = json.getJSONObject("data").getJSONArray("items");
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject items = jsonArray.getJSONObject(i);
String title = items.getString("title");
String thumbUrl = items.getJSONObject("thumbnail").getString("sqDefault");
String url = items.getJSONObject("player").getString("default");
lib.addVideo(new Video(title, url, thumbUrl));
}
} catch (JSONException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
} catch (ClientProtocolException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
} catch (IOException e) {
e.printStackTrace();
}
}
public VideoLibrary getLib() {
return lib;
}
}
public class SxePhil extends Activity { //class that makes up the interactive end of
//the app, here the JSON data is put in a list
private ListView list;
private GetVideos g;
private VideoLibrary videoLibrary;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sxephil);
list = (ListView) findViewById(R.id.sxephilList);
g = new GetVideos("sxephil");
videoLibrary = g.getLib();
ArrayList<String> titles = new ArrayList<String>();
for (int i = 0; i < 25; i++) {
titles.add(videoLibrary.getVideos().get(i).getTitle());
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.simplerow, titles);
list.setAdapter(adapter);
}
}
Basically what I tried was implementing the threading that is used here: http://blog.blundell-apps.com/show-youtube-user-videos-in-a-listview/
in my code, because this project was built on an older sdk, so I wanted to modernize it so to speak. Look at the GetYoutube*** class and MainActivity class that's where the money is
You should either use an AsyncTask or even better, a Loader, they automatically handle the treads for you
First : basic advices (that you MUST remeber and follow)
Never do processing in a class constructor (that's bad OO design, imho)
Never, ever leave time-consuming process in Activity.onCreate method (and in general, in any method that is runned by the UI Thread, ie. onResume(), onClick(), ...)
mario and runor49 were right : you should use an AsyncTask here (and you must read and understand http://developer.android.com/guide/components/processes-and-threads.html#WorkerThreads. If you don't, read it again...)
Basically, you should do the following :
onCreate must launch a separate thread, which will contain the "long" processing (load the data from network)
When done, this thread must call the Activity.runOnUiThread(Runnable), where the runnable will create the Adapter and assign it to the ListView.
Here is a code sample :
#Override
public void onCreate(Bundle savedInstanceState) {
...
loadDataInThread();
}
private void loadDataInThread() {
new Runnable() {
#Override
public void run() {
// LOAD DATA FROM NETWORK
displayDataInUIThread(datas);
}
}.start();
}
private void displayDataInUIThread(final VideoLibrary data) {
runOnUiThread(new Runnable() {
#Override
public void run() {
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.simplerow, data);
list.setAdapter(adapter);
}
});
}
This is a very basic (and rude) way to do it, and the AsyncTask way () way will be far better, but it should be working and fill the contract...
You can accomplish this most easily using an AsyncTask. Check out this tutorial.
http://android-developers.blogspot.com/2009/05/painless-threading.html
I am creating an android application which has to execute web requests in the background and then handle the received data and modify the user interface according to the server response.
The goal of posting requests and handling data in the background is to avoid the freezing of user interface. Currently however I notice that the user interface is freezing so I am not sure the logic is working as it is supposed to.
Here is the part of code which is supposed to post requests and handle responses in its own thread and then pass the data to GUI:
public class ServerConnection {
Queue<String> requests;
...
DefaultHttpClient httpClient;
HttpHost targetHost;
Handler handler;
ServerResponseHandler responseHandler;
Activity activity;
public ServerConnection(Activity activity){
this.activity = activity;
this.responseHandler = (ServerResponseHandler) activity;
httpClient = new DefaultHttpClient();
targetHost = new HttpHost(TARGET_DOMAIN, 80, "http");
requests = new LinkedList<String>();
}
private Runnable requestSender = new Runnable(){
#Override
public void run() {
if(!requests.isEmpty()){
String requestString = requests.remove();
HttpGet httpGet = new HttpGet(requestString);
httpGet.addHeader("Accept", "text/xml");
String encodingString = "testuser:testpass";
String sEncodedString = Base64Coder.encodeString(encodingString);
try{
String sContent = fetchURL(requestString, sEncodedString);
XMLParser xmlParser = new XMLParser();
List <Product> products = xmlParser.getProducts(sContent);
responseHandler.onProductsResponse(products);
}
catch(Exception ex){
Log.e(TAG, ex.getMessage());
}
}
}
};
public void sendRequest(String requestString){
requests.add(requestString);
handler = new Handler();
handler.post(requestSender);
}
The method sendRequest() is called from the main activity which implements ServerResponseHandler. I guess the request is executed in its own thread and by calling
responseHandler.onProductsResponse(products);
the list of products (data from the web) is passed to main activity. Anyway due to poor performance I would appreciate if anyone could correct any possible issue in the logic above or suggest any other (better) option.
I'd suggest you take a look at ASyncTask class (available since Android 1.5).
It simplifies the process of creating a background Thread that synchronizes with the GUI thread once it's complete.
You should be able to achieve what you're trying using code something list this
private class DownloadFilesTask extends AsyncTask<String, List<Product>, Integer> {
protected List<Products> doInBackground(String... requestStrings) {
int count = requestStrings.length;
int results = 0;
for (int i = 0; i < count; i++) {
String requestString = requestStrings[i];
HttpGet httpGet = new HttpGet(requestString);
httpGet.addHeader("Accept", "text/xml");
String encodingString = "testuser:testpass";
String sEncodedString = Base64Coder.encodeString(encodingString);
try{
String sContent = fetchURL(requestString, sEncodedString);
XMLParser xmlParser = new XMLParser();
List <Product> products = xmlParser.getProducts(sContent);
results++;
publishProgress(products);
}
catch(Exception ex){
Log.e(TAG, ex.getMessage());
}
}
return results;
}
protected void onProgressUpdate(Integer... progress) {
// TODO You are on the GUI thread, and the first element in
// the progress parameter contains the last progress
// published from doInBackground, so update your GUI
}
protected void onPostExecute(int result) {
// Processing is complete, result contains the number of
// results you processed
}
}
And execute by calling
new DownloadFilesTask().execute(url1, url2, url3);
According to the handler javadoc, I don't think the post() method create any threads. If I'm right it execute the Runnable on the thread to which the handler is attached. So in this case this is the activity thread so the UI thread ! That's why you have poor performance.
You have to implement a Thread which execute your Runnable. But by doing that, you won't be able to update your activity like you currently do by calling :
responseHandler.onProductsResponse(products);
This is because you are not any more in the UI thread, and only the UI thread is authorized to interact with the UI (so the activity).
So you have to replace this call by accessing your Handler.
Message msg = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putSerializable( "products", products ); //not sure it will pass here
msg.setData( bundle );
handler.sendMessage( msg );
And implementing the handleMessage() method for your Handler :
#Override
public void handleMessage( Message msg )
{
List <Product> products = msg.getData().getSerializable( "products" );
responseHandler.onProductsResponse(products);
}
Last but not least : the Handler has to still be created in the activity thread.