This question already has answers here:
AsyncTask Android example
(21 answers)
Closed 9 years ago.
I have a splash screen with a TextView to displays what the app is doing such as "Updating Library"... "Updating Shipping"... etc. I'm using AsyncTask to updated my database via an API.
I'm passing the update text to the AsyncTask. I need to change the text in TextView statusMessage. I'm attempting to do this:
public class JSONParser extends AsyncTask<String, String, JSONObject> {
static InputStream is = null;
static JSONObject json = null;
static String outPut = "";
TextView statusMessage;
#Override
protected void onPreExecute() {
statusMessage = (TextView) findViewById(R.id.statusMessage);
}
...
My plan is to change the text in the doInBackground method but findViewById isn't accessible in AsyncTask. I think I need to use setContentView to allow findViewById to work but I'm not sure how.
My java file is SplashScreen.java and my xml is activity_splash_screen.xml
----- EDIT -----
For more info I have three pieces talking to each other:
SplashScreen.java -> calls to method in baseActivity.java -> method sends data to JSONParser.java -> sends parsed JSON from the API to baseActivity.java to update database
Per suggestions below I've declared
statusMessage = (TextView) findViewById(R.id.statusMessage);
In baseActivity.java's onCreate since it's the file calling the AsyncTask.
In JSONParser.java I've done this, now:
public class JSONParser extends AsyncTask<String, String, JSONObject> {
static InputStream is = null;
static JSONObject json = null;
static String outPut = "";
TextView statusMessage;
#Override
protected JSONObject doInBackground(String... params) {
// TODO Auto-generated method stub
...
}
protected void onProgressUpdate() {
statusMessage.setText("testing");
}
protected void onPostExecute(JSONObject result) {
}
}
I'm just using "testing" there for testing purposes.
My plan is to change the text in the doInBackground
Bad plan! You can't update the UI from a background Thread. You will need to do this in either onPostExecute() or onProgressUpdate().
but findViewById isn't accessible in AsyncTask.
If this is an inner class of your Activity then initialize the View in the Activity then update it in your task as described above.
If it is its own file then you will want to use an interface and have a callback to the Acitivty in onPostExecute(), onPreExecute(), or onProgressUpdate(). You can see an example of that in this SO answer.
I think I need to use setContentView to allow findViewById
Definitely! But as stated above, do this before the task such as in onCreate() of your Activity.
Edit
onProgressUpdate() takes a param but your onProgressUpdate() doesn't so it isn't the same method. That's why it complained when you had #Override which is the point of the annotation. It complains and you know you are suppose to be overriding a method so you know something is wrong with it.
Change it to
protected void onProgressUpdate(Void...values) {
statusMessage.setText("testing");
}
onProgressUpdate() link
http://developer.android.com/reference/android/os/AsyncTask.html#onProgressUpdate(Progress...)
You should use onProgressUpdate, that method has acces to the ui thread.
public class yourAsync extends AsyncTask<> {
#Override
protected void onProgressUpdate() {
textView.setText();
}
}
put something like this in your activity
Handler statusUpdateHandeler = new Handler()........
In your thread, call the handler (send it a message)
MainActivity.statusUpdateHandler.sendEmptyMessage(1);
In you actual handler code, set the status message.
I think I need to use setContentView to allow findViewById to work but I'm not sure how.
Yes. You need to use setContentView(R.layout.activity_splash_screen) in onCreate.
You can initialize your view in onCreate make asynctask an inner class of activity and update ui in onPreExecute.
TextView statusMessage;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash_screen);
statusMessage = (TextView) findViewById(R.id.statusMessage);
}
Also you can use a progressdialog and display the message. I think using progressdialog would be a better choice than textview. You can publish progress in doInbackground and update progress dialog in onProgressUpdate()
http://developer.android.com/reference/android/os/AsyncTask.html
Related
I have this private class that is within my main activity, and I am using it pull a JSon object off of my server into my app. The code below works fine and will display the JSon object as a string.
private class HttpAsyncTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
return httpBuild(urls[0]);
}
// onPostExecute displays the results of the AsyncTask.
#Override
protected void onPostExecute(String result) {
Toast.makeText(getBaseContext(), "Received!", Toast.LENGTH_LONG).show();
etResponse.setText(result);
}
}
what I am trying to do is place change the onPostExecute() method so it acts like webResult = result where webResult is an instance variable of the class mainActivity The problem is once I do this when I try to put the below code into the onCreate() method after HTTpAsyncTask has been called the app fails to display the object and crashes.
public class MainActivity extends Activity {
private static final String mainSite = "http://mysitehere";
private String webResult;
// private JSONArray floorsInBuilding, roomsInGender;
// private JSONObject room;
// private JSONArray arrayOfFloors;
// private JSONObject room, arrayOfRooms;
EditText etResponse;
TextView tvIsConnected;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// get reference to the views
etResponse = (EditText) findViewById(R.id.etResponse);
tvIsConnected = (TextView) findViewById(R.id.tvIsConnected);
// check if you are connected or not
if(isConnected()){
tvIsConnected.setBackgroundColor(0xFF00CC00);
tvIsConnected.setText("You are conncted");
}
else{
tvIsConnected.setText("You are NOT conncted");
}
// call AsynTask to perform network operation on separate thread
new HttpAsyncTask().execute(this.buildBuildingAddress(8));
Toast.makeText(getBaseContext(), "Received!", Toast.LENGTH_LONG).show();
etResponse.setText(WebResult);
}
I'm wondering what makes the part of the code that displays the result dependent on the HttpAsyncTask. I'm also wondering how I can get the result of the HttpAsyncTask and store it as a string in the main class.
A good chunk of my code is based of of this example.
http://hmkcode.com/android-parsing-json-data/
I'm sorry If my knowledge of android isn't so great but my experience lies in more in java.
If you want to wait till the task is over so that you can override the result with webResult then you may use the asyncTask method onProgressUpdate which runs on UI thread. You may refer android developer site to know how to use that method.
The answer to this is that the asynchronous task runs alongside onCreate(). So you have to create some piece of code that waits for the task to complete or you have to manipulate the result of the async task in the onPostExecute() method
I have a problem. Why a data in setText method are set incorrecetly?
MainActivity class
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textViewCity = (TextView) findViewById(R.id.text_view_city_name);
textViewTemperature = (TextView) findViewById(R.id.text_view_current_temperature);
new Thread(new WeatherYahoo()).start();
Weather weather = new Weather();
textViewCity.setText(weather.getCity());
textViewTemperature.setText(String.valueOf(weather.getTemperature()));
}
Data were downloaded and set correctly in Weather class (I use JSON) but on the screen showed empty string form textViewCity and 0 for textViewTemperature.
Everything in your activity executes on the UI thread. So this is happening because you are trying to set the text right after you started a new Thread with WeatherYahoo, so you don't wait for the result, but just output empty value. I would recommend you to use AsyncTask for such kind of calls and retrieving results on UI thread. So you can do all your work you do in WeatherYahoo class in doInBackground() method instead and output the result in onPostExecute() method. As an example:
private class WeatherYahooTask extends AsyncTask<Void, Void, Weather> {
protected Weather doInBackground(Void... params) {
// do any kind of work you need (but NOT on the UI thread)
// ...
return weather;
}
protected void onPostExecute(Weather weather) {
// do any kind of work you need to do on UI thread
textViewCity.setText(weather.getCity());
textViewTemperature.setText(String.valueOf(weather.getTemperature()));
}
}
You have 2 options:
Wait for the thread to finish downloading the json using:
Thread t = new Thread(new WeatherYahoo()).start();
t.join();
Weather weather = new Weather();
Or you can use asynctasks like Yuriy posted.
Related to my previous question about ANR problem (Android - Strings.xml versus text files. Which is faster?).
I tried using AsyncTask as advised by the respondents but i'm at a loss now.
I need to pass a string from my menu activity to an Asynctask but it's really confusing me. I've been searching and studying for 5 hours already but still cannot do it.
Here's a snippet of my code:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
/** Create an option menu from res/menu/items.xml */
getMenuInflater().inflate(R.menu.items, menu);
/** Get the action view of the menu item whose id is search */
View v = (View) menu.findItem(R.id.search).getActionView();
/** Get the edit text from the action view */
final EditText txtSearch = ( EditText ) v.findViewById(R.id.txt_search);
/** Setting an action listener */
txtSearch.setOnEditorActionListener(new OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
final EditText txtSearch = ( EditText ) v.findViewById(R.id.txt_search);
String enhancedStem = txtSearch.getText().toString();
TextView databaseOutput = (TextView)findViewById(R.id.textView8);
new AsyncTaskRunner().execute();
// should I put "enhancedStem" inside execute?
}
});
return super.onCreateOptionsMenu(menu);
}
Here's the Async part: UPDATED
public class AsyncTaskRunner extends AsyncTask<String, String, String> {
String curEnhancedStem;
private ProgressDialog pdia;
public AsyncTaskRunner (String enhancedStem)
{
this.curEnhancedStem = enhancedStem;
}
#Override
protected void onPreExecute() {
// Things to be done before execution of long running operation. For
// example showing ProgessDialog
super.onPreExecute();
pdia = ProgressDialog.show(secondactivity.this, "" , "Searching for words");
}
#Override
protected String doInBackground(String... params) {
if(curEnhancedStem.startsWith("a"))
{
String[] wordA = getResources().getStringArray(R.array.DictionaryA);
String delimiter = " - ";
String[] del;
TextView databaseOutput1 = (TextView)findViewById(R.id.textView8);
for (int wordActr = 0; wordActr <= wordA.length - 1; wordActr++)
{
String wordString = wordA[wordActr].toString();
del = wordString.split(delimiter);
if (curEnhancedStem.equals(del[0]))
{
databaseOutput1.setText(wordA[wordActr]);
pdia.dismiss();
break;
}
else
databaseOutput1.setText("Word not found!");
}
}
return null;
}
#Override
protected void onProgressUpdate(String... text) {
// Things to be done while execution of long running operation is in
// progress. For example updating ProgessDialog
}
#Override
protected void onPostExecute(String result) {
// execution of result of Long time consuming operation
}
}
Retrieval now works. I saw it display the word being looked for but it terminated suddenly.
Maybe because, like what you mentioned, UI stuff should be done on the post execute.
If that's the case, what should I return on the doInBackground() part then pass on the onPostExecute()?
(Thanks a lot guys! I'm close to making this work properly now!)
That's the problem, they are local to the method that you declared them in then you are declaring an AsyncTask class which doesn't have access to them. If the AsyncTask is an inner class of your menu activity then you can declare them as member variables.
public class MenuActivity extends Activity
{
String enhancedStem;
....
If it is a separate class then you can create a constructor in your Async class and pass the variable to the constructor.
public class AsyncTaskRunner extends AsyncTask<String, String, String> {
String curEnhancedStem;
private ProgressDialog pdia;
public void AsyncTaskRunner (String variableName)
{
this.curEnhancedStem = variableName;
}
And call it like
AsyncTaskRunner newTask = new AsyncTaskRunner(enhancedStem);
newTask.execute();
Also, you can't do UI stuff in doInBackground so this will need to be changed in the Activity class or one of your other methods in the Async class such as onPostExecute() if it is an inner class. Otherwise, you can pass a value back to your menu activity to update your TextView
Edit
You are still trying to change the UI in doInBackground() with this
TextView databaseOutput1 = (TextView)findViewById(R.id.textView8);
then again when you call setText(). This needs to be put in your onPostExecute() but then you will need to pass a reference of your TextView to the AsyncTask. You could just pass back the String that you want to set the text as from your onPostExecute() and set it in your Activity. Hope this helps
Create a constructor for your AsyncTaskRunner class.
Pass both a Context (your Activity Context) and the databaseOutput TextView as arguments to your AsyncTaskRunner class constructor.
Save references to those two objects in AsyncTaskRunner.
Pass enhancedStem to the execute() method.
Use the Context you passed to the constructor as the first argument to ProgessDialog.show()
You cannot access databaseOutput from the doInBackground() method. You must only access it in onPostExecute(), which runs on the UI thread. So, use the reference to databseOutput which you passed to the constructor to update the TextView in the onPostExecute() method accordingly.
As a note, anything you return from the doInBackground() method will be available to you as the parameters of the onPostExecute() method.
Please refer to http://developer.android.com/reference/android/os/AsyncTask.html
I would recommend you pass the required data rather than accessing it using the enclosing class - this makes your ASyncTaskRunner much more flexible, and is generally better practice.
I am a newbie in Android development. Now I am trying to parse. I have got many tutorials for parsing XML. But I would like to know parse XML asynchronously. I have found somewhere, xml can be loaded asynchronously using AsyncTask. Can anybody help me to find it out.
Thanks in advance
Here's a tutorial for using AsyncTask:
http://droidapp.co.uk/?p=177
And one for parsing RSS / XML:
http://droidapp.co.uk/?p=166
You need to call your parse function in doInBackground in the AsyncTask.
public class _StackOverflowActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String yourXmlString = "<put your xml String here>";
ParseXMLTask parseXMLTask = new ParseXMLTask();
parseXMLTask.execute(yourXmlString);
}
class ParseXMLTask extends AsyncTask<String, Void, Void> {
#Override
protected Void doInBackground(String... params) {
String yourXml = params[0];
//Parse your xml here
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
//do something after parsing is done
}
}
}
First, you have to extend the class AsyncTask. In my example I named it ParseXMLTask.
AsyncTask requires you to implement one method which is "doInBackground". doInBackground runs in a separate thread, put your code for parsing the xml there.
After the thread finishes, onPostExecute is called. onPostExecute runs in your main thread, you can use this if you wish to perform something after doInBackground finishes
To use ParseXMLTask, you have to instantiate it to an object. Then run the command .execute(). You can pass objects in execute similar to what I have done parseXMLTask.execute(yourXmlString); . You can pass as many variables as you like and be sure to handle them in doInBackground similar to String yourXml = params[0]; . If you have a second variable passed in .execute say... parseXml.execute(yourXmlString, my2ndVariable); , handle it in doInBackground through
String yourXml = params[0];
String the2ndVariable = params[1];
When you call .execute you tell AsyncTask to run whatever code you have placed in doInBackground in a separate thread.
I use an AsyncTask for loading operations that I implemented as an inner class.
In onPreExecute() I show a loading dialog which I then hide again in onPostExecute(). But for some of the loading operations I know in advance that they will finish very quickly so I don't want to display the loading dialog.
I wanted to indicate this by a boolean parameter that I could pass to onPreExecute() but apparently for some reason onPreExecute() doesn't take any parameters.
The obvious workaround would probably be to create a member field in my AsyncTask or in the outer class which I would have to set before every loading operation but that does not seem very elegant. Is there a better way to do this?
You can override the constructor. Something like:
private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
public MyAsyncTask(boolean showLoading) {
super();
// do stuff
}
// doInBackground() et al.
}
Then, when calling the task, do something like:
new MyAsyncTask(true).execute(maybe_other_params);
Edit: this is more useful than creating member variables because it simplifies the task invocation. Compare the code above with:
MyAsyncTask task = new MyAsyncTask();
task.showLoading = false;
task.execute();
1) For me that's the most simple way passing parameters to async task
is like this
// To call the async task do it like this
Boolean[] myTaskParams = { true, true, true };
myAsyncTask = new myAsyncTask ().execute(myTaskParams);
Declare and use the async task like here
private class myAsyncTask extends AsyncTask<Boolean, Void, Void> {
#Override
protected Void doInBackground(Boolean...pParams)
{
Boolean param1, param2, param3;
//
param1=pParams[0];
param2=pParams[1];
param3=pParams[2];
....
}
2) Passing methods to async-task
In order to avoid coding the async-Task infrastructure (thread, messagenhandler, ...) multiple times you might consider to pass the methods which should be executed in your async-task as a parameter. Following example outlines this approach.
In addition you might have the need to subclass the async-task to pass initialization parameters in the constructor.
/* Generic Async Task */
interface MyGenericMethod {
int execute(String param);
}
protected class testtask extends AsyncTask<MyGenericMethod, Void, Void>
{
public String mParam; // member variable to parameterize the function
#Override
protected Void doInBackground(MyGenericMethod... params) {
// do something here
params[0].execute("Myparameter");
return null;
}
}
// to start the asynctask do something like that
public void startAsyncTask()
{
//
AsyncTask<MyGenericMethod, Void, Void> mytest = new testtask().execute(new MyGenericMethod() {
public int execute(String param) {
//body
return 1;
}
});
}
why, how and which parameters are passed to Asynctask<>, see detail here. I think it is the best explanation.
Google's Android Documentation Says that :
An asynchronous task is defined by 3 generic types, called Params, Progress and Result, and 4 steps, called onPreExecute, doInBackground, onProgressUpdate and onPostExecute.
AsyncTask's generic types :
The three types used by an asynchronous task are the following:
Params, the type of the parameters sent to the task upon execution.
Progress, the type of the progress units published during the background computation.
Result, the type of the result of the background computation.
Not all types are always used by an asynchronous task. To mark a type as unused, simply use the type Void:
private class MyTask extends AsyncTask<Void, Void, Void> { ... }
You Can further refer : http://developer.android.com/reference/android/os/AsyncTask.html
Or You Can clear whats the role of AsyncTask by refering Sankar-Ganesh's Blog
Well The structure of a typical AsyncTask class goes like :
private class MyTask extends AsyncTask<X, Y, Z>
protected void onPreExecute(){
}
This method is executed before starting the new Thread. There is no input/output values, so just initialize variables or whatever you think you need to do.
protected Z doInBackground(X...x){
}
The most important method in the AsyncTask class. You have to place here all the stuff you want to do in the background, in a different thread from the main one. Here we have as an input value an array of objects from the type “X” (Do you see in the header? We have “...extends AsyncTask” These are the TYPES of the input parameters) and returns an object from the type “Z”.
protected void onProgressUpdate(Y y){
}
This method is called using the method publishProgress(y) and it is usually used when you want to show any progress or information in the main screen, like a progress bar showing the progress of the operation you are doing in the background.
protected void onPostExecute(Z z){
}
This method is called after the operation in the background is done. As an input parameter you will receive the output parameter of the doInBackground method.
What about the X, Y and Z types?
As you can deduce from the above structure:
X – The type of the input variables value you want to set to the background process. This can be an array of objects.
Y – The type of the objects you are going to enter in the onProgressUpdate method.
Z – The type of the result from the operations you have done in the background process.
How do we call this task from an outside class? Just with the following two lines:
MyTask myTask = new MyTask();
myTask.execute(x);
Where x is the input parameter of the type X.
Once we have our task running, we can find out its status from “outside”. Using the “getStatus()” method.
myTask.getStatus();
and we can receive the following status:
RUNNING - Indicates that the task is running.
PENDING - Indicates that the task has not been executed yet.
FINISHED - Indicates that onPostExecute(Z) has finished.
Hints about using AsyncTask
Do not call the methods onPreExecute, doInBackground and onPostExecute manually. This is automatically done by the system.
You cannot call an AsyncTask inside another AsyncTask or Thread. The call of the method execute must be done in the UI Thread.
The method onPostExecute is executed in the UI Thread (here you can call another AsyncTask!).
The input parameters of the task can be an Object array, this way you can put whatever objects and types you want.
You can either pass the parameter in the task constructor or when you call execute:
AsyncTask<Object, Void, MyTaskResult>
The first parameter (Object) is passed in doInBackground.
The third parameter (MyTaskResult) is returned by doInBackground. You can change them to the types you want. The three dots mean that zero or more objects (or an array of them) may be passed as the argument(s).
public class MyActivity extends AppCompatActivity {
TextView textView1;
TextView textView2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
textView1 = (TextView) findViewById(R.id.textView1);
textView2 = (TextView) findViewById(R.id.textView2);
String input1 = "test";
boolean input2 = true;
int input3 = 100;
long input4 = 100000000;
new MyTask(input3, input4).execute(input1, input2);
}
private class MyTaskResult {
String text1;
String text2;
}
private class MyTask extends AsyncTask<Object, Void, MyTaskResult> {
private String val1;
private boolean val2;
private int val3;
private long val4;
public MyTask(int in3, long in4) {
this.val3 = in3;
this.val4 = in4;
// Do something ...
}
protected void onPreExecute() {
// Do something ...
}
#Override
protected MyTaskResult doInBackground(Object... params) {
MyTaskResult res = new MyTaskResult();
val1 = (String) params[0];
val2 = (boolean) params[1];
//Do some lengthy operation
res.text1 = RunProc1(val1);
res.text2 = RunProc2(val2);
return res;
}
#Override
protected void onPostExecute(MyTaskResult res) {
textView1.setText(res.text1);
textView2.setText(res.text2);
}
}
}