Concurrent access to static method - java

I have an activity that contains 2 ArcProgress , both of them get data from seperate childs from Firebase, so I created a static method in a seperate Class that change values of Arcs and then call it on the firebase's onDataChange method. The problem that the arcs shows wrong values and I think that is caused by a concurrent access to that class.
I tried to add synchronized but that doesn't solve the problem.
This is the class that contains the method
public class ArcProgSettings {
private static int diff=0;
private static int i;
private static CountDownTimer waitTimer;
private int previousTemp=0;
private int newT=0;
private ArcProgress arcTemp;
public ArcProgSettings(){
}
public ArcProgSettings(int previousTemp, int newT, final ArcProgress
arcTemp){
this.previousTemp=previousTemp;
this.newT=newT;
this.arcTemp=arcTemp;
}
public synchronized void setProg() {
diff = newT - previousTemp;
i = previousTemp;
if (diff >= 0) {
waitTimer = new CountDownTimer((diff + 2) * 100, 100) {
public void onTick(long millisUntilFinished) {
//called every 300 milliseconds, which could be used to
//send messages or some other action
arcTemp.setProgress(i);
i++;
}
public void onFinish() {
//After 60000 milliseconds (60 sec) finish current
//if you would like to execute something when time
finishes
}
}.start();
} else {
waitTimer = new CountDownTimer(-(diff - 2) * 100, 100) {
public void onTick(long millisUntilFinished) {
arcTemp.setProgress(i);
i--;
}
public void onFinish() {
}
}.start();
}
}
}
and this is the Activity that contains the calls:(setProg)
public class VisualisationActivity extends AppCompatActivity {
private ArcProgress arcTemp;
private DatabaseReference mTempDatabase;
private String MyTemp="0";
int previousTemp=0;
private ArcProgress arcCurrent;
private DatabaseReference mCurrentDatabase;
private String MyCurrent="0";
int previousCurrent=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_visualisation);
arcTemp=(ArcProgress)findViewById(R.id.arc_prog_temp);
mTempDatabase =
FirebaseDatabase.getInstance().getReference().child("temp");
mTempDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
MyTemp =(dataSnapshot.child("value").getValue().toString());
final int newT= Integer.parseInt(MyTemp);
ArcProgSettings arc=new ArcProgSettings(previousTemp,
newT,arcTemp);
arc.setProg();
previousTemp=newT;
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
arcCurrent=(ArcProgress)findViewById(R.id.arc_progress_current);
mCurrentDatabase =
FirebaseDatabase.getInstance().getReference().child("current");
mCurrentDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
MyCurrent =(dataSnapshot.child("value").getValue().toString());
final int newC= Integer.parseInt(MyCurrent);
ArcProgSettings arc1=new ArcProgSettings(previousCurrent,
newC,arcCurrent);
arc1.setProg();
previousCurrent=newC;
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}

If you need separate timers and progress for each listener, why to use static variables? (waitTimer, diff, i). The problem is that both instances of ArcProgress are updating the same values, its not concurrency, is design. Change your variables to make it private only and you'll se a different result.

Related

How to constantly update query for Firebase Realtime Database with LiveData and ViewModel in Android

I use Firebase Realtime Database and LiveData with ViewModel in Android and I would like to constantly update the query.
Here is the ViewModel class:
public class ViewModel_MainActivity extends ViewModel {
/*
LiveData with Database query for the Firebase node "Ratings"
*/
private static long currentTimeMillisRating = System.currentTimeMillis();
private static double pastMinuteForDisplayingRatings = 1;
private static long pastTimeMillisRatings = System.currentTimeMillis() - (long) (pastMinuteForDisplayingRatings * 60 * 60 * 1000);
private static final Query QUERY_RATINGS =
FirebaseDatabase.getInstance(DB_SQLite_Firebase_Entries.FIREBASE_URL).getReference().child(DB_SQLite_Firebase_Entries.FIREBASE_NODE_RATINGS).orderByChild(DB_SQLite_Firebase_Entries.FIREBASE_RATINGDATEINMILLISECONDS).endAt(pastTimeMillisRatings);
private final LiveData_FirebaseRating liveData_firebaseRating = new LiveData_FirebaseRating(QUERY_RATINGS);
#NonNull
public LiveData_FirebaseRating getDataSnapshotLiveData_FirebaseRating() {
Log.e("LogTag_ViMo", "pastTimeMillisRatings: " + pastTimeMillisRatings);
return liveData_firebaseRating;
}
}
Here is the LiveData class:
public class LiveData_FirebaseRating extends LiveData<DataSnapshot> {
private static final String LOG_TAG = "LiveData_FirebaseRating";
private Query query;
private final LiveData_FirebaseRating.MyValueEventListener listener = new LiveData_FirebaseRating.MyValueEventListener();
DataSnapshot currentDataSnapShotFromFirebase;
public LiveData_FirebaseRating(Query query) {
this.query = query;
}
public LiveData_FirebaseRating(DatabaseReference ref) {
this.query = ref;
}
public void changeQuery(Query newQuery) {
this.query = newQuery;
}
public DataSnapshot getCurrentDataSnapShotFromFirebase() {
return currentDataSnapShotFromFirebase;
}
public void setCurrentDataSnapShotFromFirebase(DataSnapshot currentDataSnapShotFromFirebase) {
this.currentDataSnapShotFromFirebase = currentDataSnapShotFromFirebase;
}
#Override
protected void onActive() {
Log.d(LOG_TAG, "onActive");
query.addValueEventListener(listener);
}
#Override
protected void onInactive() {
Log.d(LOG_TAG, "onInactive");
query.removeEventListener(listener);
}
private class MyValueEventListener implements ValueEventListener {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
setValue(dataSnapshot);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(LOG_TAG, "Can't listen to query " + query, databaseError.toException());
}
}
}
And here is the part of the main activity, where the live data and view model are created and observed:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
/*
Initiate View Model with LiveData for Firebase
*/
rootRef_Firebase = FirebaseDatabase.getInstance(DB_SQLite_Firebase_Entries.FIREBASE_URL).getReference();
viewModel = new ViewModelProvider(this).get(ViewModel_MainActivity.class);
liveData_firebaseRating = viewModel.getDataSnapshotLiveData_FirebaseRating();
liveData_firebaseRating.observe(this, new Observer<DataSnapshot>() {
#Override
public void onChanged(#Nullable DataSnapshot dataSnapshot) {
liveData_firebaseRating.setCurrentDataSnapShotFromFirebase(dataSnapshot);
if(liveData_firebaseRating.getCurrentDataSnapShotFromFirebase()!=null) {
//Do something with the dataSnapshot of the query
}
}// end onChange
}); //end observe
}
So my problem lies in the non-updating of the QUERY_RATINGS in the class ViewModel_MainActivity . What I want is to return all the nodes from the FIREBASE_NODE_RATINGS whose at attribute FIREBASE_RATINGDATEINMILLISECONDS is older than 1 Minute (meaning that the entry was created at least 1 Minute before the current time slot). How can I do that such that I always get these nodes?
Firebase Query objects are immutable, and their parameters are fixed values. So there is no way to update the existing query, nor is there a "one minute ago" value that automatically updates.
The options I can quickly think off:
Create a new query frequently to capture the new full range of relevant nodes.
Create a new query frequently to capture only the new nodes (by combining startAt and endAt) and merge them with the results you already have.
Remove the condition from the query, and perform the filtering in your application code.
Based on what I know, I'd probably start with the third option as it seems the simplest.

Making repetitive CountDownTimer with Different Times

I am trying to learn coding. So I decided to make a little project but i stuck.
I am trying to make a CountDownTimer. I have 3 different times. For example first one is 10 sec, second one 5 sec and the third one is 7 sec. So I wanna make an app that start the count from 10 sec and when it finish it start the count from second timer and then third one.
public class MainActivity extends AppCompatActivity {
private Button mStartButton;
private Button mResetButton;
private Button mStopButton;
private TextView mTextViewCountDown;
private TextView mTextViewCounter;
private CountDownTimer mCountDownTimer;
private int countme = 0 ;
private int [] array = new int[3];
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mStartButton = findViewById(R.id.button_start);
mStopButton = findViewById(R.id.button_stop);
mResetButton = findViewById(R.id.button_reset);
mTextViewCountDown = findViewById(R.id.text_view_countdown);
mTextViewCounter = findViewById(R.id.text_s);
array[0]=10000;
array[1]=5000;
array[2]=70000;
mStartButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
for(int i = 0; i<3; i++){
mCountDownTimer = new CountDownTimer(array[i], 1000) {
#Override
public void onTick(long millisUntilFinished) {
mTextViewCountDown.setText(""+ millisUntilFinished/1000);
}
#Override
public void onFinish() {
countme++;
if(countme / 3 == 3){
mCountDownTimer.cancel();
}else{
start();
} } }.start();
}
}
}); } }
I don't think for loop is right for my problem. It does not increase variable i once, it increase in every ontick i guess. As a beginner, I couldn't figure out what should I do.
You do not need for loop try something like this:
private void startCountDowntimer(long millis, int count) {
count ++;
int finalCount = count;
new CountDownTimer(millis, 1000) {
#Override
public void onTick(long millisUntilFinished) {
mTextViewCountDown.setText(""+ millisUntilFinished/1000);
}
#Override
public void onFinish() {
if (finalCount == 1) {
startCountDowntimer(5000, 1);
} else if (finalCount == 2) {
startCountDowntimer(7000, 2);
} else {
//all finished
}
}
}.start();
}
and on the button click:
mStartButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startCountDowntimer(10000, 0);
}
});

Waiting for two onChange to trigger with MediatorLiveData

I'm currently trying to learn how to use MediatorLiveData as described here:
https://developer.android.com/reference/androidx/lifecycle/MediatorLiveData
What I want to do is wait for two livedata object to get an update then do some logic on both.
So in my activity i currently got this. While this works I'm currently only getting Orders while i would like to wait for Orders AND Orderrows to finish and then make some change.
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button myButt;
private MainViewModel mvw;
private TextView myView;
private MediatorLiveData data;
#Override
protected void onCreate(Bundle savedInstanceState) {
mvw = new ViewModelProvider(this).get(MainViewModel.class);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myButt = findViewById(R.id.button);
myView = findViewById(R.id.textview);
myButt.setOnClickListener(this);
data = new MediatorLiveData<>();
data.addSource(mvw.getAllOrders(), new Observer<Orders>() {
#Override
public void onChanged(Orders orders) {
data.setValue(orders);
}
});
data.addSource(mvw.getAllOrderRows(), new Observer<OrderRows>() {
#Override
public void onChanged(OrderRows orderRows) {
data.setValue(orderRows);
}
});
data.observe(this, new Observer<Orders>() { //this observers Orders but how do i get orders AND orderrows?
#Override
public void onChanged(Orders order) {
myView.setText(mvw.extractDate(order));
//Here i want to manipulate order and orderrows
Log.i("livedata" , order.getOrders().toString());
}
});
}
#Override
public void onClick(View view) {
switch(view.getId()){
case R.id.button:
Log.i("button","clicked");
mvw.updateOrderData(); // calls for new values to be fetched
mvw.updateOrderRowData();
}
}
}
in my viewmodel:
public class MainViewModel extends ViewModel {
private GetOrder getOrderRepo;
private MutableLiveData<Orders> allOrders;
private MutableLiveData<OrderRows> allOrderRows;
public MainViewModel(){
getOrderRepo = new GetOrder();
}
public MutableLiveData<Orders> getAllOrders() {
if(allOrders == null){
allOrders = new MutableLiveData<>();
allOrders = getOrderRepo.getAllOrders();
}
return allOrders;
}
public MutableLiveData<OrderRows> getAllOrderRows() {
if(allOrderRows == null){
allOrderRows = new MutableLiveData<>();
allOrderRows = getOrderRepo.getAllOrderRows();
}
return allOrderRows;
}
public void updateOrderData(){
Log.i("updating","updating data");
Orders orders = getOrderRepo.getAllOrders().getValue();
allOrders.setValue(orders);
}
public void updateOrderRowData(){
Log.i("updating","updating data");
OrderRows orderRows = getOrderRepo.getAllOrderRows().getValue();
allOrderRows.setValue(orderRows);
}
public String extractDate(Orders orders){
ArrayList<Order> listOfOrders = orders.getOrders();
Log.i("extractDate", ""+(listOfOrders.size()-1));
String date = listOfOrders.get(listOfOrders.size()-1).getOrderTime();
return date;
}
}
In the repostiory.
public class GetOrder {
private ApiService mAPIService;
MutableLiveData<Orders> allOrders;
MutableLiveData<OrderRows> allOrderRows;
public GetOrder(){
mAPIService = ApiUtils.getAPIService();
allOrders = new MutableLiveData<Orders>();
allOrderRows = new MutableLiveData<OrderRows>();
}
public MutableLiveData<Orders> getAllOrders(){
Log.i("func","starting func");
mAPIService.getOrders().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Orders>() {
#Override
public void onCompleted() {
Log.i("func","onComplete");
}
#Override
public void onError(Throwable e) {
Log.i("onError",e.toString());
}
#Override
public void onNext(Orders orders) {
Log.i("Repo",orders.toString());
allOrders.setValue(orders);
}
});
return allOrders;
}
public MutableLiveData<OrderRows> getAllOrderRows(){
Log.i("func","starting func");
mAPIService.getOrderRows().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<OrderRows>() {
#Override
public void onCompleted() {
Log.i("func","onComplete");
}
#Override
public void onError(Throwable e) {
Log.i("onError",e.toString());
}
#Override
public void onNext(OrderRows orderRows) {
Log.i("Repo",orderRows.toString());
allOrderRows.setValue(orderRows);
}
});
return allOrderRows;
}
}

Accessing a value from a service to an adapter

In Service class :
public void onNotify(TransferHandler<ProcessHolder> handler, int percentage) {
updateprocess(percentage);
}
in adapter
onBindViewHolder
progressBar = new ProgressBar(getContext());
progressBar = parentView.findViewById(R.id.progressbar_process);
now I want to access percentage from service class method to this adapter progress
Use EventBus library
After config that follow this steps
1 : Create servieClass.java
public class serviceClass {
private int percentage;
public serviceClass(int percentage) {
this.percentage = percentage;
}
public int getPercentage() {
return percentage;
}
}
2 : Change service
public void onNotify(TransferHandler<ProcessHolder> handler, int percentage) {
updateprocess(percentage);
EventBus.getDefault().post(new servieClass(percentage));
}
3 : add setPercentage function to your Adapter
public void setPercentage(int percentage){
this.percentage = percentage;
notifyDataSetChanged();
}
4 : Finally add this in fragment that you config EventBus in it
#Subscribe
public void onEvent(BlockedEvent event) {
adapter.setPercentage(percentage);
}
Good luck
In the Service class I wrote this
public void onNotify(TransferHandler<ProcessHolder> handler, int percentage) {
Intent intent = new Intent("PercentageUpdates");
intent.putExtra("percentage", percentage);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
and at the Activity side you have to receive this Broadcast message
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
mMessageReceiver, new IntentFilter("PercentageUpdates"));
By this way you can send percentage to an Activity. here mPercentageReceiver is the class in that class you will perform what ever you want....
private BroadcastReceiver mPercentageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String percentage = intent.getStringExtra("percentage");
if (percentage != null) {
// You can set the percentage here
}
}
};

Firebase Session Management: Issues with instatiating Global User

I'm a Android Studio coding beginner and currently building a nutrition app to get programming practice. I use Firebase for Authentication and as a database to save User data.
How it works:
My app has a survey built in which asks for body specifics and taste (age, height, liked/disliked ingredients etc.). I have a class GlobalUser with public static attributes to save the answers in the app. When the user registers, he is sent directly to the survey activity. There he answers the questions and the results are written to the Firebase database under his UID (I use a User class with the same attributes as GlobalUser to create an instance and use Firebase's setValue(Object) method). If he signs in (or still is signed in), the LoginRegistrationActivity directly sends him to the MainActivity. There, the GlobalUser class gets instantiated with the data saved under his UID. From the MainActivity, he can navigate to a ProfileActivity where the UI gets updated based on his data. This works quite well. After doing the survey I can find the results in a child node consisting of the UID of the user, the UI gets updated correctly and the sign in/registration process works as intended.
What is wrong:
However, as I was playing around with different designs and constantly restarting the app, it started to crash occasionally. After some testing it showed that the GlobalUser class wasn't updated and thus the ArrayLists were null and caused NullPointerExceptions when I used .size() on them. Since this issue only occurs rarely and seems to be related to restarting the app multiple times I thought it would have something to do with the Activity lifecycle so I also updated the GlobalUser in onStart and onResume but it didn't help. I also tried updating GlobalUser again in the ProfileActivity directly before the ArrayLists were set but it didn't work. I still guess it has something to do with the lifecycle but I have no idea where I should start. Here's the code of the relevant classes/actvitites:
LoginRegistrationActivity:
public class LoginRegistrationActivity extends AppCompatActivity {
private DatabaseReference mRef;
private FirebaseAuth mAuth;
private EditText emailAddress;
private EditText emailPassword;
private Button emailLogin;
private Button emailRegistration;
private TextView forgotPassword;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login_registration);
mAuth = FirebaseAuth.getInstance();
if (mAuth.getCurrentUser()!=null){
Intent i = new Intent (LoginRegistrationActivity.this, MainActivity.class);
LoginRegistrationActivity.this.startActivity(i);
}
emailAddress = findViewById(R.id.address_edit);
emailPassword = findViewById(R.id.password_edit);
emailLogin = findViewById(R.id.mail_login_button);
emailRegistration = findViewById(R.id.mail_registration_button);
forgotPassword = findViewById(R.id.forgot_password);
emailRegistration.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String email = emailAddress.getText().toString().trim();
String password = emailPassword.getText().toString().trim();
if (TextUtils.isEmpty(email)){
Toast.makeText(LoginRegistrationActivity.this, "Bitte E-Mail Addresse eingeben!", Toast.LENGTH_LONG).show();
return;
}
if (TextUtils.isEmpty(password)){
Toast.makeText(LoginRegistrationActivity.this, "Bitte Passwort eingeben!", Toast.LENGTH_LONG).show();
return;
}
if (password.length()<6){
Toast.makeText(LoginRegistrationActivity.this, "Passwort muss mindestens sechs Zeichen lang sein!", Toast.LENGTH_LONG).show();
return;
}
mAuth.createUserWithEmailAndPassword(email, password).addOnCompleteListener(LoginRegistrationActivity.this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (!task.isSuccessful()){
Toast.makeText(LoginRegistrationActivity.this, "Unbekannter Fehler", Toast.LENGTH_LONG).show();
} else {
Intent i = new Intent (LoginRegistrationActivity.this, SurveyGreetingActivity.class);
LoginRegistrationActivity.this.startActivity(i);
finish();
}
}
});
}
});
emailLogin.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String password = emailPassword.getText().toString();
String email = emailAddress.getText().toString();
if (TextUtils.isEmpty(email)){
Toast.makeText(LoginRegistrationActivity.this, "Bitte E-Mail Addresse eingeben!", Toast.LENGTH_LONG).show();
return;
}
if (TextUtils.isEmpty(password)){
Toast.makeText(LoginRegistrationActivity.this, "Bitte Passwort eingeben!", Toast.LENGTH_LONG).show();
return;
}
if (password.length()<6){
Toast.makeText(LoginRegistrationActivity.this, "Passwort muss mindestens sechs Zeichen haben!", Toast.LENGTH_LONG).show();
return;
}
mAuth.signInWithEmailAndPassword(email, password).addOnCompleteListener(LoginRegistrationActivity.this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (!task.isSuccessful()){
Toast.makeText(LoginRegistrationActivity.this, "Unbekannter Fehler beim Einloggen", Toast.LENGTH_LONG).show();
} else {
Intent i = new Intent (LoginRegistrationActivity.this, MainActivity.class);
LoginRegistrationActivity.this.startActivity(i);
finish();
}
}
});
}
});
}
}
MainActivity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final FirebaseAuth mAuth = FirebaseAuth.getInstance();
FirebaseUser user = mAuth.getCurrentUser();
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference mRef = database.getReference().child("users").child("uid").child(mAuth.getCurrentUser().getUid());
//In case the user cancelled the app when filling out the survey for the first time
if (mRef == null){
MainActivity.this.startActivity(new Intent (MainActivity.this, SurveyGreetingActivity.class));
}
//sets GlobalUser to data saved in Firebase Database User object
mRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
if (user!=null){
GlobalUser.setToUser(user);
GlobalUser.setGlobalUid(mAuth.getCurrentUser().getUid());
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(getApplicationContext(), "Database Error", Toast.LENGTH_LONG).show();
}
});
}
}
GlobalUser:
package com.example.andre.valetto02;
import java.util.ArrayList;
public class GlobalUser {
public static String globalUid = null;
public static ArrayList<Ingredient> globalLikes;
public static ArrayList<Ingredient> globalDislikes;
public static int globalAge;
public static int globalWeight;
public static int globalHeight;
public static int globalTrainingGoal;
public static int globalDailyActive;
public static boolean globalIsMale;
public GlobalUser() {
}
public static String getGlobalUid() {
return globalUid;
}
public static void setGlobalUid(String globalUid) {
GlobalUser.globalUid = globalUid;
}
public static ArrayList<Ingredient> getGlobalLikes() {
return globalLikes;
}
public static void setGlobalLikes(ArrayList<Ingredient> globalLikes) {
GlobalUser.globalLikes = globalLikes;
}
public static ArrayList<Ingredient> getGlobalDislikes() {
return globalDislikes;
}
public static void setGlobalDislikes(ArrayList<Ingredient> globalDislikes) {
GlobalUser.globalDislikes = globalDislikes;
}
public static int getGlobalAge() {
return globalAge;
}
public static void setGlobalAge(int globalAge) {
GlobalUser.globalAge = globalAge;
}
public static int getGlobalWeight() {
return globalWeight;
}
public static void setGlobalWeight(int globalWeight) {
GlobalUser.globalWeight = globalWeight;
}
public static int getGlobalHeight() {
return globalHeight;
}
public static void setGlobalHeight(int globalHeight) {
GlobalUser.globalHeight = globalHeight;
}
public static int getGlobalTrainingGoal() {
return globalTrainingGoal;
}
public static void setGlobalTrainingGoal(int globalTrainingGoal) {
GlobalUser.globalTrainingGoal = globalTrainingGoal;
}
public static int getGlobalDailyActive() {
return globalDailyActive;
}
public static void setGlobalDailyActive(int globalDailyActive) {
GlobalUser.globalDailyActive = globalDailyActive;
}
public static boolean isGlobalIsMale() {
return globalIsMale;
}
public static void setGlobalIsMale(boolean globalIsMale) {
GlobalUser.globalIsMale = globalIsMale;
}
public static void setToUser(User user) {
GlobalUser.setGlobalAge(user.getAge());
GlobalUser.setGlobalWeight(user.getWeight());
GlobalUser.setGlobalHeight(user.getHeight());
GlobalUser.setGlobalDailyActive(user.getDailyActive());
GlobalUser.setGlobalTrainingGoal(user.getTrainingGoal());
GlobalUser.setGlobalIsMale(user.getIsMale());
GlobalUser.setGlobalLikes(user.getLikes());
GlobalUser.setGlobalDislikes(user.getDislikes());
}
public static void resetLikesAndDislikes(){
globalLikes = new ArrayList <>();
globalDislikes = new ArrayList<>();
}
public static User globalToUser () {
return new User (globalLikes, globalDislikes, globalWeight, globalHeight, globalAge, globalTrainingGoal, globalDailyActive, globalIsMale);
}
}
User:
package com.example.andre.valetto02;
import java.util.ArrayList;
public class User {
ArrayList<Ingredient> likes;
ArrayList<Ingredient> dislikes;
Boolean isMale;
public Boolean getIsMale(){return isMale;}
public void setIsMale(Boolean b){isMale = b;}
public void setDislikes(ArrayList<Ingredient> dislikes) {
this.dislikes = dislikes;
}
public User (){
likes = new ArrayList<>();
dislikes = new ArrayList<>();
weight = 0;
height = 0;
age = 0;
trainingGoal = 2;
dailyActive = 1;
isMale=true;
}
public User (ArrayList<Ingredient> l, ArrayList<Ingredient> d, int w, int h, int a, int tG, int dA, boolean iM) {
likes = l;
dislikes = d;
weight = w;
height = h;
age = a;
trainingGoal = tG;
dailyActive = dA;
isMale = iM;
}
int age;
public ArrayList<Ingredient> getDislikes() {
return dislikes;
}
public ArrayList<Ingredient> getLikes() {
return likes;
}
public void setLikes (ArrayList<Ingredient> list){
likes = list;
}
public void setDisikes (ArrayList<Ingredient> list){
dislikes = list;
}
public int getAge () {
return age;
}
public void setAge (int i) {
age = i;
}
int weight;
public int getWeight (){
return weight;
}
public void setWeight(int i) {
weight = i;
}
int height;
public int getHeight (){
return height;
}
public void setHeight(int i) {
height = i;
}
int trainingGoal; //trainingGoal = 0 means weight loss, 1 means muscle gain and 2 means healthy living
public void setTrainingGoal(int i) {
trainingGoal = i;
}
public int getTrainingGoal(){
return trainingGoal;
}
int dailyActive; //dailyActive = 0 means wenig, 1 means leicht, 2 means moderat, 3 means sehr and 4 means extrem
public int getDailyActive() {return dailyActive;}
public void setDailyActive(int i) {dailyActive = i;}
public double computeCalorieGoal(){
if (isMale) {
double RMR;
RMR = weight*10 + 6.25*height - 5*age + 5;
if (dailyActive==0) {RMR=RMR*1.2;}
else if (dailyActive==1) {RMR=RMR*1.375;}
else if (dailyActive==2) {RMR=RMR*1.55;}
else if (dailyActive==3) {RMR=RMR*1.725;}
else {RMR=RMR*1.9;}
if (trainingGoal == 0) {RMR = RMR - 400;}
else if (trainingGoal ==1){RMR = RMR + 400;}
return RMR;
} else {
double RMR;
RMR = weight*10 + 6.25*height - 5*age - 161;
if (dailyActive==0) {RMR=RMR*1.2;}
else if (dailyActive==1) {RMR=RMR*1.375;}
else if (dailyActive==2) {RMR=RMR*1.55;}
else if (dailyActive==3) {RMR=RMR*1.725;}
else {RMR=RMR*1.9;}
if (trainingGoal == 0) {RMR = RMR - 300;}
else if (trainingGoal ==1){RMR = RMR + 300;}
return RMR;
}
}
}
Thanks for the help!
I just found the mistake. It has nothing to do with the activity lifecycle and it only indirectly had something to do with restarting the app. The problem was that Firebase's Value Event Listeners are still AsyncTasks. When I started the app and immediately opened the ProfileActivity, the Activity was created before the Firebase AsyncTask could fetch the data from the Database. Thus the ProfileActivity would call the .size() method on the ArrayLists before they were instantiated. In essence, the error occurred when you clicked too quickly through the UI and were faster than the asynchronous data fetching task.
Therefore I moved the session management to the LoginRegistrationActivity like this:
if (mAuth.getCurrentUser()!=null){
FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance();
DatabaseReference mRef = firebaseDatabase.getReference().child("users").child("uid").child(mAuth.getCurrentUser().getUid());
//In case the user cancelled the app when filling out the survey for the first time
if (mRef == null){
LoginRegistrationActivity.this.startActivity(new Intent (LoginRegistrationActivity.this, SurveyGreetingActivity.class));
}
mRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
if (user!=null) {
GlobalUser.setToUser(user);
GlobalUser.setGlobalUid(mAuth.getCurrentUser().getUid());
}
Intent i = new Intent (LoginRegistrationActivity.this, MainActivity.class);
LoginRegistrationActivity.this.startActivity(i);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
By moving LoginRegistrationActivity.this.startActivity(i) to the onDataChange method, I ensure that the GlobalUser variables get instantiated before the MainActivity is started. There are probably still more elegant ways to do this.

Categories