How can I make an AsyncTask's doInBackground() only return when another async method completes?
On a high level, the Activity's purpose is to allow the user to upload a review. The review may also have images. Now in my code, I have two AsyncTasks: ImageCompressionTask and ImageUploadTask. Basically, once the user selects images to upload, ImageCompressionTask is executed for each image. In the onPostExecute() method of that task, ImageUploadTask is executed. The purpose of ImageUploadTask is to upload the compressed image and update the Firestore (database) with the download URL of the aforementioned uploaded image. Following is its code:
public class ImageUploadTask extends AsyncTask<byte[], Integer, Void> {
#Override
protected Void doInBackground(byte[]... bytes) {
StorageReference ref = App.getFireStorage().getReference();
ref = ref.child("review_images/" +
mDestinationId + "/" +
mExistingReview.getReviewId() + "/" +
mUploadProgressCount);
ref.putBytes(bytes[0]).addOnSuccessListener(taskSnapshot -> {
String url = taskSnapshot.getDownloadUrl().toString();
mExistingReview.getImages().add(url);
App.getFirestore().collection("reviews").document(mExistingReview.getReviewId()).set(mExistingReview);
mUploadProgressCount++;
});
return null;
}
}
However, the issue is that the doInBackground() returns prematurely. I understand WHY it happens (because the ref.putBytes() method runs asynchronously) but I want it to wait until ref.putBytes() finishes. How can I do that?
Can you please check it this way :
#Override
protected synchronized Void doInBackground(byte[]... bytes) {
StorageReference ref = App.getFireStorage().getReference();
ref = ref.child("review_images/" +
mDestinationId + "/" +
mExistingReview.getReviewId() + "/" +
mUploadProgressCount);
ref.putBytes(bytes[0]).addOnSuccessListener(taskSnapshot -> {
String url = taskSnapshot.getDownloadUrl().toString();
mExistingReview.getImages().add(url);
App.getFirestore().collection("reviews").document(mExistingReview.getReviewId()).set(mExistingReview);
mUploadProgressCount++;
});
return null;
}
i have solved this problem with ObservableInteger
is a listener that listen for values
private ObservableInteger mObsInt;
//Listener
mObsInt = new ObservableInteger();
mObsInt.set(0);
mObsInt.setOnIntegerChangeListener(new OnIntegerChangeListener()
{
#Override
public void onIntegerChanged(int newValue)
{
if (mObsInt.get()==1)
Log.e("Downloads"," mObsInt 1");
Log.e("Download1"," Finished first process ");
if (mObsInt.get()==2){
Log.e("Downloads"," mObsInt 2");
Log.e("Download2"," Finished second process ");
mProgressDialog.dismiss();
Intent mainIntent = new Intent().setClass(LoginActivity.this, Principal.class);
startActivity(mainIntent);
finish();
}
}
});
and then just do this (after a process has finished or an asynctask)
mObsInt.set(mObsInt.get()+1);
so it will count, if the first thing finish obsInt will be 1 , and when the second one finish, obsInt will be 2, so after obsInt == 2 , you can move on to the other activity or process you need
happy coding !
This can be accomplished with CountDownLatch:
public class ImageUploadTask extends AsyncTask<byte[], Integer, Void> {
#Override
protected Void doInBackground(byte[]... bytes) {
// Initialize CountDownLatch
final CountDownLatch signal = new CountDownLatch(1);
StorageReference ref = App.getFireStorage().getReference();
ref = ref.child("review_images/" +
mDestinationId + "/" +
mExistingReview.getReviewId() + "/" +
mUploadProgressCount);
ref.putBytes(bytes[0]).addOnSuccessListener(taskSnapshot -> {
String url = taskSnapshot.getDownloadUrl().toString();
mExistingReview.getImages().add(url);
App.getFirestore().collection("reviews").document(mExistingReview.getReviewId()).set(mExistingReview);
mUploadProgressCount++;
// Start count down
signal.countDown();
});
// Wait for putBytes to return and handle case if
// threads get interrupted.
// You can also specify a maximum time to wait before
// displaying error to user (ie Try Again)
try {
signal.await(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
Related
I have some pics to upload to the ftp server and I am using Asynctask for it.The images need to be sent to multiple host so I am using a for loop.The data to be passed is very well being fetched by the constructor but the doInBackground method is not running which was earlier running very well without the for loop and the additional data apart from the String filePathName that I am trying to pass in now in doInBackground.please help me
class uploadTask extends AsyncTask<String, Void, String> {
public uploadTask(String filePathName,String host_2,String user_2,String pass_2)
{
filePath=filePathName;
host_1=host_2;
user_1=user_2;
pass_1=pass_2;
Toast.makeText(getBaseContext(),"FTP DATA RECEIVING:"+"HOST:"+host_2+" USERNAME:"+user_2+" PASS:"+pass_2,Toast.LENGTH_LONG).show();
//hostName=host;
}
#Override
protected String doInBackground(String... params) {
try {
Toast.makeText(getBaseContext(),"Entered Do in Background Method to upload",Toast.LENGTH_SHORT).show();
ftp_host = "ftp.photoshelter.com";//This is not the correct way. Supposed to get from Backendless table
ftp_username = "brytest";//This is not the correct way. Supposed to get from Backendless table
ftp_password = "passtest";//This is not the correct way. Supposed to get from Backendless table
Toast.makeText(getBaseContext(),"HOST:"+ftp_host+" USERNAME:"+ftp_username+" PASS:"+ftp_password,Toast.LENGTH_LONG).show();
news_agency = "news agency";
easyFTP ftp = new easyFTP();
ftp.connect(ftp_host, ftp_username, ftp_password);
status = ftp.setWorkingDirectory("mem/images"); // if User say provided any Destination then Set it , otherwise
// Upload will be stored on Default /root level on server
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageTimeStamped = ftp_username + "_" + timeStamp + ".png";
FileInputStream is = new FileInputStream(imageFileLocation);
//addPhotoGrapherInfo();
ftp.uploadFile(is, imageTimeStamped);
System.out.println("Successfull ftp upload to " + ftp_host);
Toast.makeText(getBaseContext(), "Photo uploading by ftp to " + ftp_host, Toast.LENGTH_LONG).show();
//}
//reset booleans
//cameraPicTaken = false;
//galleryImageSelected = false;
//System.out.println("reset cameraPicTaken and galleryImageSelected");
// }
return new String("Upload Successful");
}catch (Exception e){
String t="Failure : " + e.getLocalizedMessage();
return t;
}
}
}
my onClickListener with for loop
if(cameraPicTaken || galleryImageSelected) {
Toast.makeText(SubmitActivity.this,"Image Location is:"+ imageFileLocation,Toast.LENGTH_LONG).show();
//addPhotoGrapherInfo();
for(int i=0;i<Common.selectedHostArray.size();i++) {
uploadFile(imageFileLocation,Common.selectedHostArray.get(i),Common.selectedUsernameArray.get(i),Common.selectedPasswordArray.get(i));
}
cameraPicTaken = false;
galleryImageSelected = false;
}
funnction called in onClick
public void uploadFile(String filePath,String host_1,String user_1,String pass_1)
{
if(cameraPicTaken == true) {
System.out.println("camera photo start upload");
//for(int i=0;i<Common.selectedHostArray.size();i++) {
//host_1=Common.selectedHostArray.get(i);
//user_1=Common.selectedUsernameArray.get(i);
//pass_1=Common.selectedPasswordArray.get(i);
//host_1="ftp.photoshelter.com";
//user_1="brytest";
//pass_1="passtest";
Toast.makeText(getBaseContext(),"FTP DATA PASSING:"+"HOST:"+host_1+" USERNAME:"+user_1+" PASS:"+pass_1,Toast.LENGTH_LONG).show();
new uploadTask(filePath,host_1,user_1,pass_1).execute();
// }
//cameraPicTaken = false;
//galleryImageSelected = false;
System.out.println("reset cameraPicTaken and galleryImageSelected");
//cameraPicTaken = false;
}
if(galleryImageSelected == true){
System.out.println("gallery image start upload");
Toast.makeText(getBaseContext(),"FTP DATA PASSING:"+"HOST:"+host_1+" USERNAME:"+user_1+" PASS:"+pass_1,Toast.LENGTH_LONG).show();
new uploadTask(filePath,host_1,user_1,pass_1).execute();
//new uploadTask(filePat)h.execute();
//galleryImageSelected = false;
}
Toast.makeText(getBaseContext(), "Photo uploading by ftp to photoshelter.com" /*+ news_agency*/, Toast.LENGTH_LONG).show();
}
You're trying to perform a UI command on a background thread (Toast). This is causing your background tasks to fail early. Since your background tasks catch their own errors, they fail silently.
#Override
protected String doInBackground(String... params) {
try {
// you can't Toast on a background thread, this should throw an exception
Toast.makeText(getBaseContext(),"Entered Do in Background Method to upload",Toast.LENGTH_SHORT).show();
...
}catch (Exception e){
// your Toast exception is getting caught silently here
String t="Failure : " + e.getLocalizedMessage();
return t;
}
}
By the way, the try/catch on everything is not a good practice. You end up with a ton of silent failures leaving you scratching your head and asking why things aren't working.
This is the method which I use to get values.
#Override
protected Void doInBackground(String... params) {
try {
Intent intent = getIntent();
String dvlaNumFin = intent.getStringExtra("dvlaNumber");
final TextView outputView = (TextView) findViewById(R.id.showOutput);
final URL url = new URL("https://dvlasearch.appspot.com/DvlaSearch?licencePlate="+dvlaNumFin+"&apikey=");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("USER-AGENT", "Mozilla/5.0");
connection.setRequestProperty("ACCEPT-LANGUAGE", "en-US,en;0.5");
final StringBuilder output = new StringBuilder(String.valueOf(url));
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line = "";
StringBuilder responseOutput = new StringBuilder();
System.out.println("output===============" + br);
while ((line = br.readLine()) != null) {
responseOutput.append(line);
}
br.close();
HandleJSON obj = new HandleJSON("");
obj.readAndParseJSON(responseOutput.toString());
output.append(System.getProperty("line.separator") + "\n" + System.getProperty("line.separator") + "Make : " + obj.getMake() + "\nModel : " + obj.getModel());
output.append("\nSix Month Rate : " + obj.getSixMonthRate() + "\nTwelve Month Rate : " + obj.getTwelveMonthRate() + "\nDate of First Registration : " + obj.getDateofFirstRegistrationegistration());
output.append("\nYear of Manufacture : " + obj.getYearOfManufacture() + "\nCylinder Capacity : " + obj.getCylinderCapacity() + "\nCO2 Emmissions : " + obj.getCo2Emissions());
output.append("\nVIN number : " + obj.getVin() + "\nTransmission type : " + obj.getTransmission());
DVLAresult.this.runOnUiThread(new Runnable() {
#Override
public void run() {
outputView.setText(output);
progress.dismiss();
}
});
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
I would like to use obj.getMake() and so on, the values from JSON. But do not understand how to do it, or return it. I know should be return value, or by using final.
Nice and simple. Make your AsyncTask return a value:
public class TestClass extends AsyncTask<Void, Void, String>{
#Override
protected String doInBackground(String... params) {
//rest of code
return output.toString();
}
}
Now all you have to do is call .get() method after calling .execute()
TestClass tc = new TestClass();
tc.execute();
String output = tc.get();
Very Very Important Note
By calling .get() right after .execute() your UI thread will be blocked until the AsyncTask is done. This is counter intuitive to the purpose of AsyncTask. One of the solutions to this problem is adding a callback interface to the AsyncTask which will be called upon finishing and call the .get() method in the implementation of that interface. For an example on how to design a callback interface see here.
FOG
Simply implement onPostExecute inside your AsyncTask class :)
for example :
#Override
protected void onPostExecute(String makeValue) {
// remember this method gets called on main thread
letsCallFogsMethod(makeValue); //call your method and pass the make value here :)
}
Thats it buddy :)
Now how come this onPostExecute is getting any value???
You have to return it from doInBackground method dude :)
like
#Override
protected String doInBackground(String... params) {
//after all bra bla simply say
return obj.getMake();
}
Do you notice any change in your doInBackground signature buddy?? Yeah I changed from Void to String :)
By writing String you are informing that when you are done executing doInBackground you will return a string to onPostExecute :)
So if I write as it is in the answer will it work ?? Nope.
Given that you have specified Void in your doInBackground your Async task signature might look something like
private class FogsAsyncTask extends AsyncTask<bla,blah,Void> {
Can you see the last Void??? :) But now you have chnaged doInBackground isn't it so update the AsyncTask signature as well :)
private class FogsAsyncTask extends AsyncTask<bla,blah,String> {
Now it should work fine :) Happy coding buddy :) Hope my answer helped you :)
You can get the output on onPostExecute method just override the method and get the output on it
AsyncTask has three (main) methods, onPreExecute, doInBackground and onPostExecute. Only doInBackGround runs on a background thread, the other two run on the UI thread. (there is also onProgressUpdate but I will skip it here)
In your case, return anything you want in doInBackground method. That return value will be the input param for onPostExecute. There you can call any other (reachable) method you want. Mind that you'd be running inside the UI thread at that time.
I have to download a Json with a list of files, and then parallel download the files in the list. I would like to update periodically the ProgressDialog, so I implemented in this way
I create and show the dialog
I start an AsyncTask
onProgressUpdate receives 2 Integers, current progress and max progress, and updates the progress bar
doInBackground
downloads the json file and obtains the list of files to download
creates a ThreadPoolExecutor (tpe), with a LinkedBlockingQueue<Runnable>
submit a runnable for each file, that download the file to disk using Apache commons-io FileUtils.copyURLToFile
exec shutdown
in a while cycle. tpe.awaitTermination(1, TimeUnit.SECONDS) invokes periodically publishProgress( (int) tpe.getCompletedTaskCount(), tot), to update the progress bar
onPostExecute hides and dismisses the progres bar, and manages the files downloades
is there any problem in using ThreadPoolExecutor inside an AsynTask?
I am discussing with a colleague who claims that there could be problems in the threads management, that could deadlock, and that might give us problems on future versions
that's the code
public static void syncFiles(...)
{
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
sWakelock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
sWakelock.acquire();
sProgress = new ProgressDialog(context);
sProgress.setCancelable(false);
sProgress.setTitle("MyTitle");
sProgress.setMessage("Sincronizzazione in corso");
sProgress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
sProgress.setIndeterminate(false);
sProgress.show();
sCurrentTask = new AsyncTask<Void, Integer, Manifest>()
{
#Override
protected void onCancelled()
{
if ((sProgress != null) && sProgress.isShowing())
sProgress.dismiss();
if ((sWakelock != null) && sWakelock.isHeld())
sWakelock.release();
};
#Override
protected Manifest doInBackground(Void... params)
{
ArrayList files = getFiles(....)// download the jsonfile, and return the list of files
final String baseurl = ... // get the remote base url
final String baselocal = ... //get the local base path ;
int tot = m.size();
publishProgress(0, tot);
final int MAX_THREADS = Runtime.getRuntime().availableProcessors(); * 4;
ThreadPoolExecutor tpe = new ThreadPoolExecutor(
MAX_THREADS,
MAX_THREADS,
1,
TimeUnit.MINUTES,
new LinkedBlockingQueue<Runnable>()
);
for (final String s: files)
{
tpe.submit(new Runnable()
{
#Override
public void run()
{
try
{
URL remoteUrl = new URL(baseurl + s);
File localUrl = new File(baselocal, s);
FileUtils.copyURLToFile(remoteUrl, localUrl, 60000, 60000);
Log.w(TAG, "Downloaded " + localUrl.getAbsolutePath() + " in " + remoteUrl);
} catch (Exception e)
{
e.printStackTrace();
Log.e(TAG, "download error " + e);
// error management logic
}
}
});
}
tpe.shutdown();
int num = 0;
publishProgress(num, tot);
try
{
while (!tpe.awaitTermination(1, TimeUnit.SECONDS))
{
int n = (int) tpe.getCompletedTaskCount();
Log.w(TAG, "COUTN: " + n + "/" + tot);
if (n != num)
{
num = n;
publishProgress(num, tot);
}
}
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return m;
}
protected void onProgressUpdate(Integer... prog)
{
if (sProgress.getMax() != prog[1]) {
sProgress.setMax(prog[1]);
}
sProgress.setProgress(prog[0]);
}
#Override
protected void onPostExecute(Manifest result)
{
sWakelock.release();
sProgress.hide();
sProgress.dismiss();
// manage results
}
}.execute();
}
If you'll checkout the implementation of AsyncTask then youi can find that AsyncTask itself has ThreadPool so it will start the task on separate thread. Acutually when we can the .execute() to start the background task this method is typically used with THREAD_POOL_EXECUTOR to allow multiple tasks to run in parallel on a pool of threads managed by AsyncTask. So why you need to implement another.
Update
Read about executeOnExecutor in this may be this can help you... It clearly says that if you are allowing multiple tasks to run in parallel from a thread pool is generally not what one wants, because the order of their operation is not defined....but here you want to download the files so I don't think the order is important so in my view you can use it and it'll not create any issue.
I tried to refer similar question on SO, but didn't got any help.
In my android app, I'm planning to implement Recent Quote the user has visited i.e. similar to recently visited pages on web.
Following are the steps I'm following:
1.) Whenever user opens any company view, fetch the company symbols from database
2.) Then store the current symbol along with dateTime in database.
3.) For all symbols fetched from database, Fetch their current value and %Change and display Company name, current value and %Change in a list.
The problem arises in the ASyncTask class as postExecute method doesn't allow it's return type to be any other than void.
Am I doing anything wrong?
Any help will be life saver !!!
String[] rsym,rcmp,rchg;
rdbM = new RecentDBManager(CompanyView.this);
try {
Calendar date1 = Calendar.getInstance();
SimpleDateFormat dateformatter = new SimpleDateFormat(
"dd/MM/yyyy HH:mm:ss");
String currentdate = dateformatter.format(date1.getTime());
rdbM.openDB();
//STEP 1
rsym = rdbM.getRecent_sym();
//STEP 2
rdbM.setData(currentsymbol, currentdate);
rdbM.closeDB();
} catch (Exception e) {
throw new Error(" *** ERROR in DB Access *** "+ e.toString());
}
//STEP 3
for(int i=0;i<rsym.length;i++)
{
DownloadRecentQuote quotetask = new DownloadRecentQuote();
recentquotetask
.execute(new String[] { "http://abc.com/stockquote.aspx?id="
+ rsym[i] });
//CURRENT VALUE and %CHANGE which should be returned from ASyncTask class
rcmp[i]=valuearr[0];
rchg[i]=valuearr[1];
}
list1 = new ArrayList<HashMap<String, String>>();
HashMap<String, String> addList1;
for (int i = 0; i < limit; i++)
{
addList1 = new HashMap<String, String>();
addList1.put(RecentSym_COLUMN, rsym[i]);
addList1.put(RecentCMP_COLUMN, rcmp[i]);
addList1.put(RecentChg_COLUMN, rchg[i]);
list1.add(addList1);
RecentAdapter adapter1 = new RecentAdapter(
CompanyView.this, CompanyView.this, list1);
listrecent.setAdapter(adapter1);
}
private class DownloadRecentQuote extends AsyncTask<String, Void, String> {
/* Fetching data for RecentQuote information */
#Override
protected String doInBackground(String... urls) {
String response = "";
for (String url : urls) {
DefaultHttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
try {
HttpResponse execute = client.execute(httpGet);
InputStream content = execute.getEntity().getContent();
BufferedReader buffer = new BufferedReader(
new InputStreamReader(content));
String s = "";
while ((s = buffer.readLine()) != null) {
response += s;
}
} catch (Exception e) {
e.printStackTrace();
}
}
return response;
}
#Override
protected void onPostExecute(String result) {
arr1 = result.split("#");
if (arr1[0].length() != 0) {
if (arr1[0].equals("1")) {
arr = arr1[1].split(";");
//RETURN 2 STRINGS
String valuearr[];
valuearr[0] = arr[3];
valuearr[1] = arr[6].concat("%");
//return valuearr;
}
}
}
postExecute() can't return a value because who or what would it return to? Your original method that invoked the AsyncTask is gone because your AsyncTask is running in the background. It's asynchronous meaning when AsyncTask.execute() returns it's still running in the background, and hence postExecute() can't return a value because there's nothing to return it to.
Instead your AsyncTask needs a reference back to your Activity or some other object so it can post your values back to it. In your code the lines after you call execute() can't be there because your task hasn't finished. Instead you should create a method called updateSymbol( currentPrice, percentChange), move all that code below execute() in there, and in your AsyncTask you should pass a reference to the Activity. Then call updateSymbol( currentPrice, percentChange ) from the onPostExecute() method.
But, be careful if you have a reference back to an Activity it can be destroyed while your doInBackground() is running, and when postExecute() runs it should just drop the results or not attempt to update the UI. For example, the user rotates their phone causing the Activity to be destroyed. I find it best to hold a reference to the AsyncTask in the activity so it can cancel() it if the Activity is destroyed. You can call AsyncTask.cancel() then check if your task was canceled like:
public void postExecute( String result ) {
if( !isCanceled() ) {
// do your updating here
activity.setSymbol( result );
}
}
It's really easy to create a base class for all Activities so you can easily keep track of AsyncTasks running:
public class BaseActivity extends Activity {
List<AsyncTask> runningTasks;
public void onStop() {
for( AsyncTask task : runningTasks ) {
task.cancel(true);
}
}
public AsyncTask start( AsyncTask task ) {
runningTasks.add( task );
return task;
}
public void done( AsyncTask task ) {
runningTasks.remove( task );
}
}
Some quick pointers. You don't need execute( new String[] { "blah" + blah } ). Varargs in Java allow you to do this. execute( "blah" + blah ). You also are catching exceptions and continuing without really handling them. It will be hard when something really happens because your app catches them, and just continues as if nothing happened. If you get an error you might want to provide some feedback to the user and stop trying to execute that process. Stop, show an error to the user, and let them do the next thing. Move the catch blocks to the bottom of the methods.
Essentially, AsyncTask.onPostExecute() is where you do whatever you want to do after AsyncTask's doInBackground() is executed and the execution result gets returned. This should be considered the best practice.
When AsyncTask().execute() is called from the UI thread (note that this method must be called from the UI thread), the Android framework creates a worker thread and starts running whatever you wrote in AsyncTask.doInBackground() on this worker thread. At this point (after calling new AsyncTask().execute()), the UI thread continues to execute code after new AsyncTask().execute(). So now during run time, you have two threads (UI and worker thread) both running simultaneously.
But where and when does the AsyncTask execution result get returned from the worker thread back to the UI thread?
The point where your worker thread (doInBackground()) finishes and returns to the UI thread is AysncTask.onPostExecute(). This method is guaranteed to be called by the framework on the UI thread as soon as AsyncTask finishes. In other words, we don't care where and when AsyncTask.onPostExecute() gets called at run time, we just need to guarantee it will be called ultimately at some stage in the future. This is the reason why this method does not return an execution result - instead, it requires that the execution result gets passed in as the only method parameter from doInBackground().
In addition, the Android API provides a way to return an AsyncTask execution result at coding time, AsyncTask.get():
MyAsyncTask myAsyncTask = new MyAsyncTask();
// This must be called from the UI thread:
myAsyncTask.execute();
// Calling this will block UI thread execution:
ExecutionResult result = myAsyncTask.get();
Bear in mind that AsyncTask.get() will block the calling thread's execution, and you will probably get an ANR exception if you call it on the UI thread. This is the payload of using AsyncTask.get(), by calling it on the UI thread, you are actually making your AsyncTask (worker thread) run synchronously with UI thread (by making UI thread wait). To sum up, this is doable but not recommended.
Just for future reference, because this post is a little old:
I have created an Activity class which has an onStart() method and a separate class for the AsyncTask. Based on my test, after the doInbackground() method the result will be sent to the activity first and after that onPostExecute() will run. This is because based off of logcat, I have my first response data (sent by server) first, then this response will show again from the activity and the last the message in onPostExecute() will show.
Code for the activity:
#Override
protected void onStart() {
super.onStart();
String str = "***";
if(isConnectedToInternet()){
myAsyncTask.execute();
try {
if(myAsyncTask.get())
str = myAsyncTask.getResponseMsg();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (CancellationException e) {
e.printStackTrace();
}
}
Log.i("Data returned by server2:", str);
}
AsyncTask code:
public class MyAsyncTask extends AsyncTask<Void, Void, Boolean> {
private URL url;
private HttpURLConnection conn;
private String strResponseMsg;
public MyAsyncTask(String url) throws MalformedURLException{
this.url = new URL(url);
}
#Override
protected void onPreExecute() {
Log.i("Inside AsyncTask", "myAsyncTask is abut to start...");
}
#Override
protected Boolean doInBackground(Void... params) {
boolean status = false;
try {
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(Manager.ConnTimeout);
conn.setReadTimeout(Manager.ReadTimeout);
int responseCode = conn.getResponseCode();
Log.i("Connection oppened", "Response code is:" + responseCode);
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
if (in != null) {
StringBuilder strBuilder = new StringBuilder();
// Read character by character
int ch = 0;
while ((ch = in.read()) != -1)
strBuilder.append((char) ch);
// Showing returned message
strResponseMsg = strBuilder.toString();
Log.i("Data returned by server:", strResponseMsg);
status = true;
}
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return status;
}
#Override
protected void onPostExecute(Boolean result) {
Log.i("Inside AsyncTask", "myAsyncTask finished its task. Returning data to caller...");
}
public String getResponseMsg(){
return strResponseMsg;
}
}
I'm using using AsyncTask to download data over internet and I have a little problem. I need to be able to start one AsyncTask a few times, that's why I'm creating a new instance everytime,but the thing that I notice is that it's working without any problem the first three or four times,but after that my AsyncTask is stuck on onPreExecute() and doing nothing after that. Am I doing something wrong ? (Actually I am using two AsyncTasks one after another just for testing purposes). Here is the sample code which I'm using :
this is how I start the AsyncTasks :
if (index == 1) {
//Login - first way
new FirstSync().execute(Synchronization.this);
} else if (index == 2) {
//SyncWithHash - second way
SyncWithHash syncHash = new SyncWithHash();
syncHash.execute(Synchronization.this);
} else if (index == 3) {
//Deactivate Collection - third way
deactivateColl = new DeactivateCollection();
deactivateColl.execute(Synchronization.this);
}
I did try with three different ways to start the asyncTask,but no change. Here is my AsyncTask :
// Sync With Hash
public class SyncWithHash extends AsyncTask <Context, Integer, Void> {
#Override
protected Void doInBackground(Context... arrContext) {
try {
String charset = "UTF-8";
hash = getAuthHash();
SharedPreferences lastUser = PreferenceManager.getDefaultSharedPreferences(Synchronization.this);
int userId = lastUser.getInt("lastUser", 1);
systemDbHelper = new SystemDatabaseHelper(Synchronization.this, null, 1);
systemDbHelper.initialize(Synchronization.this);
String sql = "SELECT dbTimestamp FROM users WHERE objectId=" + userId;
Cursor cursor = systemDbHelper.executeSQLQuery(sql);
if (cursor.getCount() < 0) {
cursor.close();
} else if (cursor.getCount() > 0) {
cursor.moveToFirst();
timeStamp = cursor.getString(cursor.getColumnIndex("dbTimestamp"));
Log.d("", "timeStamp : " + timeStamp);
}
String query = String.format("debug_data=%s&"
+ "client_auth_hash=%s&" + "timestamp=%s&"
+ "client_api_ver=%s&"
+ "set_locale=%s&" + "device_os_type=%s&"
+ "device_sync_type=%s&"
+ "device_identification_string=%s&"
+ "device_identificator=%s&" + "device_resolution=%s",
URLEncoder.encode("1", charset),
URLEncoder.encode(hash, charset),
URLEncoder.encode(timeStamp, charset),
URLEncoder.encode(clientApiVersion, charset),
URLEncoder.encode(locale, charset),
URLEncoder.encode(version, charset),
URLEncoder.encode("14", charset),
URLEncoder.encode(version, charset),
URLEncoder.encode(deviceId, charset),
URLEncoder.encode(resolution, charset));
SharedPreferences useSSLConnection = PreferenceManager
.getDefaultSharedPreferences(Synchronization.this);
boolean useSSl = useSSLConnection.getBoolean("UseSSl", true);
if (useSSl) {
UseHttpsConnection(url, charset, query);
} else {
UseHttpConnection(url, charset, query);
}
} catch (Exception e2) {
e2.printStackTrace();
}
return null;
}
#Override
protected void onProgressUpdate(Integer... progress) {
//cancelDialog.setProgress(progress[0]);
}
#Override
protected void onCancelled() {
Log.d("","ON CANCELLED");
}
#Override
protected void onPreExecute()
{
Log.d("","ON PRE EXECUTE");
// myProgress = 0;
}
#Override
protected void onPostExecute(Void v) {
Log.d("","ON POST EXECUTE");
}
}
So any ideas why it's happening and which is the best way to be able to use an AsyncTask a few times without any exceptions and bugs like the one that I get.
And another question : Is there anything in AsyncTask which can cause my connection to be Reset by peer , because I'm getting this error too (not every time).
Thanks a lot!
I think your doInBackground() is hanging. Make log statement when its entered and when its exited and check.
In the old days AsyncTask had a pool of threads, so if a doInBackground() hung, then it didnt affect the other AsyncTasks. That changed AFAIK with Android 2.2 or 2.3 to that a single thread took care of all AyncTasks, one at a time. Therefore, if your doInBackground() is hanging it might affect the next AsyncTasks being started and the will hang right after onPreExecute().
Edit: It was changed from a single thread, to multiple, and then back to a single thread:
http://developer.android.com/reference/android/os/AsyncTask.html#execute%28Params...%29 "When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. After HONEYCOMB, it is planned to change this back to a single thread to avoid common application errors caused by parallel execution."
If you really want an indefinite lot of stuff to 'hang' in parralel, then don't use AsyncTask. Use good old threads, which, when they need to update the GUI, fire off a Runnable to be runned on the GUI thread:
Button knap1, knap2, knap3;
...
Runnable r=new Runnable() {
public void run() {
// Do some stuff than hangs
try { Thread.sleep(10000); } catch (InterruptedException ex) {}
System.out.println("færdig!");
// Update GUI thread
Runnable r2=new Runnable() {
public void run() {
knap3.setText("færdig!");
}
};
runOnUiThread(r2);
}
};
new Thread(r).start();
(example from http://code.google.com/p/android-eksempler/source/browse/trunk/AndroidElementer/src/eks/asynkron/Asynkron1Thread.java?spec=svn109&r=109)
It might be happening because you are synchronizing on an object "Synchronization.this'.
Also noticed you are not closing the cursor which you opened.