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
}
}
Related
I'm using this library to validate URL within my app but I wanna run the same method for 4 or more URLs instead of one but can't seem to figure out how to.
My code:
validateUrl(this, "https://www.dummy.dummy/");
SharedPreferences sharedPreferences = getSharedPreferences("url", MODE_PRIVATE);
if (sharedPreferences.getBoolean("validated", true)) {
Log.e("WEB", "RUNNING");
doSomething():
} else {
Log.e("WEB", "DOWN");
}
private void validateUrl (Context context, String URL) {
new FarlaGetRequest(context)
.setURL(URL)
.setListener(new FarlaGetRequest.onGetRequestListener() {
#Override
public void onSuccess(String response) {
Log.e("WEB", "Done");
SharedPreferences.Editor editor = getSharedPreferences("url", MODE_PRIVATE).edit();
editor.putBoolean("validated",true);
editor.apply();
}
#Override
public void onFailure(int error) {
Log.e("WEB", "Failed");
}
}).execute();
}
The goal is, if the url is able to connect (server sent response 200) then do something, else don't.
So what I'm stuck at is, how do I do this for multiple URLs?
Example:
Check 1st (log if it's running or down)
Check 2nd (log if its running or down)
Same for the 3rd and 4th as well.
At the end, it should say which are active and which ones aren't so can someone help me please? ease?
you can use a List of url's and execute that validator function for the number of times that the size of list is and if the url connects (got response 200) then just move to the next url otherwise remove that url from list. So , in this way you will end up with all the working URL's.
Solution Code:
List<String> urlList = new ArrayList<>();
// add all url's in the list; for example : urlList.add("https://www.dummy.dummy/");
Now we will use a loop to execute the checker function for all the url's
for(int i =0; i<urlList.size();i++)
{
validateUrl (context, urlList.get(i))();
}
and in the YOUR_URL_CHECKER_FUNCTION just put a condition that if the server sents response as 200 then do nothing otherwise urlList .remove("https://www.dummy.dummy/")
Sample code:
private void validateUrl (Context context, String URL) {
new FarlaGetRequest(context)
.setURL(URL)
.setListener(new FarlaGetRequest.onGetRequestListener() {
#Override
public void onSuccess(String response) {
// HERE YOU CAN CHECK THAT WHAT'S THE RESPONSE**
if(!response.equals("200"))
{
`urlList .remove("https://www.dummy.dummy/")`// link to remove
}
}
#Override
public void onFailure(int error) {
Log.e("WEB", "Failed");
}
}).execute();
}
Hope you get that. Feel free to ask if something is unclear. And kindly mark it as the correct answer if it helps you so that this answer can help any other needy.😀
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.
I am looking at a code that I have to work on. And basically I have to add a validation to a listener of a button.
The code has already multiple validations. They are kind of set in a cascade.
The listener of the buttons calls an asyncCallBack method that if everything is ok, on the onsuccess part of the method calls for the next one, an that one on the next one, until it reaches the end and goes to the next page. I am not a fan of this approach because it is kind of messy. What would the best way to do that using best practices.
An example of the code:
Button btnOK = new Button("Aceptar");
btnOK.addListener(Events.Select, new Listener<ButtonEvent>() {
public void handleEvent(ButtonEvent e) {
myService.getInfo1(1, txt, "N",
new AsyncCallback<List<InfoService>>() {
public void onFailure(Throwable caught) {
// goes back
return
}
public void onSuccess(
List<Object> result) {
// do some validation with the result
validation2();
}
}
}
}
public void validation2(){
myService.getDireccionCanalesElectronicos(id, new AsyncCallback<MyResult>() {
public void onSuccess(MyResult result) {
// do some validation with the result
validation3();
}
...
}
}
public void validation3(){
myService.getDireccionCanalesElectronicos(id, new AsyncCallback<MyResult>() {
public void onSuccess(MyResult result) {
// do some validation with the result
validation4();
}
...
}
}
Is there a better way of doing this, it seems messy and hard to follow. Adding another validation is complicated. It doesnt seem like a good practice.
Create 1 method in the servlet that calls all the validation methods and do just one call in the client ?
public void validation()
{
boolean ok = validation1();
if (ok) ok = validation2();
return validation;
}
Using mirco services is sometimes hard to deal with. As #Knarf mentioned, this is a way to go. But sometime you may want to handle the calls on the client side. Another one will be using this tiny framework: sema4g. It will help you to solve your problem.
A solution might look like that:
First create the sem4g commands:
private SeMa4gCommand createGetInfoCommand() {
return new AsyncCommand() {
// create callback
MethodCallbackProxy<List<InfoService>> proxy = new MethodCallbackProxy<List<InfoService>>(this) {
#Override
protected void onProxyFailure(Method method,
Throwable caught) {
// Enter here the code, that will
// be executed in case of failure
}
#Override
protected void onProxySuccess(Method method,
List<InfoService> response) {
// Enter here the code, that will
// be executed in case of success
}
};
#Override
public void execute() {
// That's the place for the server call ...
myService.getInfo1(1, txt, "N", proxy);
}
};
}
do that for all your calls;
private SeMa4gCommand createCommandGetDireccionCanalesElectronicos() {
return new AsyncCommand() {
// create callback
MethodCallbackProxy<MyResult> proxy = new MethodCallbackProxy<MyResult>(this) {
#Override
protected void onProxyFailure(Method method,
Throwable caught) {
// Enter here the code, that will
// be executed in case of failure
}
#Override
protected void onProxySuccess(Method method,
List<MyResult> response) {
// Enter here the code, that will
// be executed in case of success
}
};
#Override
public void execute() {
// That's the place for the server call ...
myService. getDireccionCanalesElectronicos(id, proxy);
}
};
}
Once you have done this for all your calls, create a sema4g context and run it:
try {
SeMa4g.builder()
.addInitCommand(new InitCommand() {
#Override
public void onStart() {
// Enter here your code, that
// should be executed when
// the context is started
})
.addFinalCommand(new FinalCommand() {
#Override
public void onSuccess() {
// Enter here the code, that will
// be executed in case the context
// ended without error
}
#Override
public void onFailure() {
// Enter here the code, that will
// be executed in case the context
// ended with an error
})
.add(createGetInfoCommand())
.add(createCommandGetDireccionCanalesElectronicos())
.build()
.run();
} catch (SeMa4gException e) {
// Ups, something wrong with the context ...
}
For more informations, read the documentation. If you have questions, feel free to ask: SeMa4g Gitter room.
Hope that helps.
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.
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.