使用Otto的Leakcanary内存泄漏报告

4

跟进我的上一个问题,这是我无法摆脱的第二个内存泄漏问题。

我了解到需要根据Activity和Fragment的生命周期注册和取消注册静态Otto总线,因此我在onStop和onStart中添加了注册和取消注册调用...在一个ViewPager中有一个触发事件的按钮,然后一些Fragments接收该事件导致以下内存泄漏:

D/LeakCanary﹕ In com.doesnthaveadomain.leo.calendartracker:1.0:1.
D/LeakCanary﹕ * com.doesnthaveadomain.leo.calendartracker.MyFragment has leaked:
D/LeakCanary﹕ * GC ROOT static com.doesnthaveadomain.leo.calendartracker.MyBus.BUS
D/LeakCanary﹕ * references com.squareup.otto.Bus.handlersByType
D/LeakCanary﹕ * references java.util.concurrent.ConcurrentHashMap.table
D/LeakCanary﹕ * references array java.util.concurrent.ConcurrentHashMap$Node[].[3]
D/LeakCanary﹕ * references java.util.concurrent.ConcurrentHashMap$Node.val
D/LeakCanary﹕ * references java.util.concurrent.CopyOnWriteArraySet.al
D/LeakCanary﹕ * references java.util.concurrent.CopyOnWriteArrayList.elements
D/LeakCanary﹕ * references array java.lang.Object[].[0]
D/LeakCanary﹕ * references com.squareup.otto.EventHandler.target
D/LeakCanary﹕ * leaks com.doesnthaveadomain.leo.calendartracker.MyFragment instance

我的代码..

MainActivity 主活动

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

import java.lang.ref.WeakReference;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ViewPager mViewPager = (ViewPager) findViewById(R.id.viewpager);
        MyAdapter myAdapter = new MyAdapter(getSupportFragmentManager(), this);
        mViewPager.setAdapter(myAdapter);
        mViewPager.setCurrentItem(5);

        Button button = (Button) findViewById(R.id.do_something);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MyBus.getInstance().post(new SomeEvent("Something"));
            }
        });
    }

    static class SomeEvent {
        String mString;
        SomeEvent(String string) {
            mString = string;
        }
        public String getString() {
            return mString;
        }
    }

}

class MyAdapter extends FragmentStatePagerAdapter {

    private int mNumberOfViews;
    private final WeakReference<AppCompatActivity> mActivityWeakRef;

    public MyAdapter(FragmentManager fm,
                     AppCompatActivity activity) {
        super(fm);

        mActivityWeakRef = new WeakReference<AppCompatActivity>(activity);
        mNumberOfViews = 10;
    }

    @Override
    public Fragment getItem(int position) {
        MyFragment myFragment = new MyFragment();
        AppCompatActivity activity = mActivityWeakRef.get();
        if (activity != null) {
            MyApp.getRefWatcher(mActivityWeakRef.get()).watch(myFragment);
        }
        return myFragment;
    }

    @Override
    public int getCount() {
        return mNumberOfViews;
    }
}

MyFragment

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.squareup.otto.Subscribe;

/**
 * A placeholder fragment containing a simple view.
 */
public class MyFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_test, container, false);
    }

    @Override
    public void onStart() {
        super.onStart();

        MyBus.getInstance().register(this);
    }

    @Override
    public void onStop() {
        super.onStop();

        MyBus.getInstance().unregister(this);
    }

    @Subscribe
    public void onEvent(MainActivity.SomeEvent event) {

    }

}

布局,以防万一.. MainActivity:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <android.support.design.widget.AppBarLayout
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/toolbar_appbar_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:fitsSystemWindows="true" >

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:theme="@style/AppTheme.Toolbar"
            app:popupTheme="@style/AppTheme.Toolbar.Popup">

        </android.support.v7.widget.Toolbar>

    </android.support.design.widget.AppBarLayout>

    <Button
        android:id="@+id/do_something"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="DO"/>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

片段:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingLeft="@dimen/activity_vertical_margin"
    android:paddingStart="@dimen/activity_vertical_margin"
    android:paddingRight="@dimen/activity_vertical_margin"
    android:paddingEnd="@dimen/activity_vertical_margin"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">

</LinearLayout>
1个回答

9
您在错误的时间观看参考文献。只有在您绝对确定thing将被垃圾收集时,才应调用watch(thing)。对于您的Activities和Fragments,您需要像这样的东西:
@Override public void onDestroy() {
    super.onDestroy();
    RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
    refWatcher.watch(this);
}

那是来自于 LeakCanary 的常见问题解答 "如何使用它?"部分

非常感谢,这似乎解决了问题!如果我没有花足够的时间仔细阅读,学习库时应该慢慢来。我以为引用监视器的工作方式与活动相同。 - sickmartian
新链接 我该如何使用它? - Deni Erdyneev

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