这是我的代码还是 Firebase 代码导致了这次泄漏?

Is this my code or Firebase code causing this leak?

我创建的应用程序中出现大量内存泄漏。我创建了一个非常简单的应用程序来重现该问题。此应用程序仅引用 FirebaseDatabase 并设置一个 ChildEventListener。当用户单击该按钮时,它会向数据库添加一条记录,并启动一个新的 activity,它执行 System.gc().

多次按下按钮将导致 Leak Canary 生成转储。

MainActivity.java:

public class MainActivity extends AppCompatActivity {

private FirebaseDatabase firebaseDatabase;
private DatabaseReference dbRef;

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


    firebaseDatabase = FirebaseDatabase.getInstance();
    dbRef = firebaseDatabase.getReference("leak");
    dbRef.addChildEventListener(new ChildEventListener() {
        @Override
        public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
        }

        @Override
        public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
        }

        @Override
        public void onChildRemoved(DataSnapshot dataSnapshot) {

        }

        @Override
        public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {
        }
    });

    findViewById(R.id.btn_leak).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            dbRef.child(UUID.randomUUID().toString()).setValue("Yes");
            Intent leakIntent = new Intent(getApplicationContext(), LeakActivity.class);
            startActivity(leakIntent);
        }
    });

}

}

LeakActivity.java:

public class LeakActivity extends AppCompatActivity {

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

        System.gc();
    }
}

由于post的限制,泄漏金丝雀日志为here.

我是不是在我的代码中做错了什么,或者这与 Firebase 有关?

编辑: @qbix 的回答似乎有效。对于其他人,这里是 MainActivity.java 的工作版本:

public class MainActivity extends AppCompatActivity {

private FirebaseDatabase firebaseDatabase;
private DatabaseReference dbRef;
private ChildEventListener dbListener;

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


    firebaseDatabase = FirebaseDatabase.getInstance();
    dbRef = firebaseDatabase.getReference("leak");
    dbListener = getDbListener();
    dbRef.addChildEventListener(dbListener);

    findViewById(R.id.btn_leak).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            dbRef.child(UUID.randomUUID().toString()).setValue("Yes");
            Intent leakIntent = new Intent(getApplicationContext(), LeakActivity.class);
            startActivity(leakIntent);
        }
    });
}

@Override
protected void onStop() {
    dbRef.removeEventListener(dbListener);
    super.onStop();
}

private ChildEventListener getDbListener(){
    return new ChildEventListener() {
        @Override
        public void onChildAdded(DataSnapshot dataSnapshot, String s) {

        }

        @Override
        public void onChildChanged(DataSnapshot dataSnapshot, String s) {

        }

        @Override
        public void onChildRemoved(DataSnapshot dataSnapshot) {

        }

        @Override
        public void onChildMoved(DataSnapshot dataSnapshot, String s) {

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    };
}

}

我没有使用过 LeakCanary,所以这只是一个有根据的猜测。

ChildEventListeners 需要 unregistered when they are no longer needed. Often listeners are added and removed in activity lifecycle methods, such as onCreate() and onDestroy(). Instead of creating an anonymous listener, create an object of that type and remove it using Query.removeEventListener() 不再需要时查看是否消除泄漏报告。

我认为 add/remove 分别在 onStart()/onStop() 或 onCreate()/onDestroy() 回调中的监听器更好。

如果在 onCreate() 中添加侦听器并在 onStop() 中删除侦听器,则可能会出现 activity 将在不调用 onCreate() 而是调用 onStart() 的情况下恢复的情况,并且不会设置监听器。

https://developer.android.com/images/activity_lifecycle.png