I have an AsyncTask that takes in context (used onPostExecute) and runs doInBackground to return an ArrayList of objects from a server. When I execute this I can see that doInBackground runs fine, however it is not passing the result on to onPostExecute.
After much searching, I have yet to find an answer of how to return an ArrayList of objects in an AsyncTask.
This is the object I'm creating in doInBackground and using onPostExecute:
public class ServerTimeCard {
public String eventNameInput;
Boolean isLocation, isImage, isVoice;
public ServerTimeCard(String eventNameInput, boolean isLocation, boolean isImage, boolean isVoice) {
this.eventNameInput = eventNameInput;
this.isLocation = isLocation;
this.isImage = isImage;
this.isVoice = isVoice;
}
}
I'm executing the AsyncTask with new LoadFriendCards(context).execute(); in onCreate.
Expected Output: doInBackground should return an ArrayList to onPostExecute
Current Output: The ArrayList<ServerTimeCard> in onPostExecute has a size of zero when the same arraylist in doInBackground has a larger size.
The following is the AsyncTask code.
public class LoadFriendCards extends AsyncTask<Void, Void, ArrayList<ServerTimeCard>> {
Context context;
ArrayList<ServerTimeCard> result;
public LoadFriendCards(Context context) {
this.context = context;
}
#Override
protected ArrayList<ServerTimeCard> doInBackground(Void... voids) {
result = new ArrayList<ServerTimeCard>();
// ...a bunch of data retrieval goes on here...
// querying parse for object info
// adding a new object to the local ArrayList for all in db
for (String friend : friendsListNames) {
ParseQuery<ParseObject> query = ParseQuery.getQuery("TestObject");
query.whereEqualTo("accountName", friend+"#gmail.com");
query.findInBackground(new FindCallback<ParseObject>() {
public void done(List<ParseObject> objects, ParseException e) {
if (e == null) {
for (ParseObject cardInfo : objects) {
ServerTimeCard item = new ServerTimeCard(
cardInfo.getString("eventName"),
cardInfo.getBoolean("isImage"),
cardInfo.getBoolean("isImage"),
cardInfo.getBoolean("isVoice"));
result.add(item);
Log.e("New item called: ", String.valueOf(item.eventNameInput));
Log.e("New size of array...", String.valueOf(result.size()));
}
} else {
Log.d("info", "Error: " + e.getMessage());
}
}
});
}
// returning the new ArrayList<ServerTimeCard> to onPostExecute
// PROBLEM: THE SIZE OF THE RESULT IS 0
Log.e("Size of passing array", String.valueOf(result.size()));
return result;
}
#Override
protected void onPostExecute(ArrayList<ServerTimeCard> result) {
// PROBLEM: This returns 0
Log.e("Data list size: ", String.valueOf(result.size()));
// trying to use all objects in the arraylist here but it doesn't work
// due to the size. Something isn't passing correctly.
}
}
Logcat output of Log.e: (which looks like it calls doInBackground, then onPostExecute, then doInBackground again)
E/Size of passing array﹕ 0
E/Data list size:﹕ 0
E/New item called:﹕ yah
E/New size of array...﹕ 1
E/New item called:﹕ lplokolol
E/New size of array...﹕ 2
E/New item called:﹕ It works
E/New size of array...﹕ 3
SOLVED: I originally thought I needed the AsyncTask but I ended up deleting it all and throwing all the code into a method in my Main class. I was running too many things at once in an asynctask and the structure was throwing data all over the place. Lesson learned: keep it simple.
Your issue is that you are using findInBackground for your query. That method is done in a background thread. So you're basically running an async task, within an async task.
doInBackground
findInBackground -> run asynchronously so code execute continues
onPostExecute is called before your query finishes.
The done call back is then called for your query to fill your list
So what you need to do instead is either use findInBackground outside of an AsyncTask (that method is intended to not be used in an AsyncTask) or use the find function.
You're modifying result in an anonymous inner class. Your code there doesn't see the result you think it sees, but rather a closure. This is the root of the problem.
You are starting a background thread (your async task), and here you call query.findInBackground() multiple times which starts MORE background threads. This, however, isn't a problem.
You're getting problems because the AsyncTask is done before all the other threads it started have finished. You only know the other threads are done when each of their FindCallback's done() method is called.
If you want to know the correct size, you need to code it in a way so that you check the size in the last done() method.
You dont have to make doInBackground to return an arraylist which can be used in onPostExecute. Just return "ok" or "error". Declare and create a private arraylist as member of your ask. Then fill it in doInBackground and use it in onPostexecute.
An alternative and simpler way is to declare your doInBackground as List type.
Here is my Example
#Override
protected ArrayList<ServerTimeCard > doInBackground(String... urls) {
// Perform the HTTP request for earthquake data and process the response.
ArrayList<ServerTimeCard > result = QueryUtils.fetchTheData(urls[0]);
return result;
}
Related
I am trying to read a list of integers inside of the doInBackground of AsyncTask. When I pass the list into the constructor of AsyncTask, it is full. But, by the time I get to the doInBackground function, it is empty. Any ideas?
public class floatingActionButtonClickListener implements View.OnClickListener{
#Override
public void onClick(View v) {
if(mAdapter.getDeleteModeStatus()){
// Delete items from database
ArrayList<Integer> IDsToDelete = mAdapter.getJournalIDsToDelete();
new DeleteDatabase().execute(IDsToDelete);
// Turn FAB back to regular button
mFAB.setImageResource(R.drawable.baseline_add_white_48); // Turn FAB to delete button
// Disable delete mode
mAdapter.exitDeleteMode();
// Load database
new LoadDatabase().execute();
}
else{
Intent intent = new Intent(getBaseContext(), AcitivtyJournal.class);
int journalType = Constants.JOURNALTYPE_FULL;
intent.putExtra(Constants.JOURNAL_TYPE, journalType);
startActivity(intent);
}
}
}
private class DeleteDatabase extends AsyncTask <ArrayList<Integer>, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
mProgressBarHolder.setVisibility(View.VISIBLE);
}
#Override
protected Void doInBackground(ArrayList<Integer>... arrayLists) {
ArrayList<Integer> IDsToDelete = arrayLists[0];
AppDatabase db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "JournalEntries")
.build();
for(Integer idToDelete : IDsToDelete){
db.mJournalEntriesDao().deleteCompleteJournalEntry(idToDelete);
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
mProgressBarHolder.setVisibility(View.GONE);
}
}
}
This is not how you use and AsyncTask. You need to declare the parameters and then recieve them in the callback.
Please also note that you are trying to access the same data(IDsToDelete) from two threads(Main and Background), in your way, without proper syncronization.
private class DeleteDatabase extends AsyncTask<ArrayList<Integer>, Void, Void> {
#Override
protected Void doInBackground(ArrayList<Integer>... arrayLists) {
ArrayList<Integer> params = arrayLists[0];
// Do what you need
}
}
ArrayList<Integer> IDsToDelete = mAdapter.getJournalIDsToDelete();
new DeleteDatabase().execute(IDsToDelete);
When you have multithreading you need to look for two things:
atomic execution of operations
memory visibility.
There is a shared memory and every CPU caches the data. When you create something from one thread you can't just expect that the second thread will just read it. In your case you are creating the AsyncTask and inject the params from one thread, but then you read them in doInBackground from another. In general when you go through a synchronized block or hit a volatile variable(I say in general, because I also don't fully understand how JVM works), the thread flushes it's cache to the main memory and then reads also from it. This is how the data is shared. That is why it is better to use the framework way, because the frame will take care of proper publication of your data between threads. You are ok with immutable data, but a List is not such thing. And even if you declare the reference as immutable, you might see the right object from both threads, but the data they are holding might be old.
Just to be clear. I am not saying that the previous way was not working. I am saying that it is on good will. You can't just share data betweeb threads and hope it works.
Figured it out. Posting for people in the future who may have similar questions.
Embarrasingly enough, the ArrayList<Integer> was coming empty because I was deleting it in the function mAdapter.exitDeleteMode(); after I call AsyncTask().execute().
I was not aware that when I send the list to the AsyncTask it was the exact address of the list and not just a new list (that is, until I posted the comment above, and then it clicked). I think I got that train of thinking from C++ or another language. I don't remember which.
Solution: The solution I came up with is to just move mAdapter.exitDeleteMode() into of onPostExecute()instead of having it in the onClick() method.
Another Potential Solution: I believe another solution that would work (but I did not test) would be to just insert a new ArrayList<Integer> () into the AsyncTask
I am working on to speech to text with translation. What I want to do is when the user inputs his/her voice, there will be an automatic translation based on what it is selected in the spinner.
This is the code from my project, I am receiving an error android.os.NetworkOnMainThreadException. And the solution that I found was to put it in async.
I tried searching around the internet and even asked forums, I cannot find a solution to put this code inside a asynctask. This is the code where I want to put in async.
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
Translate translate = TranslateOptions.getDefaultInstance().getService();
TextView translatedText = view.findViewById(R.id.translatedText);
switch (position){
case 1:
Translation enTranslation = translate
.translate(translatedText
.getText()
.toString(), TranslateOption.sourceLanguage("en"), TranslateOption.targetLanguage("en"));
translatedText.setText(enTranslation.getTranslatedText());
break;
case 2:
Translation filTranslation = translate
.translate(translatedText
.getText()
.toString(), TranslateOption.sourceLanguage("en"), TranslateOption.targetLanguage("fil"));
translatedText.setText(filTranslation.getTranslatedText());
break;
case 3:
Translation cebTranslation = translate
.translate(translatedText
.getText()
.toString(), TranslateOption.sourceLanguage("en"), TranslateOption.targetLanguage("ceb"));
translatedText.setText(cebTranslation.getTranslatedText());
break;
}
}
I expected that I won't get android.os.NetworkOnMainThreadException. Thanks in advance :)
Using AsyncTask
You can use an AsyncTask. It has 3 steps to it.
1. onPreExecute() - things you want to do before running doInBackground(). This happens in the UI main thread.
2. doInBackground()- the AsyncTask, will do operations in a background thread (the background thread is created by Android so you don't need to worry about it).
3.onPostExecute() - here you can receive any data from the doInBackground method. The postExecute method is executed again, in the UI main thread.
So you can do any I/O operations in doInBackground(), and return the value you received from the server or any other data source.
How to Declare
To use AsyncTask, you need to extend the Android AsyncTask.
So your own AsyncTask declaration will look like this:
private class MyAsyncTask extends AsyncTask<Void, Void, Void> { ... }
What are the 3 generic arguments you ask?
1. Params - the type of the parameters sent to the task upon execution.
2. Progress - the type of the progress units published during the background computation. (Almost always will be Void, unless you care about the actual progress of the operation. Notice this is Void with a capital letter, and not void as the return type).
3. Result - the type of the result of the background computation.
Full Example
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.interrupted();
}
}
return "Executed";
}
#Override
protected void onPostExecute(String result) {
TextView txt = findViewById(R.id.output);
txt.setText(result);
}
}
In the example, I create a fake, long operation, that you can not run on the UI main thread (because it is a blocking operation).
When the operation is finished, it returns a String, and that same String is received in the onPostExecute() method (and remember, onPostExecute() runs on the UI main thread again). So you can change your UI with the String value you received from the long,blocking operation.
If you want the documentation, here it is:
https://developer.android.com/reference/android/os/AsyncTask
I am using AsyncTask so that the function I want executes immediately and does not wait till the end to be executed..
but for some reason I don't why it executes in at the end of all process!
I looked at other solutions and found that Thread should be executed at the end but for AsyncTask it should be executed whenever it is called..
here is my code
private void LogMeIn()
{
string CheckValue;
// Here I call the AsyncTask
new GCM().execute(null,null,null);
//gcmRegID is a public variable and should has GCM value assigned to it by now, but I it is empty as GCM() has not been executed yet
//This is always return empty string
CheckValue = gcmRegID;
}
This is the AsyncTask that wait till the end to be executed
//This is the AsyncTask
private class GCM extends AsyncTask<String, String, String> {
private String resp;
private Context context;
#Override
protected String doInBackground(String... params) {
GCMHelper gcmRegistrationHelper = new GCMHelper (
getApplicationContext());
try {
gcmRegID = gcmRegistrationHelper.GCMRegister("123456789");
} catch (Exception e) {
e.printStackTrace();
}
return gcmRegID;
}
}
I tried to put the call for GCMRegister in onPreExecute but i get an error that it has to be in the main thread
it is like i am going in circles....
the call has to be in the main thread and the main thread will be executed at the end of the function...
it is like no way to get the GCM code in the middle!!!!
How can I make this AsyncTask executes when it called??
Thanks
Without seeing more of your code it's hard for me to tell but I would take a look at where you are calling LogMeIn(). Because your AsyncTask and call to execute are nested in the LogMeIn() function, it won't be called until LogMeIn() is first called.
AsyncTask goes through the following 4 steps in order after calling execute():
onPreExecute()
doInBackground(Params...)
onProgressUpdate(Progress...)
onPostExecute(Result)
These can be added to your GCM class and used however you like. Note that you don't call them directly. AsyncTask does it automatically after calling .execute().
Only the tasks specified in doInBackground(Params...) are executed on a background thread. The rest are all done on the UI(or main) thread. I would suggest putting either a toast or a log inside onPreExecute() and in onPostExecute() to debug where/when GCM().execute is actually being called and then to tell you when the background task is complete. This will give you a better idea of what is going on.
Make sure you aren't trying to update the UI in doInBackground().
Would love to help more but we would need to see more of your code.
#Override
public void onPreExecute() {
super.onPreExecute();
Log.d("GCM", "onPreExecute: called");
}
#Override
public void onPostExecute(String resultOfDoInBackground) {
super.onPostExecute(resultOfDoInBackground);
Log.d("GCM", "onPostExecute: called");
}
AsyncTask keeps a queue of tasks and a thread pool,the thread pool execute the tasks one by one,so if you have too more tasks ,you will find it not execute your tasks immediately.
And in one process ,all your AsyncTask share one thread pool.In this case,you should make one task queue by yourself,you can just use HandleThread to execute a timely task.
Try placing your new GCM().execute(null,null,null); in the protected void onCreate(Bundle savedInstanceState) method. this way it will be called once the app is ran. This way you will have your GCM id before you get to the LogMEIn method.
Try this -
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ) {
new GCM().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,params);
} else {
new GCM().execute();
}
I have been browsing about this I am not finding the answer that suits my problem. But i would like on execute onPostExecute but it never gets called.
I send in a timestamp, unixtime, to the server and I get a list in return (ArrayList). Then I want to work with that list in onPostExecute. I do get the list though.
I send in Long
I want to work with that list, I think.
I want to return that list
Thats why I do 'Long, ArrayList, ArrayList'
Could use a little help, thanks!
private class getMealsByDayConnection extends AsyncTask<Long, ArrayList<CalanderMeal>, ArrayList<CalanderMeal>> {
protected ArrayList<CalanderMeal> doInBackground(Long... params) {
CalendarService service = CalendarServiceFactory.getCalanderService();
try {
return service.getMealsByDay(params[0]);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(ArrayList<CalanderMeal> result){
CalendarAdapter adapter = null;
adapter.notifyDataSetChanged();
}
}
According to the documentation, the method will not be called if the task is cancelled. So perhaps your task is being cancelled since an empty list is returned? Or the onPostExecute(result) doesn't work because result is an empty list? You might be able to catch this by overriding onCancel().
Btw, you did call the async task from within the main UI thread and start the task with the getMealsByDayConnection.execute() method, right?
i think you are calling asyncTask with
getMealsByDayConnection.doInBackground(params);
if you are executing like this , task will execute in main thread and the doInBackground() function alone will execute.
you should execute asynctask like this,
getMealsByDayConnection.execute();
i have a function that return an String and has an AsynTask inside it. my problem is that my function have to return the value after AsynTask finished his work!
this is my code:
public String getWeather(Context cont,int cityid,String latitude, String longitude){
this.latitude=latitude;
this.longitude=longitude;
new get_scores() {
protected void onPreExecute() {
super.onPreExecute();
}
protected void onPostExecute(Boolean result) {
}
}.execute();
return weatherdata;
}
i want to return weatherdata but after finishing AsynTask. it can be take a few seconds.
One solution is to return the value from `onPostExecute(). You can do this by setting up your AsyncTask as normal then change your return statement to something like
public String getWeather(Context cont,int cityid,String latitude, String longitude){
this.latitude=latitude;
this.longitude=longitude;
new get_scores() {
protected void onPreExecute() {
super.onPreExecute();
}
protected void onPostExecute(Boolean result) {
}
}.execute();
return new get_scores().execute();
}
Doing it this way you will probably want to add a progress bar/dialog so that the user doesn't think the app is stuck. As long as the task doesn't take more than 2 or 3 seconds(at most) and you don't want the user to be able to do anything else until it finishes then this should be fine to do. And I would have some error checking by surrounding with a try/catch or make sure you return something in case the app can't return
The reason to create an AsyncTask is not to run on the UI thread.The return statement is part of the UI thread, so the return statement will always return null because it continue executing code when background thread s start.
You have two possibilities:
1-You run your code on the UI Tread and your app get stuck working in this function until it arrives at the return statement and returns the calculated value for the variable. Please, don't do that.
2-It is what I would do. Don't return anything from this function, so change it to return void, remove the return statement, call the asynctask and from onPostExecute you call a function that will manipulate the variable that you assigned value on doInBackground.
I don't see where you are doing doInBackground but there it is where the task must be done, so implement it and give in there the value to your variable not to be null again.