AsynTask - onPostExecute is called before doInBackground - android

i got a problem getting my AsyncTask to work correct. My App offers the possibility to connect with your Google Account and add and receive tasks by using the Tasks API. When the users wants to synchronize with his account, the doInBackground() method is started. Right before that, a ProgressDialog is displayed in the onPreExecute() method of the AsyncTask class.
If the synchronisation has been successfully executed, the onPostExecute() method 'should' be called fading out the ProgressDialog.
But there is problem: the onPostExecute() ethod is called before the work in the doInBackground() is finished.
In doInBackground() I receive the token used for the authorization:
token = future.getResult().getString(AccountManager.KEY_AUTHTOKEN);
That's all. But right before that, the onPostExecute() is called and the ProgressDialog disappears while the token is still retrieving. The wired thing is that when I start the app and synchronizes for the first time, it works like it should. But after that the onPostExecute() method finishes before the work is completed. Does this have to do that there are requests to a server while executing
future.getResult().getString(AccountManager.KEY_AUTHTOKEN);
How can I tell the onPostExecute() method that there is still work to do?
private class SynchronizeGoogle extends AsyncTask<Void, Integer, Void>
{
private final ProgressDialog dialog = new ProgressDialog(RememberMe.this);
protected void onPreExecute()
{
this.dialog.setMessage("Listen werden geladen...");
this.dialog.show();
}
#Override
protected Void doInBackground(Void... arg0)
{
try
{
switch(startSynchronize())
{
case 0:
publishProgress(0);
return null;
case 1:
publishProgress(1);
return null;
case 2:
publishProgress(2);
return null;
}
}
catch (IOException e)
{
e.printStackTrace();
}
synchronize();
return null;
}
protected void onProgressUpdate(Integer... type)
{
int typeCase = type[0];
switch(typeCase)
{
case 0:
showDialog(DIALOG_INTERNET_ACCESS);
break;
case 1:
showDialog(DIALOG_CREATE_ACCOUNT);
break;
case 2:
showDialog(DIALOG_ACCOUNTS);
break;
}
}
protected void onPostExecute(final Void unused)
{
if (this.dialog.isShowing())
{
this.dialog.dismiss();
}
}
}
And here my startSynchronize() and synchronize() methods:
private int startSynchronize() throws IOException
{
googleAccountManager = new GoogleAccountManager(RememberMe.this);
Account[] accounts = googleAccountManager.getAccounts();
if(checkAccess.internetAccess() == false)
{
return 0;
}
if(accounts.length == 0)
{
return 1;
}
else
{
if(accounts.length == 1)
{
account = accounts[0];
}
else
{
return 2;
}
}
return -1;
}
private void synchronize()
{
myPrefs = this.getSharedPreferences("myPrefs", MODE_PRIVATE);
String oldToken = myPrefs.getString(MY_TOKEN, "");
if(oldToken.length() > 0)
{
// invalidate old token to be able to receive a new one
googleAccountManager.invalidateAuthToken(oldToken);
}
googleAccountManager.manager.getAuthToken(account, AUTH_TOKEN_TYPE, true, new AccountManagerCallback<Bundle>()
{
public void run(AccountManagerFuture<Bundle> future)
{
try
{
token = future.getResult().getString(AccountManager.KEY_AUTHTOKEN);
prefsEditor.putString(MY_TOKEN, token);
prefsEditor.commit();
useTasksAPI(token);
}
catch (OperationCanceledException e)
{
//...
}
catch (Exception e)
{
//...
}
}
}, null);
}
In the Optionsmenu i start it like this
new SynchronizeGoogle().execute();
Thanks everybody for your help

If I do not misunderstand your question, you're wrong with getResult() method usage.
When getResult() called by anywhere in your code, AsyncTask does not wait until finish. So you need to do your process in onPostExecute method.
I recommend you this question&answer. I hope, it's gonna help you.

Related

Using runOnUiThread to change TextView text in Android

I've got an app that makes API calls when the user logs in, I've got two classes, one that Pushes to the API and one that Pulls from it. I've also got an asyncTask on my LogIn Activity which handles the network connection. When the network connection starts I have a view switcher to switch the view to a progress loader and a textview, the textview is used to display stuff like "Connecting", "Downloading Data", ect.
The problem is my API Push and Pull methods are stored in different classes and the LogIn AsyncTask simply calls them, it all works except for updating the TextView to let the user know the progress.
In the LogIn activity, I have this method, which changes the textview info to whichever number is passed to it.
public void updateProgress(int i) {
switch (i) {
case 0:
loadInfo.setText(R.string.log_in_thread_Connecting);
break;
case 1:
loadInfo.setText(R.string.log_in_thread_Connected);
break;
case 2:
loadInfo.setText(R.string.log_in_Thread_Sending_Data);
break;
case 3:
loadInfo.setText(R.string.log_in_thread_Response);
break;
case 4:
loadInfo.setText(R.string.log_in_web_connecting);
break;
case 5:
loadInfo.setText(R.string.log_in_web_connected);
break;
case 6:
loadInfo.setText(R.string.log_in_web_user_data_download);
break;
case 7:
loadInfo.setText(R.string.log_in_web_user_data_downloaded);
break;
case 8:
loadInfo.setText(R.string.log_in_web_device_data_upload);
break;
}
}
This works if i'm calling it from the AsyncTask from the LogIn Activity but not from the API classes.
I have found a method called .runOnUiThread so I tried to implement that.
private void publishProgress(final int i){
logInActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
logInActivity.updateProgress(i);
Log.d("UI THREAD","HIT");
}
});
}
This is what I have come up with, but it doesn't seem to be hitting, I don't even see the Log post.
I did also have an error implementing this but it was resolved when I added Looper.prepare(); to the Log in AsyncTask
AsyncTask
private class RegistrationWeb extends AsyncTask<Void,Void,Void>{
private static final String tag = "API_LogIn";
private String APIKey = null
#Override
protected void onPreExecute() {
super.onPreExecute();
viewFlipper.showNext();
APIKey = getAPIKey();
}
#Override
protected Void doInBackground(Void... voids) {
try{
Looper.prepare();
GetAPIData get = new GetAPIData();
URL url = new URL(API_Register_User+APIKey);
String response = get.GetData(url);
JSONArray jsonArray = new JSONArray(response);
JSONObject jsonObject = jsonArray.getJSONObject(0);
//If the user is accepted
if(jsonObject.getString("_Code").equals("0")){
PublishProgress(8);
PostAPIData post = new PostAPIData();
url = new URL(API_Register_Device);
response = post.PostData(url);
jsonArray = new JSONArray(response);
jsonObject = jsonArray.getJSONObject(0);
} else if(jsonObject.getString("_Code").equals("2")){
}
} catch (MalformedURLException e) {
Log.e(tag,e.toString());
} catch (JSONException e) {
Log.e(tag,e.toString());
} catch (IOException e) {
Log.e(tag,e.toString());
}
return null;
}
private void PublishProgress(final int i){
runOnUiThread(new Runnable() {
#Override
public void run() {
updateProgress(i);
}
});
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}
}
You can not directly access string that are present in resource, you will have to to acquire resources first to use its content. You need to call getResources() from your Activity, then you can get the string you are looking for.
YourActivity.context.getResources().getString(R.string.id_name);
Override onProgressUpdate() and then call publishProgress()
public void publishProgress(Integer v) {
Message message = new Message();
message.what = v;
YourActivity.handler.sendMessage(message);
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
publishProgress(values[0);
}
And change your AsyncTask parameter during its creation from Void to Integer
class RegistrationWeb extends AsyncTask<Integer,Integer,Integer> {
}
In your Activity where your TextView resides on which you want update, make Handler to listen the messages from outer class and in that Handler update your TextView.
Activity:
public static Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
//update your text view on the basis of msg.what
}
}
};
Note : Make your TextView loadInfo as static

Best approach to make a login screen

I am writing here because this is my last solution of understanding this type of programming.The problem is that I got stuck on what to use to handle the connection to a server and log-in. Should I use async task, handler or thread ? I didn't find a concrete answer stating which one to use, only found that async task is used to download images or other download stuffs.
Until now I have used a thread to connect to the server. The problem I encountered was when I catch the exception ( Putting invalid username/password ) and try to log-in again. ( I needed to "close" the last thread and start one again )
After this I started to use async task but I don't really understand how it should work and I am stuck on a toast of invalid username/password.
private class connectStorage extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
try {
api = DefaultClientFactory.create(host, getUser, getPassword);
if (api.getAuthToken().trim().length() > 3) {
//TO DO LAYOUT CHANGE;
}
} catch (StorageApiException e) {
e.printStackTrace();
Log.i("TEST", "" + e.getMessage());
}
return null;
}
Also, I am 100% sure that calling inflate in the doInBackground method won't work too ( there I wanted to change the activity ).
I am starting the async task on a button press.
When you are using asynctask
You have doInBackground and onPostExecute
So basically get a json or string or boolean as a result from doinbackground
and in onpostexecute check if the login in succesful or not if its succesful save the data from server and start an intent to go to another activity or toast the user that that user login details are wrong and try again.
So your asynctask can be an inner class of your activity class which is login and onClickSubmit button call the asynctask class and on post execute parse the json and according to the result decide what to do
Example:
public class SignInAsycTask extends AsyncTask<RequestParams, Void, String> {
#Override
protected String doInBackground(RequestParams... params) {
return new HttpManager().sendUserData(params[0]);
}
#Override
protected void onPostExecute(String result) {
String[] details = parseJsonObject(result);
if (details != null) {
user.setUser_id(Integer.valueOf(details[0]));
user.setName(details[1]);
if (details.length > 2) {
user.setProfilePic(details[2]);
}
setSharedPreferences();
startActivity(new Intent(Signin.this, MainActivity.class));
finish();
} else {
Toast.makeText(Signin.this, "please try again",
Toast.LENGTH_LONG).show();
}
}
}
public String[] parseJsonObject(String result) {
JSONObject obj = null;
try {
obj = new JSONObject(result);
if (obj.has("success")) {
if (obj.getInt("success") == 1) {
if (obj.has("user_pic")) {
return new String[] {
String.valueOf(obj.getInt("user_id")),
obj.getString("user_name"),
obj.getString("user_pic") };
} else {
return new String[] {
String.valueOf(obj.getInt("user_id")),
obj.getString("user_name"), };
}
} else {
return null;
}
} else {
return null;
}
} catch (JSONException e) {
e.printStackTrace();
return null;
}
}
here my RequestParams are just a object where I stored all the details like url parameters to send etc and the output of the doinbackground is a String and I am parsing it in my postexecute method

Android Login with Azure Mobile Services

I am having trouble creating a login function with Android using Azure mobile services. When i attempt to login, using a user that I have previously created, It tells me that the password is incorrect, but when I login again using the same credentials straight afterwards it gives me access to the next activity.
This is the code in which I use to connect to my Azure mobile service
// Connect client to azure
try {
mClient = new MobileServiceClient(
"URL",
"App Key",
this
);
mUserTable = mClient.getTable(User.class);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
This is the code in which executes when i attempt the login
public class UserLoginTask extends AsyncTask<Void, Void, Boolean>
{
#Override
protected Boolean doInBackground(Void... params)
{
mUserTable.where().field("email").eq(user.getEmail()).execute(new TableQueryCallback<User>()
{
public void onCompleted(List<User> result, int count, Exception exception, ServiceFilterResponse response) {
if (exception == null) {
for (User u : result) {
if(user.getPassword().equals(u.getPassword()))
{
grantAccess = true;
}
else
{
grantAccess = false;
}
}
} else {
grantAccess= false;
}
}
});
return grantAccess;
}
#Override
protected void onPostExecute(final Boolean success)
{
mAuthTask = null;
showProgress(false);
if (success == true)
{
// Finish this activity
finish();
// Start the main activity
Intent i = new Intent(getApplicationContext(),
MainActivity.class);
startActivity(i);
}
else
{
mPasswordView.setError(getString(R.string.error_incorrect_password));
mPasswordView.requestFocus();
}
}
#Override
protected void onCancelled()
{
mAuthTask = null;
showProgress(false);
}
}
}
The problem is with how you're calling your Mobile Service to read the user table. You don't need to wrap this in an AsyncTask. The Mobile Services SDK will do that for you by default. The onCompleted method you implement inside of your TableQueryCallback will be called when the results are returned from the server. What this means is that the callback will be called (most likely) after the onPostExecute method is called. Let me try to diagram the flow:
--Query users table
----Query task kicked off by SDK with callback
--Proceed with UserLoginTask
--Call onPostExecute of UserLoginTask
Then at some separate point when the server responds, your callback is called. To fix this, I would recommend getting rid of your async task. Put everything you have in onPostExecute into your callback method as, again, this will be called once you get a response from your Mobile Service.

How can i Handle special kind of exception in doinBackground() method

I am making an android app which requires it to fetch some information from a remote server and therefore i have to make a http request in a async task.Now the problem is that that the response sometimes take more than 2 secs and when it does it give http timeout exception but most of the time it works just fine .So i want to implement the functionality that when i recieve a http timeout exception i want to retry the request again(try the doinBackground again,because network call can only be made in thread other than the main thread) because chances are that it will be successful and all the things that need to be fetched from the remote server will occur in CallRemoteServer() method
Now in my program i have implemented something like this
new AsyncTask<Void, Void, Void>() {
private boolean httpResponseOK = true;
#Override
protected Void doInBackground(Void... params) {
try {
CallRemoteServer();
}
} catch (Exception e) {
httpResponseOK = false;
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
if (httpResponseOK == false) {
//Show an alert dialog stating that unable to coonect
}
else
{
//update UI with the information fetched
}
});
Can someone advice me how can i implement something which i have mentioned above ,i mean that if i get some other exception other than timeout than show an alert dialog otherwise retry atleast five time more CallRemoteServer method before showing the dialog that unable to connect.
I am not able to think of any good way to implement this logic.
Thanks in advance
You're probably getting a ConnectTimeoutException (or check in the logs what is the IOException you're getting). I would first try to extend the timeout. Some similar answers for this can be found here or here.
However, an auto-reconnect mechanism is a must to have. I would implement it using recursive code:
final int maxAttempts = 5;
protected MyServerData callRemoteServer(int attempt) throws IOException {
try {
// do the IO stuff and in case of success return some data
} catch (ConnectTimeoutException ex) {
if(attempt == maxAttempts) {
return callRemoteServer(attempt + 1);
} else {
throw ex;
}
}
}
Your doInBackground method should look like:
#Override
protected Void doInBackground(Void... params) {
try {
callRemoteServer(0);
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
In this way if the connection timeouts it will attempt to retry for 5 max times (you can set the max attempts to anything you like). Just make sure to return some data from this IO operation as that is the most valuable asset from that method anyway ...
For this reason I would change it to following:
private class MyAsynckTask extends AsyncTask<Void, Void, MyServerData> {
#Override
protected MyServerData doInBackground(Void... params) {
try {
return callRemoteServer(0);
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(MyServerData result) {
if(result != null) {
// display data on UI
}
}
}

Performing data loading unitl succesfull or user break

In my app I performing loading data from web and then displaying it to user. Before loading data app shows progress dialog. I have problem if user locks phone in the middle of loading operation, or server is overloaded and can't respond in time my application freezes, because it doesn't dismiss progress dialog, or in some cases it crashes because lack on needed data.
If some error happened while loading data I want show some dialog to user to let him know about error and ask him should application repeat last request. I tried to use AlertDialog for it, but I haven't succeed.
Here is code of one activity (There is no progress dialog here, but it demonstrates how I loading data):
#EActivity(R.layout.layout_splash)
#RoboGuice
public class SplashScreenActivity extends Activity {
#Inject
private AvtopoiskParserImpl parser;
#Bean
BrandsAndRegionsHolder brandsAndRegionsHolder;
#ViewById(R.id.splash_progress)
ProgressBar progressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
loadData();
}
#Background
protected void loadData() {
publishProgress(10);
LinkedHashMap<String, Integer> brands = null;
try {
brands = parser.getBrands();
} catch (IOException e) {
Log.e(e.getMessage());
}
publishProgress(50);
LinkedHashMap<String, Integer> regions = null;
try {
regions = parser.getRegions();
} catch (IOException e) {
Log.e(e.getMessage());
}
publishProgress(70);
populateData(brands, regions);
}
#UiThread
protected void populateData(LinkedHashMap<String, Integer> brands, LinkedHashMap<String, Integer> regions) {
Intent intent = new Intent(SplashScreenActivity.this, SearchActivity_.class);
brandsAndRegionsHolder.brandsMap = brands;
brandsAndRegionsHolder.regionsMap = regions;
publishProgress(100);
startActivity(intent);
finish();
}
#UiThread
void publishProgress(int progress) {
progressBar.setProgress(progress);
}
}
parser.getBrands() and parser.getRegions() are loading data from the web.
I want to do something like this:
boolean repeatRequest = true;
while (repeatRequest) {
try {
brands = parser.getBrands();
repeatRequest = false;
} catch (IOException e) {
Log.e(e.getMessage());
repeatRequest = showErrorDialog();
}
}
But I didn't manage to do so because this code executes in background thread, but dialog should be shown in UI thread.
I believe that it should be standard approach of doing so, but didn't manage to find it.
Any ides how can I implement this?
The best way is to use AsyncTask.
private class LoadDataTask extends AsyncTask<Void, Integer, Object> {
private ProgressDialog mProgress;
protected Object doInBackground(Void... params) {
// This method runs in background
Object result = null;
try {
result = parser.parse();
} catch (Exception e) {
result = e.getMessage();
}
return result;
}
protected void onProgressUpdate(Integer... progress) {
// This method runs in UI thread
mProgress.setProgress(progress[0]);
}
protected void onPreExecute() {
// This method runs in UI thread
mProgress = new ProgressDialog(context);
mProgress.show();
}
protected void onPostExecute(Object result) {
// This method runs in UI thread
mProgress.dismiss();
if (result instance of String) {
// Here you can launch AlertDialog with error message and proposal to retry
showErrorDialog((String) result);
} else {
populateData(result);
}
}
}

Categories

Resources