Issue
I am doing an app using a single activity architecture. The navigation is done by the navigation component. The relevant snippet of my navigation graph looks like that:
src="https://i.stack.imgur.com/RYrnn.png" alt="nav_graph" />
On the transition from listLensesFragment to generalLensInformationFragment a bundle is added.
The three fragments generalLensInformationFragment, leftLensDetailsFragment and rightLensDetailsFragment are using a shared ViewModel.
So far all of the things work - but as soon as I navigate for a second time from listLensesFragment to generalLensInformationFragment the app crashes at the point of receiving the bundle by using getArguments():
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_general_lens_information, container, false);
manageLensViewModel = new ViewModelProvider(getActivity()).get(ManageLensViewModel.class);
Bundle bundle = this.getArguments();
In debug mode, checking "this", bundle with its object inside is there.
But the logcat output looks the follwoing:
java.lang.IllegalStateException: Fragment LeftLensDetailsFragment{f731448} (30ef95ba-f165-4385-8b84-c63a7eb435c5) not attached to a context.
at androidx.fragment.app.Fragment.requireContext(Fragment.java:900)
at androidx.fragment.app.Fragment.getResources(Fragment.java:964)
at androidx.fragment.app.Fragment.getString(Fragment.java:986)
I have no clue what to do whit those errors. Below the most relevant parts of my code.
Clicking edit object in listLensesFragment:
adapter.setOnItemEditClickListener(new LensAdapter.OnItemEditClickListener() {
@Override
public void onItemClick(LensWithWears lensWithWears) {
Bundle bundle = new Bundle();
Lens lens = lensWithWears.lens;
bundle.putParcelable(BUNDLE_LENS, lens);
navController.navigate(R.id.action_listLensesFragment_to_generalLensInformationFragment, bundle);
}
});
On Create View of my GeneralLensInformationFragment:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_general_lens_information, container, false);
manageLensViewModel = new ViewModelProvider(getActivity()).get(ManageLensViewModel.class);
Bundle bundle = this.getArguments();
Lens lens;
if (bundle.getParcelable(BUNDLE_LENS) != null) {
manageLensViewModel.action = ACTION_UPDATE;
lens = bundle.getParcelable(BUNDLE_LENS);
manageLensViewModel.setLens(lens);
}
return view;
}
leftLensDetailsFragment
public class LeftLensDetailsFragment extends Fragment {
public static final String BUNDLE_LENS = "com.studioz.lenstimer.BUNDLE_LENS";
public static final String STRING_ACTION = "com.studioz.lenstimer.STRING_ACTION";
public static final int ACTION_CREATE = 1;
public static final int ACTION_UPDATE = 2;
SliderConverter sl;
private TextView txtLensName, txtShowDiopters, txtShowDiameter, txtShowBaseCurve;
private TextInputLayout edtTextBrandLayout, edtTextModelLayout;
private TextInputEditText edtTextBrand, edtTextModel;
private Slider sliderSelectDiopters, sliderSelectDiameter, sliderSelectBaseCurve;
private CheckBox cbUniformLenses;
private MaterialButton btnNextLens, btnCreateLens;
private ManageLensViewModel manageLensViewModel;
public LeftLensDetailsFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_left_lens_details, container, false);
manageLensViewModel = new ViewModelProvider(getActivity()).get(ManageLensViewModel.class);
sl = new SliderConverter();
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initializeViews(view);
setFieldValues();
edtTextBrand.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
Lens lens = manageLensViewModel.getLens().getValue();
lens.setBrandLeft(edtTextBrand.getText().toString());
manageLensViewModel.setLens(lens);
}
});
edtTextModel.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
Lens lens = manageLensViewModel.getLens().getValue();
lens.setModelLeft(edtTextModel.getText().toString());
manageLensViewModel.setLens(lens);
}
});
sliderSelectDiopters.addOnChangeListener(new Slider.OnChangeListener() {
@Override
public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) {
double diopters = sl.convertToDoubleFromDioptersSlider(sliderSelectDiopters.getValue());
txtShowDiopters.setText(String.valueOf(diopters) + " " + getString(R.string.unitDiopters));
}
});
sliderSelectDiameter.addOnChangeListener(new Slider.OnChangeListener() {
@Override
public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) {
double diameter = sl.convertToDoubleFromDiameterSlider(sliderSelectDiameter.getValue());
txtShowDiameter.setText(String.valueOf(diameter) + " " + getString(R.string.unitDiameter));
}
});
sliderSelectBaseCurve.addOnChangeListener(new Slider.OnChangeListener() {
@Override
public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) {
double basecurve = sl.convertToDoubleFromBaseCurveSlider(sliderSelectBaseCurve.getValue());
txtShowBaseCurve.setText(String.valueOf(basecurve) + " " + getString(R.string.unitBaseCurve));
}
});
cbUniformLenses.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
btnCreateLens.setVisibility(View.VISIBLE);
btnNextLens.setVisibility(View.GONE);
} else {
btnCreateLens.setVisibility(View.GONE);
btnNextLens.setVisibility(View.VISIBLE);
}
}
});
NavController navController = Navigation.findNavController(view);
btnNextLens.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
double d = 0;
Lens lens = manageLensViewModel.getLens().getValue();
d = sl.convertToDoubleFromDioptersSlider(sliderSelectDiopters.getValue());
lens.setDioptersLeft(d);
d = sl.convertToDoubleFromDiameterSlider(sliderSelectDiameter.getValue());
lens.setDiameterLeft(d);
d = sl.convertToDoubleFromBaseCurveSlider(sliderSelectBaseCurve.getValue());
lens.setBasecurveLeft(d);
lens.setUniform(false);
manageLensViewModel.setLens(lens);
navController.navigate(R.id.action_leftLensDetailsFragment_to_rightLensDetailsFragment);
}
});
btnCreateLens.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
double d = 0;
Lens lens = manageLensViewModel.getLens().getValue();
d = sl.convertToDoubleFromDioptersSlider(sliderSelectDiopters.getValue());
lens.setDioptersLeft(d);
d = sl.convertToDoubleFromDiameterSlider(sliderSelectDiameter.getValue());
lens.setDiameterLeft(d);
d = sl.convertToDoubleFromBaseCurveSlider(sliderSelectBaseCurve.getValue());
lens.setBasecurveLeft(d);
lens.setBrandRight(lens.getBrandLeft());
lens.setModelRight(lens.getModelLeft());
lens.setDioptersRight(lens.getDioptersLeft());
lens.setDiameterRight(lens.getDiameterLeft());
lens.setBasecurveRight(lens.getBasecurveLeft());
lens.setUniform(true);
manageLensViewModel.setLens(lens);
if (manageLensViewModel.action == ACTION_CREATE) {
manageLensViewModel.insert(manageLensViewModel.getLens().getValue());
}
if (manageLensViewModel.action == ACTION_UPDATE) {
manageLensViewModel.update(manageLensViewModel.getLens().getValue());
}
navController.navigate(R.id.action_leftLensDetailsFragment_to_listLensesFragment);
}
});
}
private void setFieldValues() {
Lens lens = manageLensViewModel.getLens().getValue();
txtLensName.setText(lens.getName());
edtTextBrandLayout.getEditText().setText(lens.getBrandLeft());
edtTextModelLayout.getEditText().setText(lens.getModelLeft());
if (lens.getDioptersLeft() != 0) {
txtShowDiopters.setText(String.valueOf(lens.getDioptersLeft()) + " " + getString(R.string.unitDiopters));
sliderSelectDiopters.setValue(sl.convertToFloatFromLensDiopters(lens.getDioptersLeft()));
}
if (lens.getDiameterLeft() != 0) {
txtShowDiameter.setText(String.valueOf(lens.getDiameterLeft()) + " " + getString(R.string.unitDiameter));
sliderSelectDiameter.setValue(sl.convertToFloatFromLensDiameter(lens.getDiameterLeft()));
}
if (lens.getBasecurveLeft() != 0) {
txtShowBaseCurve.setText(String.valueOf(lens.getBasecurveLeft()) + " " + getString(R.string.unitDiopters));
sliderSelectBaseCurve.setValue(sl.convertToFloatFromLensBaseCurve(lens.getBasecurveLeft()));
}
cbUniformLenses.setChecked(lens.isUniform());
}
private void initializeViews(View view) {
txtLensName = view.findViewById(R.id.txtLensName);
txtShowDiopters = view.findViewById(R.id.txtShowDiopters);
txtShowDiameter = view.findViewById(R.id.txtShowDiameter);
txtShowBaseCurve = view.findViewById(R.id.txtShowBaseCurve);
edtTextBrandLayout = view.findViewById(R.id.edtTextBrandLayout);
edtTextBrand = view.findViewById(R.id.edtTextBrand);
edtTextModelLayout = view.findViewById(R.id.edtTextModelLayout);
edtTextModel = view.findViewById(R.id.edtTextModel);
sliderSelectDiopters = view.findViewById(R.id.sliderSelectDiopters);
sliderSelectDiopters.setValue(sl.getSliderDioptersDefault());
sliderSelectDiopters.setValueTo(sl.getSliderDioptersEnd());
txtShowDiopters.setText(String.valueOf(sl.DEFAULT_DIOPTERS) + " " + getString(R.string.unitDiopters));
sliderSelectDiameter = view.findViewById(R.id.sliderSelectDiameter);
sliderSelectDiameter.setValue(sl.getSliderDiameterDefault());
sliderSelectDiameter.setValueTo(sl.getSliderDiameterEnd());
txtShowDiameter.setText(String.valueOf(sl.DEFAULT_DIAMETER) + " " + getString(R.string.unitDiameter));
sliderSelectBaseCurve = view.findViewById(R.id.sliderSelectBaseCurve);
sliderSelectBaseCurve.setValue(sl.getSliderBaseCurveDefault());
sliderSelectBaseCurve.setValueTo(sl.getSliderBaseCurveEnd());
txtShowBaseCurve.setText(String.valueOf(sl.DEFAULT_BASECURVE) + " " + getString(R.string.unitBaseCurve));
btnNextLens = view.findViewById(R.id.btnNextLens);
btnCreateLens = view.findViewById(R.id.btnCreateLens);
cbUniformLenses = view.findViewById(R.id.cbUniformLenses);
cbUniformLenses.setChecked(false);
btnCreateLens.setVisibility(View.GONE);
btnNextLens.setVisibility(View.VISIBLE);
}
}
In the code above, I stopped using an observer for my MutableLiveData in my ViewModel. This seemed to fix the issue but is not the reccommended way.
This way the data were observed when the app used to crash:
nameObserver = new Observer<Lens>() {
@Override
public void onChanged(Lens lens) {
// do all the stuff from setFieldValues()...
}
};
manageLensViewModel.getNameData().observe(this, nameObserver);
Can anybody support me on fixing this errors? I really have no idea what to do now. If further code snippets are required, please let me know!
Solution
The answer fro Bö macht Blau helped me - using Fragment.viewLiefecycleOwner did the job.
viewModel.getAllData().observe(getViewLifecycleOwner(), new Observer<List<LensWithWears>>() {
...do the job here...
});
Answered By - Zudy
Answer Checked By - David Goodson (JavaFixing Volunteer)