Hot questions for Handling nullpointerexception in MPAndroidChart
How to prevent java.lang.nullpointerexception on adding user input to MPandroidchart
Question: I've created a line chart in a fragment on android studio using MPandroidchart, and made it so that the points of the chart are from user input. However, I get the error 'java.lang.NullPointerException'. I understand that this error is caused by attempting to use a value that is null, but I'm not sure how to fix it in my case.
I retrieve the user input from a method where there is code to ensure that the input is not saved if it is null. I believe the issue is because the graph is attempting to build before the user can add the data, as the input option is part of the fragment where the graph is displayed, so the graph is trying to build before the data is ready.
Graph.java
public class Graph extends Fragment { public Graph() { // Required empty public constructor } private LineChart mChart; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_graph, container, false); //initialize input1 database InputRoomDatabase db = Room.databaseBuilder(getActivity(),InputRoomDatabase.class, "input_database") .build(); //initialize chart mChart = view.findViewById(R.id.chart); //set description Description description = new Description(); description.setText("Blood glucose levels"); mChart.setDescription(description); //set description if no data is available mChart.setNoDataText("No Data Available. Add a blood glucose level input to see your graph"); //enable touch gestures mChart.setTouchEnabled(true); //enable scaling and dragging mChart.setDragEnabled(true); mChart.setScaleEnabled(true); //draw background grid mChart.setDrawGridBackground(true); //draw x-axis XAxis xAxis = mChart.getXAxis(); xAxis.setEnabled(false); // setting position to TOP and INSIDE the chart xAxis.setPosition(XAxis.XAxisPosition.TOP_INSIDE); // setting text size for our axis label xAxis.setTextSize(10f); xAxis.setTextColor(Color.BLACK); // to draw axis line xAxis.setDrawAxisLine(true); xAxis.setDrawGridLines(false); YAxis yAxis = mChart.getAxisLeft(); // setting the count of Y-axis label's yAxis.setLabelCount(12, false); yAxis.setTextColor(Color.BLACK); yAxis.setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART); yAxis.setDrawGridLines(true); mChart.getAxisRight().setEnabled(false); // add data setData(); mChart.getLegend().setEnabled(false); // refresh the chart mChart.notifyDataSetChanged(); mChart.invalidate(); return view; } private void setData(){ ArrayList<Entry> values = new ArrayList<>(); ** Double d = getActivity().getIntent().getExtras().getDouble("STRING_I_NEED"); ** float myDataSet = d.floatValue(); values.add(new Entry(1,myDataSet)); LineDataSet set1; if (mChart.getData() != null && mChart.getData().getDataSetCount() > 0) { set1 = (LineDataSet) mChart.getData().getDataSetByIndex(0); set1.setValues(values); mChart.getData().notifyDataChanged(); mChart.notifyDataSetChanged(); } else { set1 = new LineDataSet(values, "Blood Glucose Levels"); set1.setDrawIcons(false); set1.setLineWidth(2f); set1.setCircleRadius(3f); set1.setDrawCircleHole(false); set1.setValueTextSize(9f); set1.setFormLineWidth(1f); set1.setFormSize(15.f); set1.setColor(Color.BLACK); set1.setCircleColor(Color.BLACK); } ArrayList<ILineDataSet> dataSets = new ArrayList<>(); dataSets.add(set1); LineData data = new LineData(dataSets); mChart.setData(data); } }
Tabbed.java (base class of the fragment) (relevant code only)
//getting inputs from room and putting them into the view in table public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); mInputViewModel = ViewModelProviders.of(this).get(InputViewModel.class); //only add if there is a new input if (requestCode == INPUT_ACTIVITY_REQUEST_CODE && resultCode == RESULT_OK) { //retrieve input from room database Input1 input1 = new Input1(data.getStringExtra(input.EXTRA_REPLY)); //insert the inputs into the view model of the recyclerView mInputViewModel.insert(input1); //convert the String input1 to a double Double d = Double.parseDouble(input1.getValue()); //display pop-up if blood is above target if (d > 8.0) { Toast.makeText( getApplicationContext(), R.string.PopupHigh, Toast.LENGTH_LONG).show(); } //display pop-up if blood is below target if (d < 4.0) { Toast.makeText( getApplicationContext(), R.string.PopupLow, Toast.LENGTH_LONG).show(); } Intent i = new Intent(Tabbed.this, Graph.class); i.putExtra("STRING_I_NEED", d); //display pop-up if no input is added don't save the input } else { Toast.makeText( getApplicationContext(), R.string.empty_not_saved, Toast.LENGTH_LONG).show(); } } public static final int INPUT_ACTIVITY_REQUEST_CODE = 1; }
When I run the app, it crashes and gives this error "java.lang.NullPointerException: Attempt to invoke virtual method 'double android.os.Bundle.getDouble(java.lang.String)' on a null object reference" I've marked the problem code in Graph.java with **
As I said, I believe the issue is that the graph is trying to build before the data is ready, but I'm not sure how to avoid this issue.
Answer: The code:
Double d = getActivity().getIntent().getExtras().getDouble("STRING_I_NEED"); float myDataSet = d.floatValue();
assumes 4 method calls all return non-null values. Please see the documentation for library methods to find out when they might return null
, and in general how to avoid unhappy surprises.
The documentation says getExtras()
returns: the map of all extras previously added with putExtra()
, or null if none have been added.
When calling a library method that can return null
, your code should check for the null
result (or catch the NullPointerException
), or ensure that that case can't happen, or both.
- Since
Intents
come in from the rest of the system, you can't guarantee that they will always have extras, so the code will need to test if the result ofgetExtras()
is null before callinggetDouble()
, and handle that gracefully. - You'll also need to debug why this
Intents
has no extras when you expected that it would. - If the
Intent
has extras but those extras don't have a double value called"STRING_I_NEED"
, thengetDouble("STRING_I_NEED")
will return0.0
. You might need to pass an explicitdefaultValue
togetDouble()
or test if the BundlecontainsKey("STRING_I_NEED")
. - If you want a float value, consider calling
getFloat()
instead ofgetDouble()
. I think that will convert a double value to float for you.