Утечка памяти на AppCompatButton - PullRequest
1 голос
/ 24 октября 2019

Я работаю над приложением, в которое интегрирован LeakCanary. На протяжении всего моего приложения я получаю уведомление LeakCanary «Приложение замерзнет, ​​Бррр ...». Теперь, если утечка постоянна, ее нельзя игнорировать. Итак, я просмотрел журналы и нашел их ниже.

├─ com.ilumi.fragments.ListFragment
│    Leaking: YES (Fragment#mFragmentManager is null)
│    ↓ ListFragment.mAddToList
├─ com.ilumi.widgets.CenteredLeftDrawableButton
│    Leaking: YES (ListFragment↑ is leaking and View.mContext references a destroyed activity)
│    mContext instance of com.ilumi.activities.DetailActivity with mDestroyed = true
│    View#mParent is set
│    View#mAttachInfo is null (view detached)
│    View.mWindowAttachCount = 1
│    ↓ CenteredLeftDrawableButton.mContext
╰→ com.ilumi.activities.DetailActivity
 ​     Leaking: YES (CenteredLeftDrawableButton↑ is leaking and Activity#mDestroyed is true and 

Теперь, судя по всему, я решил, что проблема в CenteredLeftDrawableButton, который я использовал во многих местах своего приложения.

public class CenteredLeftDrawableButton extends AppCompatButton {

    public CenteredLeftDrawableButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public CenteredLeftDrawableButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CenteredLeftDrawableButton(Context context) {
        super(context);
    }

    @Override
    public void setCompoundDrawablePadding(int pad) {
        super.setCompoundDrawablePadding(pad);
        centerDrawables();
    }

    @Override
    public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) {
        super.setCompoundDrawables(left, top, right, bottom);
        centerDrawables();
    }

    @Override
    public void setCompoundDrawablesRelativeWithIntrinsicBounds(int start, int top, int end, int bottom) {
        super.setCompoundDrawablesRelativeWithIntrinsicBounds(start, top, end, bottom);
        centerDrawables();
    }

    @Override
    public void setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable start, Drawable top, Drawable end, Drawable bottom) {
        super.setCompoundDrawablesRelativeWithIntrinsicBounds(start, top, end, bottom);
        centerDrawables();
    }

    @Override
    public void setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, Drawable right, Drawable bottom) {
        super.setCompoundDrawablesWithIntrinsicBounds(left, top, right, bottom);
        centerDrawables();
    }

    @Override
    public void setCompoundDrawablesWithIntrinsicBounds(int left, int top, int right, int bottom) {
        super.setCompoundDrawablesWithIntrinsicBounds(left, top, right, bottom);
        centerDrawables();
    }

    @Override
    public void setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable end, Drawable bottom) {
        super.setCompoundDrawablesRelative(start, top, end, bottom);
        centerDrawables();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        centerDrawables();
    }

    private void centerDrawables() {
        Drawable[] compoundDrawables = getCompoundDrawables();
        Drawable drawableLeft = compoundDrawables[0];
        Drawable drawableRight = compoundDrawables[2];
        if (drawableLeft != null || drawableRight != null) {
            float textWidth = getPaint().measureText(getText().toString());
            int drawablePadding = getCompoundDrawablePadding();
            int drawableWidth = 0;
            if (drawableLeft != null) {
                drawableWidth += drawableLeft.getIntrinsicWidth();
            }
            if (drawableRight != null) {
                drawableWidth += drawableRight.getIntrinsicWidth();
            }
            float bodyWidth = textWidth + drawableWidth + drawablePadding;
            if (bodyWidth < getWidth()) {
                float emptySpace = getWidth() - bodyWidth;
                int paddingToLeftAndRight = (int) (emptySpace / 2);
                setPadding(paddingToLeftAndRight, getPaddingTop(), paddingToLeftAndRight, getPaddingBottom());
            }
        }
    }

    public static void centerDrawables(TextView view) {
        Drawable[] compoundDrawables = view.getCompoundDrawables();
        Drawable drawableLeft = compoundDrawables[0];
        Drawable drawableRight = compoundDrawables[2];
        if (drawableLeft != null || drawableRight != null) {
            float textWidth = view.getPaint().measureText(view.getText().toString());
            int drawablePadding = view.getCompoundDrawablePadding();
            int drawableWidth = 0;
            if (drawableLeft != null) {
                drawableWidth += drawableLeft.getIntrinsicWidth();
            }
            if (drawableRight != null) {
                drawableWidth += drawableRight.getIntrinsicWidth();
            }
            float bodyWidth = textWidth + drawableWidth + drawablePadding;
            if (bodyWidth < view.getWidth()) {
                float emptySpace = view.getWidth() - bodyWidth;
                int paddingToLeftAndRight = (int) (emptySpace / 2);
                view.setPadding(paddingToLeftAndRight, view.getPaddingTop(), paddingToLeftAndRight, view.getPaddingBottom());
            }
        }
    }
}

В каждом из приведенных ниже классов используются кнопки, которые я делаю.

      private CenteredLeftDrawableButton mAddToSchedule;


@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
    mAddToSchedule = (CenteredLeftDrawableButton) view.findViewById(R.id.editScheduleButton);

mAddToSchedule.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //Intent to open new screen
            }
        });
    }

 @Override
    public void onResume() {
        super.onResume();
    if (someConditionTrue) {
                mAddToList.setText(R.string.button_add_to_schedule);
                mAddToList.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_add_circle_outline_black_24dp, 0, 0, 0);

    } else {
                mAddToList.setText(R.string.button_update_firmware);
                mAddToList.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
    }

}

Ничего тяжелого не происходит, за исключением добавления рисуемого элемента. Я не уверен, как это исправить. Если кто-то может помочь, это будет здорово.

Ответы [ 2 ]

0 голосов
/ 27 октября 2019

ListFrament в верхней части имеет «Утечка: ДА», что означает, что он не должен храниться в памяти, что означает, что проблема находится перед ним в трассировке. Таким образом, вам нужно вставить весь след, чтобы получить помощь. Найдите ссылки, где «Утечка» переходит от «нет» к «да», и вы найдете причину. Причина определенно не в выдвижной кнопке.

0 голосов
/ 24 октября 2019

Это может помочь:

Вы устанавливаете clickListener на mAddToSchedule и не удаляете его после уничтожения, поэтому он сохраняет ссылку CenteredLeftDrawableButton, которую вы можете удалить следующим образом:

@Override
    public void onDestory() {
        mAddToSchedule.setOnClickLister(null)
        super.onDestory()
}
...