使用 Otto 的内存泄漏 Leakcanary 报告
Leakcanary report of memory leak using Otto
跟进,这是我无法摆脱的第二次内存泄漏..
我读到我需要根据 Activity 和 Fragment 生命周期注册和注销我的静态 Otto 总线,所以我添加了对 onStop 和 onStart 的注册和注销调用...有一个触发按钮然后由 Viewpager 中的某些片段接收到的事件给我以下内存泄漏:
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
我的代码..
主要Activity
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;
}
}
我的片段
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) {
}
}
布局,以防万一.. 主要Activity:
<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>
您在错误的时间使用观看参考。仅当您 绝对 确定 thing
将被垃圾收集时,才应调用 watch(thing)
。对于您的 Activity 和 Fragments,您需要这样的东西:
@Override public void onDestroy() {
super.onDestroy();
RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
refWatcher.watch(this);
}
来自 LeakCanary FAQ "How do I use it?" section
跟进
我读到我需要根据 Activity 和 Fragment 生命周期注册和注销我的静态 Otto 总线,所以我添加了对 onStop 和 onStart 的注册和注销调用...有一个触发按钮然后由 Viewpager 中的某些片段接收到的事件给我以下内存泄漏:
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
我的代码..
主要Activity
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;
}
}
我的片段
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) {
}
}
布局,以防万一.. 主要Activity:
<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>
您在错误的时间使用观看参考。仅当您 绝对 确定 thing
将被垃圾收集时,才应调用 watch(thing)
。对于您的 Activity 和 Fragments,您需要这样的东西:
@Override public void onDestroy() {
super.onDestroy();
RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
refWatcher.watch(this);
}
来自 LeakCanary FAQ "How do I use it?" section