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.
Related
I am new in programming and in Android development. I have 3 asynchronous method to get data from server in my MainActivity, let say it is called
getUserDataFromServer()
getProductsDataFromServer()
getBannersFromServer()
if every request takes 1 second, then it needs 3 seconds to complete those 3 request If I chain it one after the other (in series).
so what I want is.... I want to make those 3 request asynchronously (in parallel) then if those 3 request has been done (either failed or success) then I want to do something else, let say to show up the Toast message. so I can finish it faster, maybe it just need around 1,2 s, not 3 s.
I don't know how to do it or what the special method called to wrap it in Android ?
how to do that in Java or Kotlin ?
The following code should help you get started for your purposes. It also has explanations of what is happening. You can change the parameters as required:
Executing the Task:
MyTask myTask = new MyTask();
myTask.execute(String1);
//OR:
new MyTask().execute(String1, String2, String3...);
Creating the Task:
//The first type in AsyncTask<> is for specifying the type of input given.
//Second parameter: Type of data to give to onProgressUpdate.
//Third parameter: Type of data to give to onPostExecute.
private class MyTask extends AsyncTask<String, String, String> {
private String resp;
ProgressDialog progressDialog;
#Override
protected String doInBackground(String... params) {
publishProgress("Processing ..."); // Calls onProgressUpdate()
//params is the input you've given that can be used for processing.
getUserDataFromServer()
getProductsDataFromServer()
getBannersFromServer()
//Result is the String to give onPostExecute when the task is done executing.
String result = "Done Processing";
return result;
}
#Override
protected void onPostExecute(String result) {
// Get the result from your task after it is done running.
progressDialog.dismiss();
//IMPORTANT: As you asked in your question, you can now execute whatever code you
//want since the task is done running.
}
#Override
protected void onProgressUpdate(String... text) {
//Progress has been updated. You can update the proggressDialog.
}
}
I am using an AsyncTask as an inner class to save a bitmap. But the FileOutputStream operation does not work. If I run the same code in a method in the UI thread the code runs and I am able to successfully save the bitmap.
I know that the doInBackground() method does execute and the onPostExecute() method gets executed as well.
Also, if I take the input stream which is passed to the AsyncTask, and try and decode it to set an imageView, it does not work. But if I use the same code outside of the AsyncTask it works.
I implemented the AsyncTask as follows and the 2nd and 3rd log statements do not get logged, so I know something is not running properly:
public class SaveImageToInternalStorage extends AsyncTask {
InputStream bitmap;
String PICTURE_KEY;
public SaveImageToInternalStorage1(final InputStream bitmap, final String PICTURE_KEY) {
this.bitmap = bitmap;
this.PICTURE_KEY = PICTURE_KEY;
}
#Override
protected Object doInBackground(Object[] params) {
FileOutputStream fos;
try {
fos = picture_chooser.this.openFileOutput(PICTURE_KEY, MODE_PRIVATE);
Bitmap bitmap2 = BitmapFactory.decodeStream(bitmap);
Log.v("saveBitmap", " first log statement"); ////This gets logged
bitmap2.compress(Bitmap.CompressFormat.WEBP, 85, fos);
Log.v("saveBitmap", " second log statement"); // This is not logged
fos.close();
Log.v("saveBitmap", " third log statement"); // This is not logged
} catch (Exception e) {
Log.e("saveToInternalStorage()", e.getMessage());
}
return null;
}
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
Log.v("saveBitmap", " onPostExecute log statement"); // This is logged
imageViewSetter(bitmap);
}
}
//runs in the wrapper class
public void imageViewSetter(InputStream inputStream) {
imageView.setImageBitmap(BitmapFactory.decodeStream(inputStream)); //this does not set the imageView
}
Any help would be greatly appreciated.
Have a look at this example from the Android reference here
I've recreated the code for you below
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
Notice the class declaration at the top is followed by
<URL, Integer, Long>
Next, look at each of the three overriden methods inside the class. You will notice that URL refers to the argument passed in to doInBackground, Integer is what is output by onProgressUpdate and Long is what is returned by your doInBackground method to onPostExecute
From this you can hopefully see that AsyncTask should be used as follows
doInBackground - pass in whatever it is you want to do some work on. This method does the work you want on a background thread. This method needs to return the finished object(s) toonPostExecute which is where you can catch the results of the work done and update the UI if you want to.
onProgressUpdate is optional, you don't need to do anything here.
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++;
}
}
I've just gotten into android development, and while trying to create a login form i ran into some problems.
What I want to do is enter username and password and press login, when the login button is pushed I want it to do a JSON request with the account information to my server and get a response with whether or not the user is allowed to log in. If the user is allowed, I want to change to a new view.
My code receives the JSON information correctly, but from what I've understood the UI-code (pushing a new activity) should be done in onPostExecute(). My problem is that onPostExecute is never run, I've looked at other ppl with the same problem, but their solutions hasn't worked for me. What they have said is that i need to have an #Override before onPostExecute, but when I add that i get the compilation error that "the method does not override method from its superclass".
I've read solutions from people having that problem as well, and from what I have read the problem is that the method onPostExecute has to have the same parameters as the result parameter from doInBackground(). My problem is that I feel I already do, and when I try to look in what the superclass has (that is AsyncTask.java) it says that the method onPostExecute() looks like:
protected void onPostExecute(Result result) {
}
But I have no idea what class Result belongs to..
networkingTask is run using this line:
new networkingTask().execute(url);
If anyone could help me I'd be eternally grateful! :)
This is what my code looks like
private class networkingTask extends AsyncTask {
Context context;
private networkingTask(Context context) {
this.context = context.getApplicationContext();
}
public networkingTask() {
}
#Override
protected JSONObject doInBackground(Object... params) {
try {
String urlString = (String) params[0];
System.out.println(urlString);
// Creating JSON Parser instance
JSONParser jParser = new JSONParser();
// getting JSON string from URL
JSONObject json;
json = jParser.getJSONFromUrl(urlString);
String responseLogged = json.getString("logged");
System.out.println("can we log in?: "+ responseLogged);
return json;
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(JSONObject result) {
String responseLogged = "";
try {
responseLogged = result.getString("logged");
} catch (JSONException e) {
e.printStackTrace();
}
if(responseLogged.equals("true")){ //Login = true
Intent intent = new Intent(context, ShowListActivity.class);
intent.putExtra(EXTRA_JSON_OBJECT, result.toString());
startActivity(intent);
} else{ //Login = false
System.out.println("wrong password");
}
return;
}
}
In your line:
private class networkingTask extends AsyncTask
just change it to:
private class networkingTask extends AsyncTask<String, Void, JSONObject>
while String is the place for you to pass in the parameters, in your case it is url, the second parameter Void is for showing progress and the last one is the processed result to be passed from doInBackground to onPostExecute
For further explanation & info, please refer to Android Developers: AsyncTask
I think you may need to fill out the generic types for your AsyncTask. See the "Usage" section in the AsyncTask documentation.
Your class signature should look something like this:
private class networkingTask extends AsyncTask<Object, Void, JSONObject>
The types in the brackets here correspond to the argument types for doInBackground, onProgressUpdate, and onPostExecute, respectively, and are necessary if you're going to override these methods such that they are different from the generic method signatures.
Also, as a side note, it's a common convention in Java/Android to use upper CamelCase to start a class name. I'd also change the name of your class to "NetworkingTask" to help other developers looking at your code to better understand it.
The signatures don't match. You're attempting to override:
protected void onPostExecute(Result result)
with the signature
protected void onPostExecute(JSONObject result)
...which doesn't exist in the superclass.
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);
}