Android碎片内存泄漏问题

6
我会尽力为您翻译以下内容,这是一个关于IT技术的小应用程序的问题。该应用程序使用了许多碎片,但由于我对碎片比较陌生,所以我查阅了很多相关资源来使用它们,但结果好像并不如预期。随着我从一个碎片切换到另一个碎片,我的堆内存持续增加,导致内存泄漏。之前我用Activity实现应用程序时,我已经显著减少了内存泄漏。然而,现在我加入了碎片的世界,感觉重新从头开始解决内存问题。
MainActivity.java
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);

    // Setting the content view of the Activity
    setContentView(R.layout.main);

    // Setting up the actionbar.
    ActionBar actionbar = getActionBar();
    actionbar.setSubtitle("");
    actionbar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME
            | ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM);
    actionbar.setTitle("");
    actionbar = null;

    MainMenuFragment frag = new MainMenuFragment();

    getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, frag).commit();
}

这个MainActivity还设置了ActionBar。

MainMenuFragment.java

public class MainMenuFragment extends Fragment implements OnClickListener, LoaderCallbacks<Cursor> {

private static final String TAG = "MainMenuFragment";

SharedPreferences.Editor prefsEditor;
SharedPreferences prefs;

Bitmap[] picss = null;


static int width;

private static int FRIENDS = 0;
private static int PEOPLE = 1;
private static int MAP = 2;
private static int CAMERA = 3;
private static int TOP_TILE = 4;

LinearLayout topTile = null;
LinearLayout mainLayout;

private int NUM_OF_BUTTONS = 4;

ImageButton[] buttonList = null;

Bitmap background;

protected Activity activity;
protected String placeId = null;
protected ViewGroup container;
protected View view;

public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState); 
    activity = getActivity();

    view = getView();

    setupObjects(view);
    setupLayoutsAndImages(view);

    // Populate the UI by initiating the loader to retrieve the 
    // details of the venue from the underlying Place Content Provider.
    if (placeId != null)
      getLoaderManager().initLoader(0, null, this);
  }



public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.main_screen , container, false);
    this.container = container;

    return view;
}

public void onDestroyView() {
    super.onDestroyView();

    Log.d(TAG, "onDestroyView()");

    buttonList[FRIENDS].setImageBitmap(null);
    buttonList[PEOPLE].setImageBitmap(null);
    buttonList[MAP].setImageBitmap(null);
    buttonList[CAMERA].setImageBitmap(null);
    topTile.setBackground(null);

    // Removes all the onClickListeners
    buttonList[FRIENDS].setOnClickListener(null);
    buttonList[PEOPLE].setOnClickListener(null);
    buttonList[MAP].setOnClickListener(null);
    buttonList[CAMERA].setOnClickListener(null);
    topTile.setOnClickListener(null);

    // Sets buttonList and the topTile to null
     buttonList = null;
     topTile = null;

    // Recycles the bitmaps and sets picss to null
    if (picss != null) {
        picss[FRIENDS].recycle();
        picss[PEOPLE].recycle();
        picss[MAP].recycle();
        picss[CAMERA].recycle();
        picss = null;
    }

    mainLayout.setBackground(null);
    background.recycle();

    unbindDrawables(view);
    view = null;
    activity = null;
    System.gc();
}

private void unbindDrawables(View view) {
    if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
    }
    if (view instanceof ViewGroup && !(view instanceof AdapterView)) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
        }
        ((ViewGroup) view).removeAllViews();
    }
}


public void onClick(View v) {
    Log.d(TAG, "Got within here.");
    switch (v.getId()) {
    case R.id.b_friendButton:

        FragmentTransaction trans = getActivity().getSupportFragmentManager().beginTransaction();
        trans.replace(R.id.fragment_container, new FriendsListFrag());
        trans.addToBackStack(null);
        trans.commit();
        break;
    case R.id.b_peopleButton:
        break;
    case R.id.b_mapButton:
        break;
    case R.id.b_CameraButton:
        break;
    case R.id.ll_Dynamic_Tile:
        break;
    }

}

private void setupObjects(View view) {

    Log.d(TAG, "Attempting to setupObjects.");

    mainLayout = (LinearLayout) view.findViewById(R.id.MainActivity_Main_Layout);
    topTile = (LinearLayout) view.findViewById(R.id.ll_Dynamic_Tile);

    buttonList = new ImageButton[NUM_OF_BUTTONS];
    buttonList[FRIENDS] = (ImageButton) view.findViewById(R.id.b_friendButton);
    buttonList[PEOPLE] = (ImageButton) view.findViewById(R.id.b_peopleButton);
    buttonList[MAP] = (ImageButton) view.findViewById(R.id.b_mapButton);
    buttonList[CAMERA] = (ImageButton) view.findViewById(R.id.b_CameraButton);

    picss = new Bitmap[5];

    buttonList[FRIENDS].setOnClickListener(this);
    buttonList[PEOPLE].setOnClickListener(this);
    buttonList[MAP].setOnClickListener(this);
    buttonList[CAMERA].setOnClickListener(this);

    int width = getActivity().getWindowManager().getDefaultDisplay().getWidth();
    int height = getActivity().getWindowManager().getDefaultDisplay().getHeight();

    background = GetImage.decodeSampledBitmapFromResource(getResources(),
            R.drawable.background2, width, height);

    System.out.println(mainLayout == null);

    mainLayout.setBackground(new BitmapDrawable(getResources(), background));

}

private void setupLayoutsAndImages(View view) {

    Log.d(TAG, "Attempting to setupLayoutsAndImages.");

    System.out.println("Width: " + width);
    final ImageButton myButton = (ImageButton) getView().findViewById(R.id.b_friendButton);
    getView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        public void onGlobalLayout() {
            System.out.println("Fragwidth: " + getView().getWidth());
            System.out.println("FragHeight: " + getView().getHeight());
            System.out.println("Button: " + myButton.getWidth());
            width = myButton.getWidth();

            System.out.println("Width: " + width);

            getView().getViewTreeObserver().removeGlobalOnLayoutListener(this);

            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                    width, width, 1.0f);
            int marg = (int) getResources().getDimension(
                    R.dimen.main_buttonpadding);
            params.setMargins(marg, marg, marg, marg);

            // Setting the params for the buttons.
            buttonList[FRIENDS].setLayoutParams(params);
            buttonList[PEOPLE].setLayoutParams(params);
            buttonList[MAP].setLayoutParams(params);
            buttonList[CAMERA].setLayoutParams(params);

            picss[FRIENDS] = GetImage.decodeSampledBitmapFromResource(
                    getResources(), R.drawable.tilefriendsmall, width, width);
            picss[PEOPLE] = GetImage.decodeSampledBitmapFromResource(
                    getResources(), R.drawable.tilepeoplesmall, width, width);
            picss[MAP] = GetImage.decodeSampledBitmapFromResource(
                    getResources(), R.drawable.tilemapsmall, width, width);
            picss[CAMERA] = GetImage.decodeSampledBitmapFromResource(
                    getResources(), R.drawable.tilecamerasmall, width, width);
            picss[TOP_TILE] = GetImage.decodeSampledBitmapFromResource(
                    getResources(), R.drawable.tilefriendlong, width, width);

            // Setting up the background for the buttons.
            buttonList[FRIENDS].setImageBitmap(picss[FRIENDS]);
            buttonList[FRIENDS].setAlpha(0.6f);
            buttonList[PEOPLE].setImageBitmap(picss[PEOPLE]);
            buttonList[PEOPLE].setAlpha(0.6f);
            buttonList[MAP].setImageBitmap(picss[MAP]);
            buttonList[MAP].setAlpha(0.6f);
            buttonList[CAMERA].setImageBitmap(picss[CAMERA]);
            buttonList[CAMERA].setAlpha(0.6f);

            LinearLayout.LayoutParams topParams = new LinearLayout.LayoutParams(
                    topTile.getWidth(), width);
            topParams.setMargins(marg, marg, marg, marg);
            topTile.setLayoutParams(topParams);
            topTile.setBackground(new BitmapDrawable(getResources(),
                    picss[TOP_TILE]));
            topTile.setAlpha(0.6f);
        }
    });

}

在上面的代码中,当我切换到FriendsListFrag()时,会保留先前为MainMenuFragment分配的内存。我尝试使用Memory Analyzer,但没有什么效果。

2个回答

8
您一直在创建(并附加)新的片段实例。
请改为:
 MainMenuFragment frag = new MainMenuFragment();

 getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, frag).commit();

请将其翻译为类似以下内容:

Fragment fragment = getSupportFragmentManager().
                   findFragmentById(R.id.fragment_container);
    if ( fragment == null ) {
        fragment = new MainMenuFragment();
            getSupportFragmentManager()
            .beginTransaction()
            .add(R.id.fragment_container, fragment, "SOME_TAG_IF_YOU_WANT_TO_REFERENCE_YOUR_FRAGMENT_LATER")
            .commit();
    } 

FragmentManager会保留Fragment的引用并自行处理。您可以使用FragmentManager中的.detach()方法在调用onSaveInstance()之前删除它们。

您可以这样做:

Fragment f = getSupportFragmentManager().findFragmentByTag("SOME_TAG_IF_YOU_WANT_TO_REFERENCE_YOUR_FRAGMENT_LATER");

if ( f != null ) {
   getSupportFragmentManager().detach(f);
}  

注意:以下所有代码都是伪代码,我没有测试过它,可能存在拼写错误,但是你能理解我的意思。


好的,你的意思是说每次我打开一个新的碎片(fragment)时它只是保留了旧的那个?那么在 onDestryoView() 中,我仍然需要将对活动(activity)的所有引用进行反引用(de-reference),对吗? - Jake Alsemgeest
我尝试了你的建议,但似乎我仍在使用相同数量的内存。当切换片段时,它从10023开始跳到18123...这并不太合理,因为第二个片段在对象和图像方面远不如第一个。 - Jake Alsemgeest
2
现在你已经正确处理了Fragments。下一步可能是优化位图加载。你可以尝试使用Picasso(库)来处理,因为它将处理位图的生命周期,并确保不会泄漏内存。 - Martin Marconcini
好的,听起来不错。我看了一下Picasso库。它在片段中能正常工作吗? - Jake Alsemgeest
是的,它是由写过SherlockLibrary和其他知名Android库的同一人编写和维护的(或者至少他在其中工作)。 - Martin Marconcini
显示剩余2条评论

0

很可能是你的OnGlobalLayout监听器。我有同样的问题,即使我在onDestroy中取消注册并将其设置为null,它仍然会泄漏片段。

尝试不实现监听器的片段,并检查是否仍然泄漏。

然而,不幸的是,我还没有找到解决方法或替代方案...


我同意这可能是问题所在。请参考https://dev59.com/OV4c5IYBdhLWcg3wJnb8 - ban-geoengineering

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接