Контекст недоступен во фрагменте.onStart() метод
Я использую MPAndroidCharts внутри фрагмента, и это позволяет мне создавать необходимые таблицы, которые я хочу отобразить. Часть библиотеки MPAndroidCharts требует следующего для инициализации диаграмм, у меня есть этот код в моем фрагменте .onStart()
метод:
barColors.add(ContextCompat.getColor(getActivity().getApplicationContext(),R.color.bar_one));
barColors.add(ContextCompat.getColor(getActivity().getApplicationContext(), R.color.bar_two));
barColors.add(ContextCompat.getColor(getActivity().getApplicationContext(), R.color.bar_three));
barColors.add(ContextCompat.getColor(getActivity().getApplicationContext(), R.color.bar_four));
barColors.add(ContextCompat.getColor(getActivity().getApplicationContext(), R.color.bar_five));
Время от времени я получаю сообщение об ошибке ниже, и я не уверен, почему, это происходит не каждый раз, когда я открываю Действие, в котором размещен Фрагмент, а время от времени. Я считаю, что контекст фрагмента еще не активен, но я не могу подтвердить:
java.lang.NullPointerException: попытка вызвать виртуальный метод 'android.content.Context android.content.ContextWrapper.getApplicationContext()' для ссылки на нулевой объект в com.troychuinard.livevotingudacity.Fragment.PollFragment.updatePollRragment.jpg) в com.troychuinard.livevotingudacity.Fragment.PollFragment.access$1300(PollFragment.java:63) в com.troychuinard.livevotingudacity.Fragment.PollFragment$3.onDataChange(PollFragment.java:304) в com.google.firebase.obfuscated.zzap.zza(com.google.firebase:firebase-database@@16.0.2:75) на com.google.firebase.database.obfuscated.zzca.zza(com.google.firebase:firebase-database@@16.0.2:63) на com.google.firebase.database.obfuscated.zzcd$1.run(com.google.firebase:firebase-database@@16.0.2:55) на android.os.Handler.handleCallback(Handler.java:751) на android.os.Handler.dispatchMessage(Handler.java:95) на android.os.Looper.loop(Looper.java:154) на android.app.ActivityThread.main(ActivityThread.java:6119) на java.lang.reflect.Method.invoke(собственный метод) на com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) на com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
ОБНОВЛЕНИЕ: Пожалуйста, смотрите ниже код соответствующего фрагмента:
@Override
public void onStart() {
super.onStart();
valueEventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
int numberOfPollAnswersAtIndexBelowDate = (int) dataSnapshot.child(ANSWERS_LABEL).getChildrenCount();
updatePollResultAnswersDynamically(numberOfPollAnswersAtIndexBelowDate, dataSnapshot);
//Simply displaying, no need for mutable transaction
int voteCountTotal = 0;
ArrayList<Long> pollAnswersTotals = new ArrayList<Long>();
for (int x = 0; x < numberOfPollAnswersAtIndexBelowDate; x++) {
pollAnswersTotals.add(x, (Long) dataSnapshot.child(ANSWERS_LABEL).child(String.valueOf(x + 1)).child(VOTE_COUNT_LABEL).getValue());
voteCountTotal += pollAnswersTotals.get(x);
}
DecimalFormat formatter = new DecimalFormat("#,###,###");
String totalFormattedVotes = formatter.format(voteCountTotal);
mTotalVoteCounter.setText(getResources().getString(R.string.votes) + String.valueOf(totalFormattedVotes));
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
};
mSelectedPollRef.addValueEventListener(valueEventListener);
}
Метод:
private void updatePollResultAnswersDynamically(int numberOfAnswers, DataSnapshot dataSnapshot) {
// mPollResults.clearValues();
pollResultChartValues = new ArrayList<BarEntry>();
for (int i = 0; i < numberOfAnswers; i++) {
Long chartLongResultvalue = (Long) dataSnapshot.child(ANSWERS_LABEL).child(String.valueOf(numberOfAnswers - i)).child(VOTE_COUNT_LABEL).getValue();
float chartFloatResultvalue = chartLongResultvalue.floatValue();
pollResultChartValues.add(new BarEntry(chartFloatResultvalue, i));
}
data = new BarDataSet(pollResultChartValues, "Poll Results");
// data.setColors(new int[]{getResources().getColor(R.color.black)});
//TODO: Check attachment to Activity; when adding a color, the getResources.getColor states
//TODO: that the fragment was detached from the activity; potentially add this method to onCreateView() to avoid;
// ArrayList<Integer> barColors = new ArrayList<>();
// barColors.add(R.color.bar_one);
// barColors.add(R.color.bar_two);
// barColors.add(R.color.bar_three);
// barColors.add(R.color.bar_four);
// barColors.add(R.color.bar_five);
mBarColors.clear();
mBarColors.add(ContextCompat.getColor(getActivity().getApplicationContext(),R.color.bar_one));
mBarColors.add(ContextCompat.getColor(getActivity().getApplicationContext(), R.color.bar_two));
mBarColors.add(ContextCompat.getColor(getActivity().getApplicationContext(), R.color.bar_three));
mBarColors.add(ContextCompat.getColor(getActivity().getApplicationContext(), R.color.bar_four));
mBarColors.add(ContextCompat.getColor(getActivity().getApplicationContext(), R.color.bar_five));
data.setColors(mBarColors);
data.setAxisDependency(YAxis.AxisDependency.LEFT);
MyDataValueFormatter f = new MyDataValueFormatter();
data.setValueFormatter(f);
//xAxis is a inverted yAxis since the graph is horizontal
XAxis xAxis = mPollResults.getXAxis();
xAxis.setDrawGridLines(false);
xAxis.setDrawAxisLine(false);
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
if ((getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE) {
xAxis.setTextSize(20);
} else if ((getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE) {
xAxis.setTextSize(16);
}
//yAxis is an inverted xAxis since the graph is horizontal
YAxis yAxisLeft = mPollResults.getAxisLeft();
yAxisLeft.setDrawGridLines(false);
yAxisLeft.setEnabled(false);
YAxis yAxisRight = mPollResults.getAxisRight();
yAxisRight.setDrawGridLines(false);
yAxisRight.setEnabled(false);
Legend legend = mPollResults.getLegend();
legend.setEnabled(false);
//TODO: This figure needs to be dynamic and needs to adjust based on the number of users in the application
//TODO: Or does it? Right now, it scales without it
dataSets = new ArrayList<IBarDataSet>();
dataSets.add(data);
//Poll Answer Options get added here
ArrayList<String> yVals = new ArrayList<String>();
for (int x = 0; x < numberOfAnswers; x++) {
String pollResult = (String) dataSnapshot.child(ANSWERS_LABEL).child(String.valueOf((numberOfAnswers) - x)).child("answer").getValue();
yVals.add(x, pollResult);
}
BarData testBarData = new BarData(yVals, dataSets);
//TODO: Fix all text sizes using this method
if ((getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE) {
testBarData.setValueTextSize(22);
} else if ((getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE) {
testBarData.setValueTextSize(14);
} else if ((getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_NORMAL) {
testBarData.setValueTextSize(12);
} else if ((getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_SMALL) {
testBarData.setValueTextSize(12);
}
mPollResults.getXAxis().setLabelRotationAngle(-15);
mPollResults.getXAxis().setSpaceBetweenLabels(5);
mPollResults.setTouchEnabled(false);
mPollResults.setPinchZoom(false);
mPollResults.setData(testBarData);
data.notifyDataSetChanged();
mPollResults.notifyDataSetChanged();
mPollResults.invalidate();
}
2 ответа
Вы должны переместить всю контекстную инициализацию в onAttach(Context context)
метод. Фрагменты должны быть прикреплены к контексту, прежде чем они смогут использовать getActivity()
метод. В противном случае этот метод вернет ноль.
Вы имеете дело с двумя разными жизненными циклами компонентов, которые не знают друг о друге:
- Жизненный цикл вашего фрагмента, который не всегда имеет прикрепленный контекст (Activity)
- Жизненный цикл вашего Firebase ValueEventListener, который привязан к чтению из вашей базы данных.
Как пишут вещи, ни ваш фрагмент, ни ваш ValueEventListener не знают о жизненном цикле другого.
Несколько способов решить эту проблему:
Удалите свой ValueEventListener в onStop
Поскольку onStart происходит после onAttach, а onStop происходит раньше, чем onDetach, вы можете удалить свой ValueEventListener в onStop. Таким образом, регистрация вашего слушателя будет симметричной. Конечно, вы пропустите DataSnapshots, пока ваш фрагмент отделен от контекста. Если все в порядке, это, вероятно, будет работать нормально.
Передайте контекст приложения вашему конструктору Fragment
Другой подход заключается в том, чтобы ваш фрагмент брал контекст в своем конструкторе и держал его в переменной-члене и ссылался на него вместо вызова getActivity()
,
ПРИМЕЧАНИЕ. Если вы передадите Context в конструкторе, обязательно удалите контекст приложения из переданного в Context и сделайте ссылку на него, чтобы избежать утечки контекста действия. Вы можете сделать это, позвонив context.getApplicationContext();