Why is this List sometimes null and sometimes not? - java

I have an activity that shows the results of the previous activity to the user. I save these results to a database. In my onCreate method I create a reference to my ViewModel and populate a List.
mAtcViewModel = new AtcViewModel(getApplication());
atcUserStatsList = mAtcViewModel.getAllUsersList();
I then go on call the save to database method after this assignment:
private void saveToDB(String playerName) {
mUser = playerName;
getAtcUser getAtcUser = new getAtcUser(playerName, this);
getAtcUser.delegate = this;
getAtcUser.execute();
}
When I get the result from onPostExecute this method is called:
#Override
public void processFinish(AtcUserStats atc_user) {
callOnChanged(atc_user);
}
This then calls a method where I am able to see the users previous stored results and adjust them with their new results but in this block of code a problem only sometimes arises, saying that atcUserStatsList is null:
try {
for (int i = 0; i < atcUserStatsList.size(); i++) {
if (atcUserStatsList.get(i).getUserName().equals(mUser)) {
mAtcViewModel.updateAtcPlayerStats(mUser, finalDartsHit, finalGamesWon, finalGamesPlayed, finalSinglesPlayed, finalDoublesPlayed, finalTreblesPlayed, finalDartsThrown,
finalSinglesDartsThrown, finalDoublesDartsThrown, finalTreblesDartsThrown, finalSinglesDartsHit, finalDoublesDartsHit, finalTreblesDartsHit, finalSecondPlace, finalThirdPlace);
}
}
} catch (Execption e) {
e.printStackTrace();
Log.d("atcUserStatsList", "Unable to save to database");
getMessage("Unable to save to database");
}
When I debug the list is populated most of the time and then when I run the app without debugging sometimes it is empty/null and the snackbar message appears and nothing is saved whereas other times it wouldn't appear and results are saved? Why would this be?
Is it anything to do with the lifecycle?
EDIT:
For example here is the list populated while debugging now this code will work:
And tbh for some reason it can be hard to get this list not Null when debugging(?).

Related

Getting unwanted popup "You have not saved your changes. If you close this flow, then your changes will be lost. Do you want to continue?"

Getting this popup every time whenever calling a bean method via valueChangeListener property from SelectOneChoice in a jsff page.
I need help to block this unwanted popup.
SelectOneChoice's property of the .jsff page:
<af:selectOneChoice value="................."
label=".................."
required="..............."
shortDesc=".............."
id="....................."
valueChangeListener="#{TransferWorkAreaBean.onBookLovChange}"
autoSubmit="true">
<f:selectItems value="............" id="si2"/>
<f:validator binding="......."/>
</af:selectOneChoice>
Method in Bean Class::
public void onBookLovChange(ValueChangeEvent valueChangeEvent) {
valueChangeEvent.getComponent().processUpdates(FacesContext.getCurrentInstance());
invokeELMethod("#{bindings.methodToExecute.execute}", new Class[0], new Object[0]);
AdfFacesContext.getCurrentInstance().addPartialTarget(getBusinessTable());
}
Method details of binding Method::
public void executeInvetoryQueryOnBookChange(String btg) {
OAViewObjectImpl vo = getBusinessOverview();
VariableValueManager vvm = vo.ensureVariableManager();
vvm.setVariableValue("bindBookTypeCode", btg);
vo.executeQuery();
}
Please note, in some places I have used encrypted data for policy.
Please also note, that the uncommittedDataWarning property is not ENABLED.
This popup only appear when the option uncommittedDataWarning is set to "on" at the root af:document tag. Try to run a full search in your JDevelopper for "uncommittedDataWarning".
Another way of avoiding this popup in this specific case would be to ensure that your data are committed or rollback in your data model. As the popup only appear if some data aren't committed when a user navigate outside the af:document. You could run something like so right before your
invokeELMethod("#{bindings.methodToExecute.execute}", new Class[0], new Object[0]);
How to commit if needed (https://cedricleruth.com/how-to-programmatically-commit-or-rollback-a-transaction-in-oracle-adf/)
private void commitIfDirty() {
try {
ViewObject vo = this.getViewObjectFromIterator("YOUR_ITERATOR_NAME");
boolean isNotSaved = vo.getApplicationModule()
.getTransaction()
.isDirty();
if (isNotSaved) {
vo.getApplicationModule()
.getTransaction()
.validate();
vo.getApplicationModule()
.getTransaction()
.commit();
}
} catch (ValidationException validationException) {
//log it and warn the user that his data isn't valid
} catch (Exception error) {
//log it and warn the user something went wrong
}
}
private ViewObjectImpl getViewObjectFromIterator(String nomIterator) {
ViewObjectImpl returnVO = null;
DCBindingContainer dcb = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
if (dcb != null) {
DCIteratorBinding iter = dcb.findIteratorBinding(nomIterator);
if (iter != null) {
returnVO = (ViewObjectImpl)iter.getViewObject();
}
}
return returnVO;
}

Firestore NullPointerException on retrieve firebase.Timestamp.toDate (FieldValue.serverTimestamp update slow to update)

After calling a batch.commit or docRef.update (part of it calls FieldValue.serverTimeStamp to update time of submission), I call finish(); to go back to previous activity that loads a recycleView of the list of documents that was updated.
I get this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.Date com.google.firebase.Timestamp.toDate()' on a null object reference`
I suspect it's that FieldValue.servertimeStamp takes more time to compute and the app crashes. However, the same field where recyclerView is pulling the datetime from already have an old value.
I'm not sure why the old value is not retrieved, but crashes on null instead.
Q1) Does FieldValue.servertimeStamp make the field null until new datetime is computed?
My guess is, this particular call is waiting for an answer from Firebase server, thus taking more time but other calls are done locally first on the device before updating in the cloud. Some of your insights is appreciated.
In the mean time, as a work-around to stop this asynchronous error, I have used a Thread loop with Thread.sleep while waiting for onCompleteSuccess to respond:
FirestoreFieldUpdated = false;
Thread myThread = new Thread(
new Runnable() {
#Override
public void run() {
try {
while (!FirestoreFieldUpdated) { //db.updateFields will change FirestorefieldUpdated to true uponSuccess
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
finish();
}
}
}
);
myThread.start();
Q2) Is there a more elegant way or better way to do this? Or to enable synchronicity only for this particular transaction of updating datetime?
EDIT (added on more details on what I'm trying to do):
I am trying to call this method from AddNewOrder.java:
public void updateFields(String actionDateField) {
Map<String, Object> updates = new HashMap<>();
updates.put(actionDateField, FieldValue.serverTimestamp());
updateSubmit.update(updates);
}
from a class outside (AddNewOrder.java):
db = new DatabaseHelper(getApplicationContext());
db.updateFields("OrderDate");
finish();
Finish(); will then pass me back to the previous activity that calls RecyclerView:
Query query = mFirestore
.collection("Org")
.document(RootCollection)
.collection("Stores")
.document(StoreID)
.collection("Orders")
.whereGreaterThan("OrderVerified", "")
.limit(queryLimit);
mAdapter = new OrdersAdapter(query, FulfilmentActivity.this) {
#Override
protected void onError(FirebaseFirestoreException e) {
// Show a snackbar on errors
Snackbar.make(findViewById(android.R.id.content),
"Error: check logs for info.", Snackbar.LENGTH_LONG).show();
}
};
mAdapter.setQuery(query);
mOrdersRecycler.setAdapter(mAdapter);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
mOrdersRecycler.setLayoutManager(layoutManager);
In OrdersAdapter.java, I have this:
Orders orders = snapshot.toObject(Orders.class);
submitDate.setText(FORMAT.format(orders.getOrderDate()));
in public void bind.
The above is the line that NullPointerException appeared on.
Orders.java:
public class Orders {
private Timestamp OrderDate;
public Orders(Timestamp orderDate) { this.OrderDate = orderDate; }
public java.util.Date getOrderDate() { return OrderDate.toDate(); }
}
How do I fix this properly?
First of all, if you're working with threading in order to deal with Firestore, you're almost certainly doing the wrong thing. All Firestore APIs (actually, all Firebase APIs) are asynchronous, and require no threading on the part of your app.
Q1 - there is no intermediate null value in a document that's going to be created with a timestamp. The server interprets the server timestamp token immediately and writes a Timestamp object to the document atomically.
Q2 - I can't really tell what you're trying to accomplish with this code. It's way out of bounds of what you would normally do to write to Firestore. If you want to know if a document has changed in Firestore, you attach a listener to a reference to that document, and the listener is invoked when the document is seen to change. Again, there's no need for threading in this case, because the callbacks are all asynchronous. As long as the listener is added, it will be called.
serverTimeStamp() indeed becomes Null in the cache before getting a confirm response from the server: https://github.com/firebase/firebase-js-sdk/issues/192
Refer to this for solution: https://firebase.google.com/docs/reference/android/com/google/firebase/firestore/Document
I fixed this by adding this:
DocumentSnapshot.ServerTimestampBehavior behavior = ESTIMATE;
Date date = snapshot.getDate("OrderDate", behavior);

How to make sure that NullPointerException doesn't come while showing infinite data from api.

I am fetching data from api and showing them into recyclerview.
I am using InfiniteScroll to load more data on scroll.
It works fine if I scroll smoothly but if I scroll fast I get NullPointerException as data is not being fetched on time even after I am checking if model data is not null in Adapter class.
How to avoid this situation?
Please find the code below:
#NonNull
private InfiniteScrollListener createInfiniteScrollListener() {
return new InfiniteScrollListener(10, linearLayoutManager) {
#Override public void onScrolledToEnd(final int firstVisibleItemPosition) {
offset += 10;
final List<Purchase2> itemsLocal = loadMore(offset);
refreshView(recyclerView, new PurchaseMainTestAdapter(itemsLocal, R.layout.list_orders_layout, getApplicationContext(), emptyView), firstVisibleItemPosition);
}
};
}
private List<Purchase2> loadMore(int index){
progressBar.setVisibility(View.VISIBLE);
//add loading progress view
purchases.add(new Purchase2());
adapter.notifyItemInserted(purchases.size()-1);
OneViewApi apiService =
ApiClient.getClient().create(OneViewApi.class);
Call<Orders> call;
call = apiService.getPurchaseData(auth_token,index,10);
Log.d("Called url is:", call.request().url().toString());
call.enqueue(new Callback<Orders>() {
#Override
public void onResponse(Call<Orders> call, Response<Orders> response) {
if(response.isSuccessful()){
//remove loading view
purchases.remove(purchases.size()-1);
List<Purchase2> result = response.body().getPurchases();
if(result.size()>0){
//add loaded data
purchases.addAll(result);
}else{//result size 0 means there is no more data available at server
adapter.setMoreDataAvailable(false);
Toast.makeText(getApplicationContext(),"No More Data Available",Toast.LENGTH_LONG).show();
}
progressBar.setVisibility(View.GONE);
//should call the custom method adapter.notifyDataChanged here to get the correct loading status
}else{
Log.e("Item list"," Load More Response Error "+String.valueOf(response.code()));
}
}
#Override
public void onFailure(Call<Orders> call, Throwable t) {
Log.e("Item list"," Load More Response Error "+t.getMessage());
}
});
return purchases;
}
at first use try{} catch{} then trace your code via break point to fine where exception happened
but i guess exception occur in here :purchases.remove(purchases.size()-1); that your list is null and you are trying to remove an item.(in first time) or about adding and removing items.
but for detect showing load more or not you can add null to purchases list then handle it in adapter - it's too better
Got the fix for this null pointer Exception:
1.) Added adapter code inside
try{} catch{} block
2.) Has set the flag so that on scroll it could not call the service again until last one is executed
if(!isLoading) {
isLoading = true;
final List<Purchase2> itemsLocal = loadMorePurchaseData(offset, null, null, null);
refreshView(recyclerView, new PurchaseMainAdapter(itemsLocal, R.layout.list_orders_layout, getApplicationContext(), emptyView), firstVisibleItemPosition);}
And in network call set the appropriate flag:
public void onResponse(Call<Orders> call, Response<Orders> response) {
if(response.isSuccessful()){
List<Purchase2> result = response.body().getPurchases();
if(result.size()>0){
//add loaded data
purchases.addAll(result);
}else{//result size 0 means there is no more data available at server
purchase_adapter.setMoreDataAvailable(false);
//telling adapter to stop calling load more as no more server data available
Toast.makeText(getApplicationContext(),"No More Data Available",Toast.LENGTH_LONG).show();
}
progressBar.setVisibility(View.GONE);
isLoading = false;
//should call the custom method adapter.notifyDataChanged here to get the correct loading status
}else{
Log.e("Item list"," Load More Response Error "+String.valueOf(response.code()));
}
Thanks everyone for giving the hint.

Android - Dropbox Datastore is "empty" until application restarts

When I sign in on my application and try to get the size from the datastore's table, it is empty. But after I restart the application, it works as it should and gives back the size of the datastore's table.
In my case, there are two activities. The activity, and in-turn the fragment, SettingsFragment.java has the sign-in part. The activity MainActivity.java has the part with getting the datastore's table size.
SettingsFragment.java (Sign-in).
// Dropbox Signin
mAccountManager = DbxAccountManager.getInstance(getActivity().getApplicationContext(), APP_KEY, SECRET_KEY);
mDropBoxPreference = findPreference("preference_sync_dbx");
mDropBoxPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
if (mAccountManager.hasLinkedAccount()) {
mAccountManager.unlink();
} else {
mAccountManager.startLink(getActivity(), REQUEST_LINK_TO_DBX);
}
return true;
}
});
MainActivity.java (Get datastore size).
// Dropbox Get Datastore Size
mAccountManager = DbxAccountManager.getInstance(getApplicationContext(), APP_KEY, SECRET_KEY);
if (mAccountManager.hasLinkedAccount()) {
DbxAccount mDbxAccount = mAccountManager.getLinkedAccount();
try {
mDbxStore = DbxDatastore.openDefault(mDbxAccount);
mDbxNotesTable = mDbxStore.getTable("notes");
mDbxNotesCount = mDbxNotesTable.query().count();
Log.e("mDbxNotesCount", Integer.toString(mDbxNotesCount));
} catch (DbxException e) {
e.printStackTrace();
}
}
Let's say there are 3 records in the datastore.
After I sign in, this shows up from the Logcat.
E/mDbxNotesCount﹕ 0
Once I restart it, this shows up from the Logcat.
E/mDbxNotesCount﹕ 3
What's causing it to work like this?
Edit: Added listener after using smarx's answer, and it works! Here is what I did.
mDbxStore.addSyncStatusListener(new DbxDatastore.SyncStatusListener() {
#Override
public void onDatastoreStatusChange(DbxDatastore dbxDatastore) {
if (!dbxDatastore.getSyncStatus().isDownloading) {
try {
mDbxStore.sync();
mDbxNotesCount = mDbxNotesTable.query().count();
} catch (DbxException e) {
e.printStackTrace();
}
Log.e("mDbxNotesCount", Integer.toString(mDbxNotesCount));
}
}
});
It looks like you're trying to read the contents of the datastore before it's had a chance to sync. You'll need to wait until the initial sync is completed. I think one way to do that would be to add a listener via addSyncStatusListener and wait for store.getSyncStatus().isDownloading to be false.
But typically the pattern you'll want to follow is to always show the current information... so set up a listener and update your UI any time it changes. That way whether it's the initial sync bringing in new records or updates from a different device, your app will respond appropriately when the information changes.

CheckboxField gives StackOverflow Error in Blackberry java application

I am creating 2-3 checkbox field in my screen and adding those in a vertical field manager. Idea here is to disable other check box on click of another checkbox. but it is giving me stackoverflow error. I am posting my code here...
final CheckboxField[] checkBoxField = new CheckboxField[2];
checkBoxField[0] = cashCardCheckboxField;
checkBoxField[1] = creditDebitCardCheckboxField;
checkBoxField[0].setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
if(context != FieldChangeListener.PROGRAMMATIC){ //It means manually clicked by User
if(checkBoxField[0].getChecked()){
checkBoxField[0].setChecked(false);
}else{
checkBoxField[0].setChecked(true);
//Please wait Screen starts
// call here a user defined function to populate the drop down list
//Please wait Screen ends
}
}else{
checkBoxField[0].setChecked(false);
}
}
});
checkBoxField[1].setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
if(context != FieldChangeListener.PROGRAMMATIC){ //It means manually clicked by User
if(checkBoxField[1].getChecked()){
checkBoxField[1].setChecked(false);
}else{
checkBoxField[1].setChecked(true);
//Please wait Screen starts
// call here a user defined function to populate the drop down list
//Please wait Screen ends
}
}else{
checkBoxField[1].setChecked(false);
}
}
});
Thanks and Regards.
Solution
Try using this code. It allows you to create any number of checkboxes. When one is checked, the listener will uncheck all the other ones.
public class CheckBoxScreen extends MainScreen {
private CheckboxField checkBoxField[];
public CheckBoxScreen() {
super(MainScreen.VERTICAL_SCROLL | MainScreen.VERTICAL_SCROLLBAR);
checkBoxField = new CheckboxField[3];
checkBoxField[0] = new CheckboxField("one", true); // checked by default
checkBoxField[1] = new CheckboxField("two", false);
checkBoxField[2] = new CheckboxField("three", false);
FieldChangeListener listener = new CheckboxListener();
for (int i = 0; i < checkBoxField.length; i++) {
checkBoxField[i].setChangeListener(listener);
add(checkBoxField[i]);
}
}
private class CheckboxListener implements FieldChangeListener {
public void fieldChanged(Field field, int context) {
if (context != FieldChangeListener.PROGRAMMATIC) {
// user modified this field
CheckboxField checkbox = (CheckboxField)field;
if (checkbox.getChecked()) {
// uncheck the other checkboxes
for (int i = 0; i < checkBoxField.length; i++) {
if (checkBoxField[i] != checkbox && checkBoxField[i].getChecked()) {
checkBoxField[i].setChecked(false);
}
}
}
} else {
// nothing more to do here ... this time, fieldChanged() is being
// called as a result of calling setChecked() in the code.
}
}
}
}
Why Your Code Created a Stack Overflow
The fieldChanged() method gets called whenever a field has its properties modified. For a CheckboxField, that happens when the field is checked or unchecked. This can happen either because the user checks/unchecks the field, or because your code calls setChecked(boolean). This line:
if(context != FieldChangeListener.PROGRAMMATIC){ //It means manually clicked by User
is what allows the code inside fieldChanged() to determine if this call is occurring because the user changed the field, or because setChecked() was called by your code. Unfortunately, you have placed calls to setChecked() in all branches of the if statement, which causes setChecked() to be called both for user events, and when your code changes the fields.
In this code:
checkBoxField[0].setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
if(context != FieldChangeListener.PROGRAMMATIC){ //It means manually clicked by User
if(checkBoxField[0].getChecked()){
checkBoxField[0].setChecked(false);
}else{
checkBoxField[0].setChecked(true);
//Please wait Screen starts
// call here a user defined function to populate the drop down list
//Please wait Screen ends
}
}else{
checkBoxField[0].setChecked(false);
}
}
});
it is the last line (checkBoxField[0].setChecked(false);) that is causing problems. That else branch is reached after calling setChecked() in the "manually clicked" branch of the if statement. Once you call setChecked(false) again, you have modified the field programmatically. This will cause fieldChanged() to be called back again. And again. And again. This loop will never stop, until the program stack is full, and your app crashes.
So, if you see my code, you notice that I am not calling setChecked() in the programmatic branch of the if statement. Another problem in your code is that you don't have the correct logic to uncheck checkbox 1 when checkbox 0 is checked. But, that's only a functional bug. The recursive call to fieldChanged() is what caused the stack overflow.
Hope that helps.

Categories

Resources