У меня есть FragmentA. Когда я нажимаю кнопку в FragmentA, я иду в FragmentB. В FragmentB у меня есть PopupWindow. В PopupWindow есть ViewPager с двумя страницами.Утечка памяти из-за PopupWindow
Я взял справку из этого кода - Emojicon
У меня есть 2 отдельные классы, View1 и View2, для взглядов на странице 1 и 2-ViewPager соответственно. Оба этих класса, View1 и View2, расширяют класс ViewBase родительского класса.
Вот моя проблема:
Сценарий 1: Когда я в Fragmenta график памяти показывает использование 13MB. Когда я перехожу к FragmentB, не показывая PopupWindow, график памяти показывает 16 МБ, и когда я вернусь к FragmentA, он снизится до 13 МБ. Это хорошо.
Сценарий 2: Когда я нахожусь в FragmentA, график памяти показывает использование 13 МБ. Когда я перехожу к FragmentB с показом PopupWindow, график памяти показывает 20 МБ, и когда я вернусь к FragmentA, он не дойдет до 13 МБ.
Я пробовал Eclipse MAT и кучу кучи, чтобы узнать проблему, но по-прежнему не помогаю. Я вижу в MAT, что FragmentB все еще находится в памяти, когда я возвращаюсь к FragmentA, содержащему экземпляры PopupWindow, View1 и View2. Ни один из них не выпущен. FragmentB не должен быть в памяти.
Пожалуйста, помогите мне.
Вот мой DemoPopupWindow.java
public class DemoPopupWindow extends PopupWindow {
// Views
private TabLayout mTabLayout;
private CustomViewPager mViewPager;
private PagerAdapter mViewPagerAdapter;
private RelativeLayout mLayout;
private View mRootView;
// Variables
private int mGreyColor, mPrimaryColor;
private OnSoftKeyboardOpenCloseListener onSoftKeyboardOpenCloseListener;
private int keyBoardHeight = 0;
private Boolean pendingOpen = false;
private Boolean isOpened = false;
private Context mContext;
ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
mRootView.getWindowVisibleDisplayFrame(r);
int screenHeight = mRootView.getRootView().getHeight();
int heightDifference = screenHeight - (r.bottom);
if (heightDifference > 100) {
keyBoardHeight = heightDifference;
setSize(WindowManager.LayoutParams.MATCH_PARENT, keyBoardHeight);
if (isOpened == false) {
if (onSoftKeyboardOpenCloseListener != null)
onSoftKeyboardOpenCloseListener.onKeyboardOpen(keyBoardHeight);
}
isOpened = true;
if (pendingOpen) {
showAtBottom();
pendingOpen = false;
}
} else {
isOpened = false;
if (onSoftKeyboardOpenCloseListener != null)
onSoftKeyboardOpenCloseListener.onKeyboardClose();
}
}
};
/**
* Constructor
* @param rootView
* @param mContext
*/
public DemoPopupWindow(View rootView, Context mContext){
super(mContext);
this.mContext = mContext;
this.mRootView = rootView;
Resources resources = mContext.getResources();
View customView = createCustomView(resources);
setContentView(customView);
setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
setSize((int) mContext.getResources().getDimension(R.dimen.keyboard_height), WindowManager.LayoutParams.MATCH_PARENT);
}
/**
* Set keyboard close listener
* @param listener
*/
public void setOnSoftKeyboardOpenCloseListener(OnSoftKeyboardOpenCloseListener listener){
this.onSoftKeyboardOpenCloseListener = listener;
}
/**
* Show PopupWindow
*/
public void showAtBottom(){
showAtLocation(mRootView, Gravity.BOTTOM, 0, 0);
}
/**
* Show PopupWindow at bottom
*/
public void showAtBottomPending(){
if(isKeyBoardOpen())
showAtBottom();
else
pendingOpen = true;
}
/**
* Check whether keyboard is open or not
* @return
*/
public Boolean isKeyBoardOpen(){
return isOpened;
}
/**
* Set soft keyboard size
*/
public void setSizeForSoftKeyboard(){
mRootView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
}
/**
* Remove global layout listener
*/
public void removeGlobalListener() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
mRootView.getViewTreeObserver().removeGlobalOnLayoutListener(mGlobalLayoutListener);
} else {
mRootView.getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
}
}
/**
* Set PopupWindow size
* @param width
* @param height
*/
public void setSize(int width, int height){
keyBoardHeight = height;
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, keyBoardHeight);
mLayout.setLayoutParams(params);
setWidth(width);
setHeight(height);
}
/**
* Create PopupWindow View
* @return
*/
private View createCustomView(Resources resources) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.popup, null, false);
mViewPager = (CustomViewPager) view.findViewById(R.id.pager);
mLayout = (RelativeLayout) view.findViewById(R.id.layout);
mViewPagerAdapter = new ViewPagerAdapter(
Arrays.asList(
new View1(mContext, this),
new View2(mContext, this)
)
);
mViewPager.setAdapter(mViewPagerAdapter);
mPrimaryColor = resources.getColor(R.color.color_primary);
mGreyColor = resources.getColor(R.color.grey_color);
mTabLayout = (TabLayout) view.findViewById(R.id.tabs);
mTabLayout.addTab(mTabLayout.newTab());
mTabLayout.addTab(mTabLayout.newTab());
mTabLayout.setupWithViewPager(mViewPager);
return view;
}
/**
* ViewPager Adapter
*/
private static class ViewPagerAdapter extends PagerAdapter {
private List<ViewBase> views;
public ViewPagerAdapter(List<ViewBase> views) {
super();
this.views = views;
}
@Override
public int getCount() {
return views.size();
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View v = views.get(position).mRootView;
((ViewPager)container).addView(v, 0);
return v;
}
@Override
public void destroyItem(ViewGroup container, int position, Object view) {
((ViewPager)container).removeView((View)view);
}
@Override
public boolean isViewFromObject(View view, Object key) {
return key == view;
}
}
/**
* Soft keyboard open close listener
*/
public interface OnSoftKeyboardOpenCloseListener{
void onKeyboardOpen(int keyBoardHeight);
void onKeyboardClose();
}
}
Пожалуйста, обратите внимание, что я не вставил полный класс PopupWindow здесь, но только необходимая часть.
Вот как я использую этот DemoPopupWindow в моем FragmentB
mPopupWindow = new DemoPopupWindow(mLayout, getActivity());
mPopupWindow.setSizeForSoftKeyboard();
// If the text keyboard closes, also dismiss the PopupWindow
mPopupWindow.setOnSoftKeyboardOpenCloseListener(new DemoPopupWindow.OnSoftKeyboardOpenCloseListener() {
@Override
public void onKeyboardOpen(int keyBoardHeight) {
}
@Override
public void onKeyboardClose() {
if (mPopupWindow.isShowing())
mPopupWindow.dismiss();
}
});
В FragmentB OnDestroy я называю этот метод, чтобы удалить GlobalLayoutListener
mPopupWindow.removeGlobalListener();
У меня есть кнопка в FragmentB к показать и закрыть PopupWindow.
Вот мой ViewBase.java
public class ViewBase {
public View mRootView;
DemoPopupWindow mPopup;
private Context mContext;
public ViewBase (Context context, DemoPopupWindow popup) {
mContext = context;
mPopup = popup;
}
public ViewBase() {
}
}
Вот мой View1
public class View1 extends ViewBase{
// Views
public View mRootView;
DemoPopupWindow mPopup;
private LinearLayout mLayoutText;
// Variables
private Context mContext;
private List<String> mText;
/**
* Constructor
*/
public View1(Context context, DemoPopupWindow popup) {
super(context, popup);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
mPopup = popup;
mRootView = inflater.inflate(R.layout.fragment_view1, null);
mContext = context;
// Set parent class rootview
super.mRootView = mRootView;
registerViews(mRootView);
registerListeners();
populateText();
}
/**
* Register all the views
* @param view
*/
private void registerViews(View view) {
mLayoutText = (LinearLayout) view.findViewById(R.id.view1_layout);
mText = TextManager.getInstance().getText();
}
/**
* Populate text
*/
private void populateText() {
int length = mText.size();
for(int i=0; i<length; i++) {
addNewText(mText.get(i).getText());
}
}
/**
* Add new text
* @param text
*/
private void addNewText(final String text) {
TextView textView = createTextView(text);
mLayoutText.addView(textView);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Do something
}
});
}
/**
* Create textview
* @param text
* @return
*/
private TextView createTextView(final String text) {
TextView textView = new TextView(mContext);
FlowLayout.LayoutParams params = new FlowLayout.LayoutParams(FlowLayout.LayoutParams.WRAP_CONTENT, 40);
params.setMargins(4, 4, 0, 0);
textView.setLayoutParams(params);
textView.setClickable(true);
textView.setGravity(Gravity.CENTER);
textView.setPadding(10, 0, 10, 0);
textView.setText(text);
textView.setTextSize(20);
return textView;
}
}
EDIT СНОВА:
Я нашел этот вопрос, но я не знаю, как это исправить. Проблема заключается в mGlobalLayoutListener. Это удерживает ссылку на какое-то представление. Если я вообще не использую GlobalLayoutListener, экземпляр FragmentB удаляется из памяти.
Даже после вызова removeGlobalLayout() этот прослушиватель не освобождается.Пожалуйста, помогите мне.
привет, вы можете указать код PopupWindow? спасибо – jeorfevre
Пожалуйста, отредактируйте свой вопрос с частями вашего кода. Закрываете ли вы всплывающее окно, а вы вызываете его? – Rohan
Да, у вас есть утечка памяти. Чтобы узнать, где нам нужен код. Также вы можете использовать LeakCanary. – BladeCoder