这是我的代码还是 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() 的情况下恢复的情况,并且不会设置监听器。
我创建的应用程序中出现大量内存泄漏。我创建了一个非常简单的应用程序来重现该问题。此应用程序仅引用 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() 的情况下恢复的情况,并且不会设置监听器。