How to start multiple parallel Asynctasks or threads? (Android Java) - java

I have written an Asynctask that loads 5 feeds from different URLs, writes all to the same file (via the WriteFeed method shown below), and then loads an activity only based on the first feed.
However, I am getting a android.os.TransactionTooLargeException: data parcel size 1052800 bytes, even though all five feeds together only have 70 feed items overall. Please note that I am launching the next activity onPostExecute only with the parsed first feed, and yet I get this Exception during the AsyncTask. How to run these feeds parallelly? Please help.
private class AsyncLoadXMLFeed extends AsyncTask<String, Void, Void> {
#Override
protected Void doInBackground(String... params) {
// Obtain feed
String feedlink1, feedlink2, feedlink3, feedlink4, feedlink5;
feedlink1=params[0];
feedlink2=params[1];
feedlink3=params[2];
feedlink4=params[3];
feedlink5=params[4];
Log.e("MY LINK",feedlink1);
try {
DOMParser myParser = new DOMParser();
feed = myParser.parseXml(feedlink1);
feed2 = myParser.parseXml(feedlink2);
feed3 = myParser.parseXml(feedlink3);
feed4 = myParser.parseXml(feedlink4);
feed5 = myParser.parseXml(feedlink5);
if (feed != null && feed2 != null && feed3 != null && feed4 != null && feed5 != null) {
WriteFeed(feed);
WriteFeed(feed2);
WriteFeed(feed3);
WriteFeed(feed4);
WriteFeed(feed5);
} else {
Log.e("FEED", "IS NULL");
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
startNextActivity(feed);
}
}

I think this is occurring because you are requesting 5 request at a time.
You can make some delay with every request like below:
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
feed = myParser.parseXml(feedlink1);
if(feed!=null)
WriteFeed(feed);
}
}, 1000);
Please let me know the result.

This is not caused by the parsing, it should be that the next activity is called with its intent data exceeding 1 MB size. The feed object which you are passing to startNextActivity() should be the main culprit.
This might be a bit slower to the end user but should help resolve the error. Instead of calling AsyncLoadXMLFeed on the calling activity, call it in the onCreate() of the called activity and modify the async as follows.
private class AsyncLoadXMLFeed extends AsyncTask<String, Void, Void> {
FeedListener fl;
interface FeedListener{
void onFeedParsed(Feed feed); //use appropriate class name
}
AsyncLoadXMLFeed(FeedListener fl){
this.fl=fl;
}
#Override
protected Void doInBackground(String... params) {
//No changes here
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
fl.onFeedParsed(feed); //the same feed object which was passed in startNewActivity.
}
on the activity which you are calling this async, you will get the feed object in onFeedParsed() then do the awesome stuff you plan to do with it.

Related

How does AsyncTask work one process to another one?

I'm currently studying android on my own and pretty new to java. I'm wondering how AsyncTask works like this: onPreExecute() -> doInBackground() -> onPostExecute(). When I look at others define their AsynTask, it seems like only method is declared in their code with no calls upon the method. I can't figure out how doInBackground() comes after onPreExecute() with no code that links both like:
onPreExecute(){ ~~~~~ call doInBackground()}
My point is that when AsyncTask.execute() is called, onPreExecute() is called, then doInBackground(), finally onPostExecute(). I couldn't find any code in library that actually connects these together. All I could find is this:
#MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
#MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
Here when AsyncTask.execute() is called, onPreExecute() is called. But without any connection to doInBackground the task works just fine. I feel like I'm missing some fundamental logic or process of java or android. Plz, help me with this unsolved question in mind. Sample code is shown below. Thank you in advance.
#Override
protected void onPreExecute() {
super.onPreExecute();
mLoadingIndicator.setVisibility(View.VISIBLE);
}
#Override
protected String[] doInBackground(String... params) {
/* If there's no zip code, there's nothing to look up. */
if (params.length == 0) {
return null;
}
String location = params[0];
URL weatherRequestUrl = NetworkUtils.buildUrl(location);
try {
String jsonWeatherResponse = NetworkUtils
.getResponseFromHttpUrl(weatherRequestUrl);
String[] simpleJsonWeatherData = OpenWeatherJsonUtils
.getSimpleWeatherStringsFromJson(MainActivity.this, jsonWeatherResponse);
return simpleJsonWeatherData;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(String[] weatherData) {
// COMPLETED (19) As soon as the data is finished loading, hide the loading indicator
mLoadingIndicator.setVisibility(View.INVISIBLE);
if (weatherData != null) {
// COMPLETED (11) If the weather data was not null, make sure the data view is visible
showWeatherDataView();
/*
* Iterate through the array and append the Strings to the TextView. The reason why we add
* the "\n\n\n" after the String is to give visual separation between each String in the
* TextView. Later, we'll learn about a better way to display lists of data.
*/
for (String weatherString : weatherData) {
mWeatherTextView.append((weatherString) + "\n\n\n");
}
} else {
// COMPLETED (10) If the weather data was null, show the error message
showErrorMessage();
}
I guess you shouldn't waste time on AsyncTask since it is deprecated.
Instead you should focus on coroutines, recommended by google here , or some other state of the art framework to achive what you want (e.g. rx java)
Yes, you are correct. The logic is onPreExecute() -> doInBackground() -> onPostExecute()
Synchronous VS asynchronous
You can read this article for a better understanding even though it's using Javascript to explain it.

How to run process inside loop after previous process is finished? Java

Long story short, i want to upload multiple images to my server using Retrofit 2. i want to loop the process of sending single image based on the size of List image but asynchronously, so second upload only run if the first upload is succeeded. Some people tell me i should send an Array of File to my server instead and parse the array there, but i want to know if there is an error while uploading in client side or not. that way if there is an error (network problem) on first loop, second loop will stop running.
I really don't have clear idea as for how to do the task above, but here is a start.
public class UploadAllImages extends AsyncTask<Void, Void ,Void>{
#Override
protected Void doInBackground(Void... params) {
doSingleUpload(image);
return;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
if(upload == succeeded){
new UploadAllImage().execute();
}
}
}
I would probably solve it like this:
public class UploadImages extends AsyncTask<Image, Integer, Boolean> {
#Override
protected Boolean doInBackground(Image... images) {
List<Image> remainingImages = new ArrayList<>(Arrays.asList(images));
while (!remainingImages.isEmpty()) {
boolean success = doSingleUpload(remainingImages.remove(0));
if (!success) {
return false;
}
}
return true;
}
#Override
protected void onPostExecute(Boolean success) {
// Handle the result of all uploads
}
}

Dynamically update an AsyncTask

QUESTION:
I have an AsyncTask with a public ArrayList and I wanna know if I can dynamically update this ArrayList without stop the Task.
The thing is that my task load information about the elements in his internal array, at the same time my activity can load more elements so I would like to know if I can push theses new elements into the task's array instead of creating a new task.
SOLUTION:
MY TASK:
public class TaskGetMatchesDetails extends AsyncTask<Void, MatchDetails, Void> {
private FragmentHistory fragmentHistory;
//Dynamic Data, Array where we have to add and remove elements.
private ArrayList<Match> matchesArrayList;
//Constructor
public TaskGetMatchesDetails(FragmentHistory f) {
this.fragmentHistory = f;
this.matchesArrayList = new ArrayList<>();
}
//SYNCHRONIZED METHODS
public synchronized void addMatch(Match match) {
if (this.matchesArrayList != null) {
this.matchesArrayList.add(match);
Log.d("TASK DETAILS", "ADDED MATCH: " + match.getMatchId());
}
}
public synchronized Match getFirsMatchFromArrayList() {
if (matchesArrayList.size() > 0) {
return matchesArrayList.get(0);
}
return null;
}
public synchronized void removeMatchFromArrayList(Match match) {
if (this.matchesArrayList != null) {
this.matchesArrayList.remove(match);
Log.d("TASK DETAILS", "REMOVED MATCH: " + match.getMatchId());
}
}
#Override
protected Void doInBackground(Void... params) {
Match match;
MatchDetails matchDetails;
while (!isCancelled()) {
//If we have not work to do continue
if (matchesArrayList.size() <= 0) {
continue;
}
//Get the work for this iteration
Match m = getFirsMatchFromArrayList();
//If we have already calculated this data we just jump to other cycle
if (fragmentHistory.getMatchDetails(m.getMatchId()) != null) {
removeMatchFromArrayList(m);
continue;
}
matchDetails = new MatchDetails();
//TODO: Here we have to proccess the data.
publishProgress(matchDetails);
removeMatchFromArrayList(m);
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
fragmentHistory.setTaskGetMatchesDetails(null);
cancel(true);
}
#Override
protected void onProgressUpdate(MatchDetails... matches) {
super.onProgressUpdate(matches);
//We save the data calculated in this fragment
fragmentHistory.addMatchDetails(matches[0]);
}
#Override
protected void onCancelled() {
super.onCancelled();
}
}
CREATE THE TASK IN THE FRAGMENT onCreate method:
taskGetMatchesDetails = new TaskGetMatchesDetails(this);
taskGetMatchesDetails.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
TO ADD A NEW ELEMENT:
taskGetMatchesDetails.addMatch(m);
TO CLOSE THE TASK YOU JUST HAVE TO:
taskGetMatchesDetails.cancel(true);
The answers are No and Very Carefully. No- you can't do this with a list without additional work. You'd need to either protect access to the data with a semaphore or used a synchronized list. Otherwise you could concurrently access the list leading to incorrect partial state. This is bad, especially if both are updating the list at once, that can lead to memory access errors and even crashes.
If you use a synchronized list or semaphore, you can access it but you need to write your algorithm carefully to avoid problems if items are removed/added in midstream. But answering how to do that is awfully broad, you'd need to give us a more concrete algorithm to do so.

Is it possible to run the "AsyncTask" method inside another method "AsyncTask"?

I have an activity that when started makes a call to a "json" for get data categories of songs, after that I make a call to the method "AsyncTask" for the list of songs that category from another "JSON "the problem is that when I start the activity, this is locked , after 2 seconds, the activity opens the layout and I can see the categories on the action bar and not because the songs are looking for in the background.
main activity (onCreate):
java.io.InputStream source = null;
source = retrieveStream(UrlApi.URL_BASE + UrlApi.URL_STORE + _bundle.getString("_id") + UrlApi.CATEGORY_SONG);
Log.i("URL - KARAOKE", UrlApi.URL_BASE + UrlApi.URL_STORE + _bundle.getString("_id") + UrlApi.CATEGORY_SONG);
Reader reader = new InputStreamReader(source);
Type happyCollection = new TypeToken<Collection<String>>() {}.getType();
_karaoke_category_response = new Gson().fromJson(reader, happyCollection);
if(_karaoke_category_response.size() < 1){
finish();
Toast.makeText(getApplicationContext(), "Local sin karaokes", Toast.LENGTH_SHORT).show();
}else{
Log.i("Category - response", _karaoke_category_response.toString());
_karaoke_category_adapter = new ArrayAdapter<String>(getSupportActionBar().getThemedContext(), R.layout.spinner_item,_karaoke_category_response);
getSupportActionBar().setListNavigationCallbacks(_karaoke_category_adapter, this);
}
The follow code is of search the songs of that categori and set it
class AsyncKaraoke extends AsyncTask<Void, Void, Void> {
String category;
public AsyncKaraoke(String category) {
this.category = category;
}
protected void onPreExecute(){
super.onPreExecute();
setSupportProgressBarIndeterminateVisibility(true);
}
#Override
protected Void doInBackground(Void... params) {
java.io.InputStream source = null;
try {
source = retrieveStream(UrlApi.URL_BASE + UrlApi.URL_STORE + _bundle.getString("_id") + UrlApi.KARAOKE_URL + UrlApi.FILTER_CATEGORY + URLEncoder.encode(category, "UTF-8"));
Log.i("URL - KARAOKE", UrlApi.URL_BASE + UrlApi.URL_STORE + _bundle.getString("_id") + UrlApi.KARAOKE_URL + UrlApi.FILTER_CATEGORY + URLEncoder.encode(category, "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Reader reader = new InputStreamReader(source);
Type karaokeCollection = new TypeToken<Collection<KaraokeModel>>() {}.getType();
_response = new Gson().fromJson(reader, karaokeCollection);
Log.i("Response - KaraokeCategory" , _karaoke_category_response.toString());
return null;
}
protected void onPostExecute(Void Void){
super.onPostExecute(Void);
setSupportProgressBarIndeterminateVisibility(false);
_karaoke_adapter = new KaraokeAdapter(KaraokeActivity.this, _bundle.getString("_id"), _response);
if(_response.size() == 0){
Toast.makeText(getApplicationContext(), "Categoria sin karaoke", Toast.LENGTH_SHORT).show();
}
_list_view.setAdapter(_karaoke_adapter);
_karaoke_adapter.notifyDataSetChanged();
}
}
How should I do to call 2 times to "AsyncTask" method and prevent the activity is engaged by a few seconds?
The primary rule of AsyncTask is that it must always be create and run on the main thread. You will get an exception if you start another AsyncTask inside the doInBackground() method. Your options are to start the next AsyncTask in one of the callbacks. Generally, some people will chain AsyncTask in the onPostExecute() method, but you can also start them in onPreExecute() and onProgressUpdate().
EDIT:
Additionally, you can run AsyncTask in sequence of each other using AsyncTask#executeOnExecutor(). From HoneyComb on, you don't need to do this. All AsyncTask run in a serial thread pool in the order they are executed. Though it may be easier to understand that the code is running serially if you use it. You do need to chain if using Android Android 1.6 - 2.3.x though.
You should build the URL in the main activity, then run an AsyncTask to download the content and finally process the result back in your activity.
The syntax to run an AsyncTask is:
String category = "...";
new AsyncKaraoke().execute(category);
You can also remove the onPostExecute() method from your AsyncKaraoke class and put it in the activity:
String category = "...";
new AsyncKaraoke() {
#Override
protected void onPostExecute(Void Void){
// do stuff (and moving the third type of the AsyncKaraoke to something else
// than Void will allow you to get the result here.
}.execute(category);
Generally, we use AsyncTask to perform an action in another thread than the UI thread to prevent the user from being halt while performing some actions. SO, it does not make any sense to create an additional AsyncTask inside the outer one. Try to manage your code to do it all the those method soInBackground(), onPreExecution() and onPostExecution() and make use of their order of execution
int count = 0;
protected void onPostExecute(Void Void){
super.onPostExecute(Void);
// call same asynctask
if (count == 0)
{
execute asynctask
count++;
}
}

Unable to call methods from onPostExecute

I am using the an asynchronous task to run a JSON downloader as thus: (abridged)
public class JSONDownloader extends AsyncTask<Object, Object, Object>{
#Override
protected Object doInBackground(Object... params) {
if(JSONstate == false){
try {
final URL url = new URL([REDACTED]);
final URLConnection urlConnection = url.openConnection();
urlConnection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
urlConnection.connect();
final InputStream inputStream = urlConnection.getInputStream();
final StringBuilder sb = new StringBuilder();
while (inputStream.available() > 0) {
sb.append((char) inputStream.read());
}
String result = sb.toString();
JSONObject jsonOrg = new JSONObject(result);
String ok = "ok";
Response = jsonOrg.getString("response");
System.out.println(Response);
if(Response.equals(ok)){
Settingsresponse = true;
orgName = jsonOrg.getString("orgName");
System.out.println("orgName" + orgName);
accessPointName = jsonOrg.getString("attendanceRecorderName");
System.out.println("accessPointName" + accessPointName);
lat = jsonOrg.getString("latitude");
System.out.println("lat" + lat);
longi = jsonOrg.getString("longitude");
System.out.println("longi" + longi);
floor = jsonOrg.getString("floor");
System.out.println("floor" + floor);
orgId = jsonOrg.getString("orgId");
System.out.println("orgId" + orgId);
}
else{
System.out.println("Data sent was erroneous");
Settingsresponse = false;
}
} catch (Exception e) {
System.err.print(e);
}
}
else if(JSONstate == true){
try {
[redacted]
}
else{
System.out.println("Data sent was erroneous");
Settingsresponse = false;
}
} catch (Exception e) {
System.err.print(e);
}
}
return null;
}
protected void onPostExecute(Void result){
if(JSONstate == false){
System.out.println("This piece of code is definitely being run");
setfields();
}
else if(JSONstate == true){
settestfields();
//This method does not run upon the completion of the JSON request, as it supposedly should
}
}
}
Once the JSONRequest has been completed, the 'onPostExecute' method doesn't run. I have been attempting to use this method so that a set of fields can be updated as soon as the request is complete, instead of having to set a definite wait time. Am I simply utilizing the code wrong? Or is there something I've missed?
You aren't overriding the correct method for onPostExecute.
You have:
protected void onPostExecute(Void result)
You need:
protected void onPostExecute(Object result)
Notice the third generic parameter you supplied was of type Object. That's the type that onPostExecute uses as an argument. So, the method signature for onPostExecute needs to accept an Object, not Void.
You should probably use a result type of boolean here rather than object, and remove the Json state class variable. This keeps your AsyncTask more flexible, and could allow you to display some indication the operation completed to the user after execution.
I have to say you codes in AsyncTask is nothing matches the point.
AsyncTask is designed as another thread running out from the UI-thread. So you should either use it as a inner class which is in a running UI-thread, then the onPostExecute() part can do something to show the result, or you as your codes, if you leave it as a stand alone class. You should design an interface, other class, like activity or fragment, which run new AsyncTask.execute() should implements that interface.
Also, java is not javascript. Your variables in doInBackground() is only limited in the function. So what you did in onPostExecute() will get nothing.
You should either use
JSONObject jsonOrg
as a class variable or you should return that at the end of doInBackground() and gain it back in onPostExecute()
After all, I suggest you look at the api document's example. Although it is a little complex, but it shows everything perfect.
try to use override methods
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
Log.i("in on ", "entered");
hideProgress();
}
As suggested by william the type should match with the override methods. I have edited the answer below
public class JSONDownloader extends AsyncTask<Object, Object, Object>
#Override
protected void onPostExecute(Object result) {
super.onPostExecute(result);
}

Categories

Resources