Sarah
Sarah

Reputation: 109

Calculation Problem in Helper Class - Return is hit before calculation

My current problem is that I wanna create a Helper class that calculates my costs (stored in database) so that everything is organized better (because I need this calculation in two of my Activities).

Right now the calculation code (which is working fine) is just written inside the two Activities (which makes everything rather ugly).

So: I created a "CostIntervalHelper"-Class.

I put my code there and changed a few things but I am hitting a big problem right now. To explain it better, first here you go with the code of the calculation method:

 public BigDecimal calculateMonthlyCosts(SubViewModel subViewModel, Context context){
    //set this two times just for debugging reasons to see if the calculation actually works
    cMonthly = new BigDecimal(0);

    subViewModel.getAllMonthlyCosts().observe((LifecycleOwner) context, new Observer<BigDecimal>() {
        @Override
        public void onChanged(@Nullable BigDecimal bigDecimal) {
            cMonthly = new BigDecimal(0);
            if (bigDecimal != null) {
                cMonthly = cMonthly.add(bigDecimal);
            }
        }
    });

    subViewModel.getAllBiannuallyCosts().observe((LifecycleOwner) context, new Observer<BigDecimal>() {
        @Override
        public void onChanged(@Nullable BigDecimal bigDecimal) {
            cBiannually = new BigDecimal(0);
            if (bigDecimal != null) {
                cBiannually = cBiannually.add(bigDecimal.divide(new BigDecimal(6), 2));
            }
        }
    });

    subViewModel.getAllYearlyCosts().observe((LifecycleOwner) context, new Observer<BigDecimal>() {
        @Override
        public void onChanged(@Nullable BigDecimal bigDecimal) {
            cYearly = new BigDecimal(0);
            if (bigDecimal != null) {
                cYearly = cYearly.add(bigDecimal.divide(new BigDecimal(12), 2));
                cMonthly = cMonthly.add(cBiannually).add(cYearly);
            }
        }
    });

    //return is hit before the calculation so it only returns "0"
    return cMonthly;
}

Now here we go with the problem (already put some comments in the code):

Whenever I call for the method, the return cMonthly is called first. In Debug-Mode I checked and I can say: AFTER the return statement was hit, the calculation gets done (in the right way).

Now I need an idea or example how I can make my code FIRST (in the right order) do all the subViewModel.getAll(Monthly/Biannually/Yearly)Costs().[...] methods and AFTERWARDS hit the return statement.

I know this is a bit tricky because I am using LiveData/ViewModel but maybe someone has an idea! Would be awesome! Thanks ahead!

Upvotes: 0

Views: 81

Answers (1)

EpicPandaForce
EpicPandaForce

Reputation: 81578

public BigDecimal calculateMonthlyCosts(SubViewModel subViewModel...) {

If you are using LiveData, then your calculations should not immediate in the form of synchronous function.

You don't want to return a BigDecimal, you want to create a LiveData that is based on chain between your other LiveData that will update the BigDecimal when either of the LiveData changes.

For this, you should create a single MediatorLiveData, and should observe that LiveData to receive the latest value of the BigDecimal whenever any of its "components" change.

public LiveData<BigDecimal> calculateMonthlyCosts(SubViewModel subViewModel) {
    MediatorLiveData<BigDecimal> mediator = new MediatorLiveData<>() {
        private BigDecimal cMonthly = new BigDecimal(0);
        private BigDecimal cBiannually = new BigDecimal(0);
        private BigDecimal cYearly = new BigDecimal(0);

        {
            addSource(subViewModel.getAllMonthlyCosts(), new Observer<BigDecimal>() {
                @Override
                public void onChanged(@Nullable BigDecimal bigDecimal) {
                    if (bigDecimal != null) {
                        cMonthly = cMonthly.add(bigDecimal);
                        setValue(cMonthly);
                    }
                }
            });

            addSource(subViewModel.getAllBiannuallyCosts(), new Observer<BigDecimal>() {
                @Override
                public void onChanged(@Nullable BigDecimal bigDecimal) {
                    if (bigDecimal != null) {
                        cBiannually = cBiannually.add(bigDecimal.divide(new BigDecimal(6), 2));
                        setValue(cMonthly); // TODO: ???
                    }
                }
            });

            addSource(subViewModel.getAllYearlyCosts(), new Observer<BigDecimal>() {
                @Override
                public void onChanged(@Nullable BigDecimal bigDecimal) {
                    if (bigDecimal != null) {
                        cYearly = cYearly.add(bigDecimal.divide(new BigDecimal(12), 2));
                        cMonthly = cMonthly.add(cBiannually).add(cYearly);
                        setValue(cMonthly);
                    }
                }
            });
         }
    };

    return mediator;
}

Please note that I am not 100% certain about your business logic and might need to follow the way of "using tuples to combine multiple LiveData" as outlined here or here.

You can also refer to this video.

Upvotes: 1

Related Questions