【问题标题】:Android Fragment Memory LeaksAndroid 片段内存泄漏
【发布时间】:2013-11-30 03:21:58
【问题描述】:

我在业余时间制作一个小应用程序,我正在使用许多片段。我对片段相当陌生,但是我已经使用了许多资源来适当地使用……或者我是这么认为的。当我从一个片段更改为另一个片段时,我的堆继续增长,我得到了内存泄漏。我之前使用活动实现了我的应用程序,并且显着减少了内存泄漏。然而,现在我加入了碎片世界,我觉得在记忆方面我好像从一开始就开始了。

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 分配的内存。我曾尝试使用内存分析器,但运气不佳。

【问题讨论】:

    标签: android memory android-fragments


    【解决方案1】:

    您不断创建(并附加)片段的新实例。

    改变这个:

     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 的引用并自行处理。如果需要(在调用 onSaveInstance() 之前),您可以使用 FragmentManager 中的 .detach() 方法删除它们。

    你可以这样做:

    Fragment f = getSupportFragmentManager().findFragmentByTag("SOME_TAG_IF_YOU_WANT_TO_REFERENCE_YOUR_FRAGMENT_LATER");
    
    if ( f != null ) {
       getSupportFragmentManager().detach(f);
    }  
    

    注意:这都是伪代码,我没有测试,可能有错别字,但你明白了

    【讨论】:

    • 哦,好吧,所以你是说每次我打开一个新片段时都会保留旧片段?我仍然需要取消对片段 onDestryoView() 上活动的引用,对吧?
    • 我尝试了您的建议,看来我仍在使用相同数量的内存。它从 10023 开始,在切换片段时跳到 18123……这没有多大意义,因为第二个在对象和图像方面几乎没有那么多。
    • 现在你已经正确处理了片段。下一步可能是优化位图加载。您可能想为此尝试 Picasso(库),因为它将处理位图生命周期并确保您不会泄漏内存。
    • 好的,听起来不错。我看了毕加索图书馆。它在片段中是否正常工作?
    • 是的。它是由编写 SherlockLibrary 和其他已知 Android 库的同一个人编写和维护的。 (或者至少他在努力)。
    【解决方案2】:

    这可能是您的 OnGlobalLayout 侦听器。我有同样的问题,即使我在 onDestroy 中取消注册并将其设置为 null,它也会以某种方式泄漏片段。

    在没有实现监听器的情况下尝试片段并检查它是否仍然泄漏。

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

    【讨论】:

    猜你喜欢
    • 2019-12-22
    • 1970-01-01
    • 1970-01-01
    • 2015-07-20
    • 2018-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多