I have been trying for awhile to figure out an issue with Asynchronous i/o in an android application that I am working on.
This application is required to download data to from a series of tables from Microsoft Dynamics CRM.
Once the data has been down it must preform a series of operations on the data to fill out some forms.
My problem is that I must wait for the downloads to be complete in order to start the update process.
If I add a any form of wait to my code it seems that it blocks indefinitely and never executes the callback.
I have tried methods using AtomicBooleans, AtomicIntegers, and CountDownLatchs with no success.
Here is an example using an AtomicInteger.
final CountDownLatch latch = new CountDownLatch(1);
OrganizationServiceProxy orgService;
orgService = new OrganizationServiceProxy(Constant.ENDPOINT, CRMLogin.getRequestInterceptor());
ColumnSet columnSet = new ColumnSet();
columnSet.AddColumns(AccountEntry.FETCH_COLS);
orgService.Retrieve(AccountEntry.ENTITY, UUID.fromString(accountid), columnSet, new Callback<Entity>() {
#Override
public void success(Entity entity, Response response) {
Account account = new Account();
//Load the existing fields for the account
account.load(index);
String activityid = account.getValue(AccountEntry.ACTIVITY_ID);
String recordid = account.getValue(AccountEntry.RECORD_ID);
String name = account.getValue(AccountEntry.ACCOUNT_NAME);
//Overload the fields for the account
account.load(entity);
//Reset overloaded fields on the account.
account.setValue(AccountEntry.ACTIVITY_ID, activityid);
account.setValue(AccountEntry.RECORD_ID, recordid);
account.setValue(AccountEntry.ACCOUNT_NAME, name);
//overwrite the record in the database.
account.setValue(AccountEntry.SYNCED, "1");
account.update();
Log.d("pullAccount>>>", accountid + " " + "pulled.");
latch.countDown();
}
#Override
public void failure(RetrofitError error) {
Log.d("pullAccount>>>", accountid + " " + error.getMessage());
latch.countDown();
}
});
try{
latch.await(); //THIS BLOCKS FOREVER AND EVER
}
catch (Exception e){
}
Of note is the CallBack is implemented using Retrofit.
Any guidance would be greatly appreciated.
Thank you.
Look at AsyncTask it will handle what you want in a way that Android is optimized for. There is example usage here
http://developer.android.com/reference/android/os/AsyncTask.html
EDIT:
I kinda threw this together, let me know if it works as you would expect
public class AsyncOrganizationService extends AsyncTask<Void, Void, Entity> {
#Override
protected Entity doInBackground(Void... params) {
final CountDownLatch blocker = new CountDownLatch(1);
OrganizationServiceProxy orgService;
orgService = new OrganizationServiceProxy(Constant.ENDPOINT, CRMLogin.getRequestInterceptor());
ColumnSet columnSet = new ColumnSet();
columnSet.AddColumns(AccountEntry.FETCH_COLS);
final SettableFuture<Entity> result = SettableFuture.create();
orgService.Retrieve(AccountEntry.ENTITY, UUID.fromString(accountid), columnSet, new SortedList.Callback<Entity>() {
#Override
public void success(Entity entity, HttpHelper.Response response) {
result.set(entity);
blocker.countDown();
}
});
try {
blocker.await();
return result.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
#Override
protected void onPostExecute(Entity entity) {
Account account = new Account();
//Load the existing fields for the account
account.load(index);
String activityid = account.getValue(AccountEntry.ACTIVITY_ID);
String recordid = account.getValue(AccountEntry.RECORD_ID);
String name = account.getValue(AccountEntry.ACCOUNT_NAME);
//Overload the fields for the account
account.load(entity);
//Reset overloaded fields on the account.
account.setValue(AccountEntry.ACTIVITY_ID, activityid);
account.setValue(AccountEntry.RECORD_ID, recordid);
account.setValue(AccountEntry.ACCOUNT_NAME, name);
//overwrite the record in the database.
account.setValue(AccountEntry.SYNCED, "1");
account.update();
Log.d("pullAccount>>>", accountid + " " + "pulled.");
}
Im using Guava's SettableFuture class (http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/util/concurrent/SettableFuture.html). Guava is quite an amazing library - if you're not using it you should consider doing so. Otherwise, you could whip something up really quick
Related
I am trying to create a Warehouse management app for Android using Java and the Google Sheets API. I create a query to get the data stored in a sheet. I have a class which handles all the requests to the API. I create an instance of this object from an activity class and I want to load another Activity after the method readCurrentWarehouse() returns. The problem is that the main thread execution continues before the query responds and the result is obtained some seconds later. I have tried using both the Thread() and the AsyncTask, but the results were not the desired. My code for the function that handles the query is the following:
public class GoogleHandler {
private Sheets service;
private Exception mLastError = null;
public GoogleHandler(GoogleAccountCredential credential){
HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
this.service = new Sheets.Builder(transport,jsonFactory,credential)
.setApplicationName("EvoScanner")
.build();
}
public void readCurrentWarehouse(){
Thread thread = new Thread(){
public void run(){
String spreadsheetId = //spreadsheetID;
try{
Spreadsheet spreadsheet = service.spreadsheets().get(spreadsheetId).execute();
Sheet lastSheet = spreadsheet.getSheets().get(spreadsheet.getSheets().size()-1);
String range = lastSheet.getProperties().getTitle();
List<String> results = new ArrayList<String>();
ValueRange response = service.spreadsheets().values().get(spreadsheetId,range).execute();
List<List<Object>> values = response.getValues();
if (values!=null){
for (List row : values){
results.add(row.get(0).toString());
}
}
System.out.println(results.size());
}catch (IOException e){
System.out.println("-------------------------------");
e.printStackTrace();
}
}
};
thread.start();
}
}
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private void handleSignInResult(Task<GoogleSignInAccount> task) {
try{
GoogleSignInAccount account = task.getResult(ApiException.class);
GoogleSignIn.requestPermissions(this,2,account, new Scope(SheetsScopes.SPREADSHEETS));
if (GoogleSignIn.hasPermissions(
GoogleSignIn.getLastSignedInAccount(this),new Scope(SheetsScopes.SPREADSHEETS)
)){
String accountName = account.getEmail();
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(this, Arrays.asList(SheetsScopes.SPREADSHEETS)).setBackOff(new ExponentialBackOff());
credential.setSelectedAccountName(accountName);
GoogleHandler googleHandler = new GoogleHandler(credential);
googleHandler.readCurrentWarehouse();
Intent mainMenuIntent = new Intent(getBaseContext(),MainMenuActivity.class);
mainMenuIntent.putParcelableArrayListExtra("warehouse",warehouse.getWarehouse());
startActivity(mainMenuIntent);
}
}catch(ApiException e){
Log.w("tag","signInResult:failed code=" + e.getStatusCode());
Log.w("tag2","signInResult:failed description = "+e.getCause());
//updateUI(null)
}
}
Is there any way for the main thread to wait for the query to return the response before proceeding with loading the next Activity?
Blocking the Main Thread is against the android development policies. Blocking Main Thread for too long can cause an ANR error. You can show some progress dialog or loading information until you receive a response. Or if your MainActivity class is responsible only for making that request consider moving the request to another Activity (MainMenuActivity), which you are navigating to after response.
I am trying to authenticate client token created by Firebase authentication library in Android in GCE endpoint.
The guide of how to do this can be found here
Basically I need to call this code snippet from the end point (i.e. server backend code not android code).
FirebaseAuth.getInstance().verifyIdToken(idToken)
.addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
#Override
public void onSuccess(FirebaseToken decodedToken) {
String uid = decodedToken.getUid();
// ...
}
});
Let say I want to execute that code and return the user to android client code. How should I do that?
This is my sample code that does not make sense. But it demonstrate what I want to do!
#ApiMethod(name = "serverAuth")
public MyUser serverAuth(#Named("token") String token) {
FirebaseAuth.getInstance().verifyIdToken(token)
.addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
#Override
public void onSuccess(FirebaseToken decodedToken) {
String uid = decodedToken.getUid();
String email = decodedToken.getEmail();
String name = decodedToken.getName();
Map<String, Object> claims = decodedToken.getClaims();
String claimString = "";
for (Object claim : claims.values()) {
claimString += claims.toString();
}
MyUser user = new MyUser(uid, email, name, claimString);
//How to return this user?
}
});
//This is compile error since user varriable does not exist here
return user;
}
I have google search how to execute async code in GCE endpoints. But getting nowhere with that. What I get is something about code execution that is blocking until done and then return the user. But how to code so that async code as above become blocking?
CountDownLatch is the magic class you need. It will let you wait till the OnSuccessListener is actually completed.
Adapt your method this way: (I removed the steps that lead to MyUser's creation in order to focus on important points.)
#ApiMethod(name = "serverAuth")
public MyUser serverAuth(#Named("token") String token) {
final List<MyUser> users = new ArrayList<>();
final CountDownLatch cdl = new CountDownLatch(1);
FirebaseAuth.getInstance().verifyIdToken(token)
.addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
#Override
public void onSuccess(FirebaseToken decodedToken) {
// ... init uid, email, name and claimString
users.add(new MyUser(uid, email, name, claimString));
cdl.countDown();
}
});
try {
cdl.await(); // This line blocks execution till count down latch is 0
} catch (InterruptedException ie) {
}
if (users.size() > 0) {
return users.get(0);
} else {
return null ;
}
}
This is the basic version of what you need. IMHO, it requires 2 more improvements :
You should also take the possibility of failure into account :
FirebaseAuth.getInstance().verifyIdToken(token)
.addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
#Override
public void onSuccess(FirebaseToken decodedToken) {
cdl.countDown();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// log error, ...
cdl.countDown();
}
});
You should also take the possibility that none of the listeners are called. In this situation your method will never return. To avoid that, you can set a timeout on the await() method :
try {
// This line blocks execution till count down latch is 0
// or after 30 seconds.
cdl.await(30l, TimeUnit.SECONDS);
} catch (InterruptedException ie) {
}
That's it. Hope this may help.
I have a single incident where a complete duplicate of a entry was made into the database (the same user comment appeared twice). They had different object IDs but were otherwise the exact same. It was slower than usual to finish the posting and only happened once out of dozens of comments, so I want to say it was a Parse issue during the saveInBackground call. Even so, I expect a service like Parse to be a little more robust. As my first time working with Android though, I also can't be sure nothing is wrong on my end. Any help? Also just any criticisms? This is the method called when the user hits a comment submission button:
private void submitComment() {
String text = commentText.getText().toString().trim();
Intent intent = getIntent();
String ID = intent.getStringExtra("imageID");
String parentID = intent.getStringExtra("parent");
// Set up a progress dialog
final ProgressDialog loadingDialog = new ProgressDialog(CommentSubmitActivity.this);
loadingDialog.setMessage(getString(R.string.publishing_comment));
loadingDialog.show();
Comment comment = new Comment();
comment.setText(text);
comment.setUser((ParseUser.getCurrentUser()));
if (ID.equals("#child")) {
comment.setParent(parentID);
comment.setImage("#child");
ParseQuery<ParseObject> query = ParseQuery.getQuery("Comment");
query.getInBackground(parentID, new GetCallback<ParseObject>() {
public void done(ParseObject parentComment, ParseException e) {
if (e == null) {
int numChild = parentComment.getInt("numChild");
parentComment.put("numChild", ++numChild);
parentComment.saveInBackground();
} else {
Log.d("numChild: ", "error");
}
}
});
} else {
comment.setImage(ID);
comment.put("numChild", 0);
ParseQuery<ParseObject> query = ParseQuery.getQuery("ImageUpload");
query.getInBackground(ID, new GetCallback<ParseObject>() {
public void done(ParseObject image, ParseException e) {
if (e == null) {
int numComments = image.getInt("numComments");
image.put("numComments", ++numComments);
image.saveInBackground();
} else {
Log.d("numComments: ", "error");
}
}
});
}
comment.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
loadingDialog.dismiss();
finish();
}
}
});
}
I encountered similar problem like yours.
I created an app where user can create account and add photo to it and list of objects (friends in my case).
Once when I was testing it user was created twice.
I went through my code and my my suspicions are connected with async calls.
I see that you use asynchronous parse api in you application so no fragment of code is waiting for response and blocking the rest of operations.
You cannot control when parse server will response.
What I did I just put all synchronous requests in my custom async code (AsyncTask in Android).
Hope that my answer somehow meeets your expectations.
I'm trying to write a simple test of the Firebase user creation and authentication routines so that I can test my Firebase security settings. The code runs smoothly but the callbacks are not invoked and no users are created on the Firebase side.
The output of below with the print statements is:
Begin process
Start Create User
Creating User
End Creating User
End process
Process finished with exit code 0
The code is using the firebase-client-android-2.2.3.jar file for the Firebase classes though I'm just running my test as a java application on a Mac OS. Later this will go into an Android app but I'd like to be able to run it inside my IDE for now. Any insights from experienced Firebase coders much appreciated.
import com.firebase.client.Firebase;
import com.firebase.client.AuthData;
import com.firebase.client.FirebaseError;
import java.util.*;
public class FireRulesTest {
static String firebase_baseUrl = "https://<myfirebase>.firebaseio.com/";
public static void main(String[] args)
throws FirebaseException {
System.out.println("Begin process");
FireRulesTest tester = new FireRulesTest();
tester.createUser();
System.out.println("End process");
}
private void createUser()
throws FirebaseException {
try {
System.out.println("Start Create User");
final String mEmail = "me#email.com";
final String mPassword = "password";
final Firebase ref = new Firebase(firebase_baseUrl);
System.out.println("Creating User");
ref.createUser(mEmail, mPassword,
new Firebase.ValueResultHandler<Map<String, Object>>() {
#Override
public void onSuccess(Map<String, Object> result) {
System.out.println("Successfully created user account with uid: " + result.get("uid"));
ref.authWithPassword(mEmail, mPassword, new Firebase.AuthResultHandler() {
#Override
public void onAuthenticated(AuthData authData) {
//success, save auth data
HashMap<String, Object> authMap = new HashMap<String, Object>();
authMap.put("uid", authData.getUid());
authMap.put("token", authData.getToken());
authMap.put("email", mEmail);
authMap.put("password", mPassword);
Firebase currentUserRef = new Firebase(firebase_baseUrl + "movo/users/" + authData.getUid());
authMap.put("currentUser", currentUserRef);
System.out.println("User ID: " + authData.getUid() +
", Provider: " + authData.getProvider() +
", Expires:" + authData.getExpires());
System.out.println("Authentication complete");
}
#Override
public void onAuthenticationError(FirebaseError firebaseError) {
System.out.println("Authentication Error authenticating newly created user. This could be an issue. ");
System.out.println(firebaseError.getMessage());
}
});
}
#Override
public void onError(FirebaseError firebaseError) {
System.out.println("On Error authenticating newly created user. This could be an issue. ");
System.out.println(firebaseError.getMessage());
}
});
System.out.println("End Creating User");
} catch (Exception fbe) {
System.out.println("Exception: " + fbe.getMessage());
fbe.printStackTrace();
}
}
}
You'll want to add a Thread.sleep at the end of the program. Likely your program exits before Firebase gets a chance to send anything to the server.
A more proper solution would be to introduce actual lifecycle management into your app, e.g. waiting for the createUser call to finish. But given that you'll be migrating this to Android (which handles app lifecycle completely different anyway) that might not be worth the effort.
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++;
}
}