I'm new to asking questions here so any advice about that will be appreciated...but to my problem:
I'm trying to switch from one activity to another, where I'll show some data from the server. I'm pulling the data from server via AsyncTask but there is black screen showing between the activities, when the download is longer. I already tried to put the AsyncTask from onCreate() to onStart() but no change there.
New activity:
public class SalesMenu extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.sales_menu);
setProgressBarIndeterminateVisibility(true);
super.onCreate(savedInstanceState);
}
#Override
protected void onStart() {
List<HashMap<String, Object>> result = null;
AsyncTask<Void, Boolean, List<HashMap<String, Object>>> aTask = new AsyncSearchRead(
"sale.order",
new Object[0],
new String[] {"name"},
this).execute();
try {
result = aTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
Log.d("test", (result!=null ? result.toString() : "nothing"));
super.onStart();
}
}
My AsyncTask:
public class AsyncSearchRead extends AsyncTask<Void, Boolean, List<HashMap<String, Object>>> {
String mModel;
boolean mCount;
Integer mOffset;
Integer mLimit;
String mOrder;
boolean mReverseOrder;
Object[] mConditions;
String[] mFields;
Activity parentActivity;
public AsyncSearchRead(String model, boolean count, Integer offset, Integer limit,
String order, boolean reverseOrder, Object[] conditions, String[] fields,
Activity activity) {
mModel = model;
mCount = count;
mOffset = offset;
mLimit = limit;
mOrder = order;
mReverseOrder = reverseOrder;
mConditions = conditions;
mFields = fields;
parentActivity = activity;
}
#Override
protected List<HashMap<String, Object>> doInBackground(Void... params) {
Long[] ids;
List<HashMap<String, Object>> results;
//Downloading the data
ids = SetServer.connection.search(mModel, mCount, mOffset, mLimit, mOrder, mReverseOrder, mConditions);
results = SetServer.connection.read(mModel, ids, mFields);
return results;
}
#Override
protected void onPostExecute(List<HashMap<String, Object>> result) {
parentActivity.setProgressBarIndeterminateVisibility(false);
}
}
When I did not turn visibility off onPostExecute it kept spinning after showing the activity but it's only shown after the loading.
How can I get it to show the activity and only after that start to load the data and show some progress bar/loader?
Thanks for any help.
It's because you're using .get(). .get() essentially makes an AsyncTask synchronous (makes the main thread wait until it's done), so it's basically like not running the AsyncTask at all.
My suggestion is as follows:
Move List<HashMap<String, Object>> result = null; to be a member variable.
Remove the whole try/catch statement (the one with .get() in it), and the call to Log.*.
Move your AsyncTask to be an inner class of SalesMenu.
Do this.result = result in onPostExecute(), as well as whatever other processing you need to do on it.
In addition to #Eric's answer, which is very good advice (+1), try putting super.onCreate() before setContentView(). As a general rule, the super call should happen before anything else in overridden Android lifecycle methods, and is especially important in the case of these two methods.
In addition to the other excellent asnwers.
In your doInBackground() method, to show progress you call publichProgress() at regular intervals, which will in turn call onProgressUpdate(). onPostExecute() is only called AFTER doInBackground() has returned.
There is a full usage example in the AsyncTask docs.
There some points to keep under consideration while doing any long running tasks:
1) Keep them out of main thread
2) Use Async task class for short lived tasks and make them Asynchronous, use handlers with threads
3) Keep out any long running/heavy tasks from oncreate method
4) Do any task after super.onStart(); call under onStart() or onCreate super calls.
Hope this will help you out.
Related
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 haven't spent much time working with AsyncTasks in Android. I'm trying to understand how to pass variables to and from the class. The syntax:
class MyTask extends AsyncTask<String, Void, Bitmap>{
// Your Async code will be here
}
it's a little bit confusing with the < > syntax on the end of the class definition. Never seen that type of syntax before. It seems like I'm limited to only passing one value into the AsyncTask. Am I incorrect in assuming this? If I have more to pass, how do I do that?
Also, how do I return values from the AsyncTask?
It's a class and when you want to use it you call new MyTask().execute() but the actual method you use in the class is doInBackground(). So where do you actually return something?
Note: all of the information below is available on the Android Developers AsyncTask reference page. The Usage header has an example. Also take a look at the Painless Threading Android Developers Blog Entry.
Take a look at the source code for AsynTask.
The funny < > notation lets you customize your Async task. The brackets are used to help implement generics in Java.
There are 3 important parts of a task you can customize:
The type of the parameters passed in - any number you want
The type for what you use to update the progress bar / indicator
The type for what you return once done with the background task
And remember, that any of the above may be interfaces. This is how you can pass in multiple types on the same call!
You place the types of these 3 things in the angle brackets:
<Params, Progress, Result>
So if you are going to pass in URLs and use Integers to update progress and return a Boolean indicating success you would write:
public MyClass extends AsyncTask<URL, Integer, Boolean> {
In this case, if you are downloading Bitmaps for example, you would be handling what you do with the Bitmaps in the background. You could also just return a HashMap of Bitmaps if you wanted. Also remember the member variables you use are not restricted, so don't feel too tied down by params, progress, and result.
To launch an AsyncTask instantiate it, and then execute it either sequentially or in parallel. In the execution is where you pass in your variables. You can pass in more than one.
Note that you do not call doInBackground() directly. This is because doing so would break the magic of the AsyncTask, which is that doInBackground() is done in a background thread. Calling it directly as is, would make it run in the UI thread. So, instead you should use a form of execute(). The job of execute() is to kick off the doInBackground() in a background thread and not the UI thread.
Working with our example from above.
...
myBgTask = new MyClass();
myBgTask.execute(url1, url2, url3, url4);
...
onPostExecute will fire when all the tasks from execute are done.
myBgTask1 = new MyClass().execute(url1, url2);
myBgTask2 = new MyClass().execute(urlThis, urlThat);
Notice how you can pass multiple parameters to execute() which passes the multiple parameter on to doInBackground(). This is through the use of varargs (you know like String.format(...). Many examples only show the extraction of the first params by using params[0], but you should make sure you get all the params. If you are passing in URLs this would be (taken from the AsynTask example, there are multiple ways to do this):
// This method is not called directly.
// It is fired through the use of execute()
// It returns the third type in the brackets <...>
// and it is passed the first type in the brackets <...>
// and it can use the second type in the brackets <...> to track progress
protected Long doInBackground(URL... urls)
{
int count = urls.length;
long totalSize = 0;
// This will download stuff from each URL passed in
for (int i = 0; i < count; i++)
{
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
}
// This will return once when all the URLs for this AsyncTask instance
// have been downloaded
return totalSize;
}
If you are going to be doing multiple bg tasks, then you want to consider that the above myBgTask1 and myBgTask2 calls will be made in sequence. This is great if one call depends on the other, but if the calls are independent - for example you are downloading multiple images, and you don't care which ones arrive first - then you can make the myBgTask1 and myBgTask2 calls in parallel with the THREAD_POOL_EXECUTOR:
myBgTask1 = new MyClass().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url1, url2);
myBgTask2 = new MyClass().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, urlThis, urlThat);
Note:
Example
Here is an example AsyncTask that can take as many types as you want on the same execute() command. The restriction is that each type must implement the same interface:
public class BackgroundTask extends AsyncTask<BackgroundTodo, Void, Void>
{
public static interface BackgroundTodo
{
public void run();
}
#Override
protected Void doInBackground(BackgroundTodo... todos)
{
for (BackgroundTodo backgroundTodo : todos)
{
backgroundTodo.run();
// This logging is just for fun, to see that they really are different types
Log.d("BG_TASKS", "Bg task done on type: " + backgroundTodo.getClass().toString());
}
return null;
}
}
Now you can do:
new BackgroundTask().execute(this1, that1, other1);
Where each of those objects is a different type! (which implements the same interface)
I recognize that this is a late answer, but here's what I've been doing for the last while.
When I'm needing to pass in a bunch of data to an AsyncTask, I can either create my own class, pass that in and then access it's properties, like this:
public class MyAsyncTask extends AsyncTask<MyClass, Void, Boolean> {
#Override
protected Boolean doInBackground(MyClass... params) {
// Do blah blah with param1 and param2
MyClass myClass = params[0];
String param1 = myClass.getParam1();
String param2 = myClass.getParam2();
return null;
}
}
and then access it like this:
AsyncTask asyncTask = new MyAsyncTask().execute(new MyClass());
or I can add a constructor to my AsyncTask class, like this:
public class MyAsyncTask extends AsyncTask<Void, Void, Boolean> {
private String param1;
private String param2;
public MyAsyncTask(String param1, String param2) {
this.param1 = param1;
this.param2 = param2;
}
#Override
protected Boolean doInBackground(Void... params) {
// Do blah blah with param1 and param2
return null;
}
}
and then access it like this:
AsyncTask asyncTask = new MyAsyncTask("String1", "String2").execute();
Hope this helps!
Since you can pass array of objects in the square bracket, that is the best way to pass data based on which you want to do processing in the background.
You could pass the reference of your activity or the view in the Constructor and use that to pass data back into your activity
class DownloadFilesTask extends AsyncTask<URL, Integer, List> {
private static final String TAG = null;
private MainActivity mActivity;
public DownloadFilesTask(MainActivity activity) {
mActivity = activity;
mActivity.setProgressBarIndeterminateVisibility(true);
}
protected List doInBackground(URL... url) {
List output = Downloader.downloadFile(url[0]);
return output;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
private void setProgressPercent(final Integer integer) {
mActivity.setProgress(100*integer);
}
protected void onPostExecute(List output) {
mActivity.mDetailsFragment.setDataList((ArrayList<Item>) output);
//you could do other processing here
}
}
Alternatively, you could just use a regular thread and usea handler to send data back to the ui thread by overriding the handlemessage function.
Passing a simple String:
public static void someMethod{
String [] variableString= {"hello"};
new MyTask().execute(variableString);
}
static class MyTask extends AsyncTask<String, Integer, String> {
// This is run in a background thread
#Override
protected String doInBackground(String... params) {
// get the string from params, which is an array
final String variableString = params[0];
Log.e("BACKGROUND", "authtoken: " + variableString);
return null;
}
}
I have the following code:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.screen_database);
db=new DBAdapter(this);
InitTask init_task=new InitTask();
init_task.execute(db);
}
public class InitTask extends AsyncTask <DBAdapter, Void, List<GeoPoint>> {
List<GeoPoint> geoPointsArray = new ArrayList<GeoPoint>();
DBAdapter db;
int latitude;
int longitude;
GeoPoint p;
protected List<GeoPoint> doInBackground(DBAdapter...adapters) {
try{
db.openDataBase();
Cursor c=db.getAllData();
if (c.moveToFirst()) {
do{
longitude=Integer.parseInt(c.getString(0));
latitude=Integer.parseInt(c.getString(1));
p = new GeoPoint(latitude,longitude);
geoPointsArray.add(p);
} while(c.moveToNext());
}
c.close();
db.close();
} catch(Exception e) {
Log.d("Eroare","doInBackground",e);
}
return geoPointsArray;
}
protected void onPostExecute(List<GeoPoint> geoPointsArray {
super.onPostExecute(geoPointsArray);
for (int i=0;i<geoPointsArray.size();i++) {
Log.d("Lista",geoPointsArray.get(i).toString());
}
}
}
In doInBackground I try to read from the database and in my onPostExecute() I try to display the data, but I get nothing, and I'm sure that I have something in the DB.
Since I do not see your declaration for AsyncTask Params,Progress,Result this is difficult to debug, but I can see two problems
1) Your doInBackground does not match the prototype doInBackground(Params...)
2) I see no code that actually does anything with geoPointsArray when the threaded doInBackground returns in onPostExecute. To answer your question, at onPostExecute you are back in the GUI thread.
I have some code here, which may or may not help.
EDIT: Consider passing db as the param
new AsynchTask<DataBase?,Void,List<GeoPoint>>
Then comment out all of the non database code.
Wrap the database code in try catch Exception e.
Write Log.d(TAG,"doInBackground",e) on exception.
Then in onPostExecute examine the List to see what the thread in doInBackground is returning perhaps by calling Log.d(TAG,new Boolean(myList.isEmpty()).toString())
Firstly, you haven't declared your AsyncTask class correctly, as can be seen from the example in android documentation here you need to specifify the three types it will use:
public class InitTask extends AsyncTask<Params, Progress, Result>
This will fix your doInBackround problem. The reason it isn't recognised is Result needs to be substituted for your List.
To get the data back to the UI thread, you need to set up a listener, onProgressUpdate() which will get called, passing the Progress object which you send from the AsyncTask using publishProgress().
I have the following block of code based on an asynctask.
I'm trying to return a List variable via return LoadFeed()
and the return type of doInBackground is String.
If I change the return type of doInBackground from String to List
then I get an error
"The return type is incompatible with AsyncTask<String,Void,String>.doInBackground(String[])"
How do I fix this error?
Please help
Thanks,
private class DispData extends AsyncTask<String, Void, String> {
private final ProgressDialog dialog = new ProgressDialog(MessageList.this);
// can use UI thread here
protected void onPreExecute() {
dialog.setMessage("Fetching scores...");
dialog.show();
}
// automatically done on worker thread (separate from UI thread)
protected String doInBackground(final String... args) {
return loadFeed();
}
// can use UI thread here
protected void onPostExecute(final List<String> result) {
if (dialog.isShowing()) {
dialog.dismiss();
}
adapter =
new ArrayAdapter<String>(MessageList.this,R.layout.row,result);
MessageList.this.setListAdapter(adapter);
}
}
Change your class definition to:
class DispData extends AsyncTask<String, Object, List<String>>
This will force the doInBackground declaration to become:
protected List<String> doInBackground(String... arg);
First, you really should add #Override annotations to your onPreExecute(), doInBackground(), and onPostExecute() methods. On an AsyncTask, this is critical to help you keep track of all the data types.
Then, if you want doInBackground() to return List<String>, you need to change it both in doInBackground() and in your AsyncTask declaration (the third data type will need to change from String to List<String>), to go along with your change that you already made to onPostExecute().