View.getViewTreeObserver(). addOnGlobalLayoutListener утечки Фрагмент
Когда я использую GlobalLayoutListener
, чтобы узнать, открыт ли экран softKeyboard или нет, фрагмент больше не будет уничтожен garbageCollected после его уничтожения.
Что я делаю:
- Я удаляю Listener в
onDestroy()
моего фрагмента
- Я установил Listener в
null
в onDestroy()
- Я установил представление, которое наблюдается в null в
onDestroy()
Все еще просачивается фрагмент.
Есть ли у кого-то подобная проблема и знает, как это исправить?
Мой onDestroy
:
@Override
public void onDestroy(){
Log.d(TAG , "onDestroy");
if(Build.VERSION.SDK_INT < 16){
view.getViewTreeObserver().removeGlobalOnLayoutListener(gLayoutListener);
}else{
view.getViewTreeObserver().removeOnGlobalLayoutListener(gLayoutListener);
}
view = null;
gLayoutListener = null;
super.onDestroy();
}
Ответы
Ответ 1
Я считаю, что сильно удалить Listener, на который ссылается объект View, в onDestroy
() слишком поздно. Этот метод переопределения происходит после onDestroyView
(), который должен "... очищать ресурсы, связанные с его представлением".
Вместо этого вы можете использовать тот же код в onStop()
. Хотя я не использовал эту технику.
Я могу предложить этот код, который я использовал без каких-либо проблем с отладчиком.
// Code below is an example. Please change it to code that is more applicable to your app.
final View myView = rootView.findViewById(R.id.myView);
myView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@SuppressLint("NewApi") @SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {
// Obtain layout data from view...
int w = myView.getWidth();
int h = myView.getHeight();
// ...etc.
// Once data has been obtained, this listener is no longer needed, so remove it...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
myView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
else {
myView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
}
});
Примечания:
- Так как
getViewTreeObserver
используется для макетов, обычно вам нужен этот прослушиватель только на короткое время. Следовательно, слушатель немедленно удаляется.
- Второй вызов
removeOnGlobalLayoutListener
() должен быть вычеркнут Studio, поскольку он недоступен до JELLY_BEAN.
- Код Pragma
@SuppressWarnings("deprecation")
не нужен, если вы используете Android Studio.
- Код
myView = rootView.findViewById(R.id.myView);
может потребоваться изменить на более подходящий код для вашего приложения или ситуации.
Ответ 2
Хорошо, может быть, это немного переборщило, но трудно сказать что-то точно без источников, поэтому попробуйте это. Сделайте свой gLayoutListener
статическим внутренним классом (чтобы не содержать ссылки на ваш фрагмент).
Если вам нужно выполнить некоторые операции с фрагментом или его поля внутри слушателя, создайте WeakReference<YourFragment>
внутри конструктора вашего пользовательского класса прослушивателя и получите доступ к фрагменту через эту ссылку. Не забудьте сделать чек weakref.get() != null
.
Ответ 3
Вместо этого вы можете попробовать создать пользовательский макет и поместить его в качестве корневого представления в свой XML файл.
class CustomLayout extends LinearLayout{
public CustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
Затем переопределите метод onsizechanged
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (h < oldh) {
// there is a difference, means keyboard is open.
} else {
}
}
Я полагаю, что ваше приложение поддерживает только один режим (портретный или альбомный)
Обновление
Сделайте все в
@Override
public void onDestroyView(){
super.onDestroyView();
}
Потому что я думаю, что вы инициализируете слушателя в onCreateView()
, поэтому слушатель должен быть удален в onDestoryView()
. onDestroy() будет вызываться только тогда, когда фрагмент будет уничтожен, а не во время изменения состояния.
Проверьте жизненный цикл фрагмента
Ответ 4
У меня была такая же проблема, но я разрешил ее, удалив слушателя в onDestroy(). Обратите внимание на способ использования, измененный в JellyBean.
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
}
@Override
public void onDestroy() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
mView.getViewTreeObserver().removeGlobalOnLayoutListener(mGlobalLayoutListener);
} else {
mView.getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
}
super.onDestroy();
}
Ответ 5
У меня также была эта проблема в пользовательском представлении. Я зарегистрировал onPreDrawListener
над дочерним представлением в конструкторе пользовательского вида и отменил его в onDetachedFromWindow
. Утечка памяти сохранилась. Чтобы решить эту проблему, я все испробовал, но в конце концов мне пришлось написать альтернативный механизм, не основанный на TreeObserver.