Is this my code or Firebase code causing this leak? - java

I am getting a lot of memory leaks in an application I created. I created a very simple app to reproduce the problem. This application just makes a reference to the FirebaseDatabase and sets up a ChildEventListener. When the user clicks the button it adds a record to the database, and starts a new activity which does System.gc().
Pressing the button multiple times will cause Leak Canary to generate a dump.
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();
}
}
Due to the post limit, the leak canary log is here.
Am I doing something wrong in my code, or is this related to Firebase?
EDIT: #qbix's answer seemed to work. For others, here is the working version of 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) {
}
};
}
}

I haven't used LeakCanary, so this is just an educated guess.
ChildEventListeners need to be 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() when no longer needed to see if that eliminates the leak report.

I think that is better to add/remove listeners within the onStart()/onStop() or the onCreate()/onDestroy() callbacks respectively.
If a listener was added in onCreate() and removed in onStop() there could occur a situation when an activity will be restored without of calling the onCreate() but with calling of onStart() and listener will not be set.
https://developer.android.com/images/activity_lifecycle.png

Related

Background firebase database scanner

I need to make background process in android studio, that will work when phone is turned off and will listen to the firebase realtime database and send notifications if the user data matches sent from another user firebase data.
I have a realtime database in which, when the "SOS" button is pressed, data is sent from which user the message goes and to whom.
That's how it looks like in Firebase console:
Firebase database {
Sos(all sos button clicks){
RandomID( like -N3AB5hwgkPO5kCqdaJf){
emailfrom(who clicked button)
emailto(user who will receive message)
time
}
}
}
i need to scan section "sos" all the time, and if emailto matches with my email, app will send notification.
I made the prototype with "extends service", but it doesn't even work, and does not perform any function.
public class SosListener extends Service {
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
String email = user.getEmail();
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
DatabaseReference dataBase = FirebaseDatabase.getInstance().getReference();
dataBase.child("Sos/").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
String emailTo = snapshot.child("emailTo").getValue(String.class);
if (emailTo == email) {
}
}
#Override
public void onChildChanged(#NonNull DataSnapshot snapshot, #Nullable String previousChildName) {
}
#Override
public void onChildRemoved(#NonNull DataSnapshot snapshot) {
}
#Override
public void onChildMoved(#NonNull DataSnapshot snapshot, #Nullable String previousChildName) {
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
}
Here is also MainActivity where I start this Service:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this, SosListener.class));
}
}
How can I make it?

notifyItemInserted() is having same effect on list as notifyDataSetChanged() when I only want last item in list updated

The problem that I am having is that when a user adds a comment the entire list refreshes because I have notifyDataSetChanged(); set on the CommentAdapter. Everything jumps, and refreshes and I want it to be smoother, so I decided to use notifyItemInserted(); instead of notifyDataSetChanged();, but it isn't doing anything different.
notifyItemInserted(); should only update the last item or the newest item added to the list, so why is everything being refreshed/updated...?
Can someone tell me how to fix this? Only want last item added to list to be "added"/"updated"/whatever, not the entire list because if many people start commenting everything is always reloading...
In my readComments(); what I have now is mCommentAdapter.notifyItemInserted(mCommentList.size() - 1);, and what I had before was mCommentAdapter.notifyDataSetChanged();, but they are having the same effect. How can I fix this?
CommentsActivity
public class CommentsActivity extends AppCompatActivity {
private CommentAdapter mCommentAdapter;
private List<Comment> mCommentList;
private RecyclerView mRecyclerView;
EditText mAddComment;
ImageView mImageProfile;
TextView mPost;
String mPostId;
String mPublisherId;
String mNotificationId;
FirebaseUser mFirebaseUser;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_comments);
mRecyclerView = findViewById(R.id.recycler_view);
mRecyclerView.setHasFixedSize(true);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(linearLayoutManager);
mCommentList = new ArrayList<>();
mCommentAdapter = new CommentAdapter(this, mCommentList, mPostId);
mRecyclerView.setAdapter(mCommentAdapter);
mAddComment = findViewById(R.id.add_comment);
mImageProfile = findViewById(R.id.image_profile);
mPost = findViewById(R.id.post_comment);
mFirebaseUser = FirebaseAuth.getInstance().getCurrentUser();
mPost.setOnClickListener(v -> {
if (mAddComment.getText().toString().equals("")) {
Toast.makeText(CommentsActivity.this, "Can't send empty comments", Toast.LENGTH_SHORT).show();
} else {
addCommentNotification(mPublisherId, mPostId);
}
});
getImage();
readComments();
}
private void getImage() {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Users").child(mFirebaseUser.getUid());
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
if (user != null)
Glide.with(getApplicationContext()).load(user.getImageurl()).into(mImageProfile);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
private void readComments() {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Comments").child(mPostId);
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
mCommentList.clear();
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Comment comment = snapshot.getValue(Comment.class);
mCommentList.add(comment);
}
mCommentAdapter = new CommentAdapter(CommentsActivity.this, mCommentList, mPostId);
mRecyclerView.setAdapter(mCommentAdapter);
mCommentAdapter.notifyItemInserted(mCommentList.size() - 1);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
}
I am guessing you should be using a ChildEventListener:
Your readComments() must be like this:
private void readComments() {
//your reference
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("Comments").child(mPostId);
//the child listener
ChildEventListener listener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
// A new comment has been added
Comment comment = dataSnapshot.getValue(Comment.class);
mCommentList.add(comment);
//Notify adapter
mCommentAdapter.notifyItemInserted(mCommentList.size() - 1);
}
#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) {
}
};
//attach the listener
ref.addChildEventListener(listener);
}

How to implement admob rewarded video ads in a list view?

I want to know how to implement AdMob rewarded video ads in a list view?
I'm using the source code from here
and I want to use it in this class StickerPackDetailsActivity.java
and the layout is gonna like this
![layout][1]
I want to lock add to WhatsApp and unlock it by watching video reward.
but this stickerdetails showed from listview from
![here][2]
so, how to implement video reward ads only in 1 specified item of the listview not all of them?
public class MainActivity extends AppCompatActivity implements RewardedVideoAdListener {
private RewardedVideoAd mRewardedVideoAd;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MobileAds.initialize(this,"ca-app-pub111111111");
mRewardedVideoAd = MobileAds.getRewardedVideoAdInstance(this);
listitem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
loadRewardedVideoAd();
}
});
}
private void loadRewardedVideoAd() {
mRewardedVideoAd.loadAd("ca-app-pub-",
new AdRequest.Builder().build());
}
#Override
public void onRewardedVideoAdLoaded() {
}
#Override
public void onRewardedVideoAdOpened() {
}
#Override
public void onRewardedVideoStarted() {
}
#Override
public void onRewardedVideoAdClosed() {
loadRewardedVideoAd();
}
#Override
public void onRewarded(RewardItem rewardItem) {
}
#Override
public void onRewardedVideoAdLeftApplication() {
}
#Override
public void onRewardedVideoAdFailedToLoad(int i) {
}
#Override
public void onRewardedVideoCompleted() {
}
#Override
protected void onPause() {
mRewardedVideoAd.pause(this);
super.onPause();
}
#Override
protected void onResume() {
mRewardedVideoAd.resume(this);
super.onResume();
}
}

add buttons to listview firebase arraylist strings android

I am new in programing
and this is my java code below
I want the simplest way to add button to each row of listview and relate it with that row firebase child (like a vote button for example) ...
should I custom simple_list_item_1 ?? or creat a new xml file .. please answer me in detail because like I said I am new in android
tnx for help
public class Main2Activity extends ListActivity {
String us , userId ;
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference();
ArrayList<String> listItems = new ArrayList<>();
ArrayAdapter<String> adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
adapter = new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,listItems);
setListAdapter(adapter);
userId = Profile.getCurrentProfile().getId() ;
us = Profile.getCurrentProfile().getName();
Toast.makeText(Main2Activity.this, "HI "+us,
Toast.LENGTH_SHORT).show();
ProfilePictureView profilePictureView;
profilePictureView = findViewById(R.id.ProfilePicture);
profilePictureView.setProfileId(userId);
ref.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
for (DataSnapshot childSnapshot: dataSnapshot.getChildren()) {
String value = childSnapshot.getValue(String.class);
listItems.add(value);
}
adapter.notifyDataSetChanged();
}
#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) {
}
});
}
}
android.R.layout.simple_list_item_1 is a reference to an built-in XML layout document that is part of the Android OS which containt only one TextView, so if you want to add another view (e.g. button) you need to create your own layout.

New bie : Why is this code not able to read anything from the firebase database

The Following is my main activity, in another activity the user can login and upload audio files to the firebase storage; after that it saves the link and the current date time in the firebase database in reverse order.
The structure of the database is Audios> Feb 26,2018> "http:// link to firebase storage"
What I am trying to achieve in this activity is to read the link from the database> Store the value in the string value> and play it on mediaplayer but the following code is not able to read the link from the database for the life of me Please help me I am a new bie
package com.shresthawebworks.dailydevotional;
public class MainActivity extends AppCompatActivity implements MediaPlayer.OnPreparedListener{
private MediaPlayer mMediaplayer;
private ImageButton dplayButton;
private ImageButton dpauseButton;
private ImageButton dstopButton;
private SeekBar seekBar;
private Handler handler;
private Runnable runnable;
private TextView durl;
private TextView drealurl;
private String currentDateTimeString = DateFormat.getDateInstance().format(new Date());
private static final String TAG = null;
private String value = "Udated";
FirebaseDatabase database = FirebaseDatabase.getInstance();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dplayButton = findViewById(R.id.playButton);
dpauseButton = findViewById(R.id.pauseButton);
dstopButton = findViewById(R.id.stopButton);
durl = findViewById(R.id.dateString);
drealurl = findViewById(R.id.urlText);
handler = new Handler();
seekBar = findViewById(R.id.seekBar1);
mMediaplayer = new MediaPlayer();
mMediaplayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
DatabaseReference myRef = database.getReference("Audios");
// Read from the database
myRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
value = (String) dataSnapshot.child(currentDateTimeString).getValue();
// do your stuff here with value
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
fetchAudioUrlFromFirebase();
//.setDataSource();
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean input) {
if (input){
mMediaplayer.seekTo(progress);
}
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
configureNextButton();}
public void playCycle(){
seekBar.setProgress(mMediaplayer.getCurrentPosition());
if (mMediaplayer.isPlaying()){
runnable =new Runnable() {
#Override
public void run() {
playCycle();
}
};
handler.postDelayed(runnable,1000);
}
}
#Override
protected void onResume() {
super.onResume();
// mMediaplayer.resu;
playCycle();
}
#Override
protected void onPause() {
super.onPause();
mMediaplayer.pause();
playCycle();
}
#Override
protected void onDestroy() {
super.onDestroy();
mMediaplayer.release();
handler.removeCallbacks(runnable);
}
private void fetchAudioUrlFromFirebase() {
final FirebaseStorage storage = FirebaseStorage.getInstance();
durl.setText(currentDateTimeString);
drealurl.setText(value);
// Create a storage reference from our app
StorageReference storageRef = storage.getReferenceFromUrl("https://firebasestorage.googleapis.com/v0/b/dailydevotional-9f982.appspot.com/o/audios%2Ftwoprincess.mp3?alt=media&token=e3f98223-68e8-461a-8452-54eca96af4fd");
storageRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
try {
// Download url of file
final String url = uri.toString();
mMediaplayer.setDataSource(url);
// wait for media player to get prepare
mMediaplayer.setOnPreparedListener(MainActivity.this);
mMediaplayer.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.i("TAG", e.getMessage());
}
});
}
#Override
public void onPrepared(final MediaPlayer mp) {
dplayButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
seekBar.setMax(mp.getDuration());
mp.start();
playCycle();
}
});
dpauseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mp.pause();
}
});
dstopButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
playCycle();
mp.stop();
mp.reset();
//fetchAudioUrlFromFirebase();
}
});
}
private void configureNextButton(){
ImageButton dButton= (ImageButton) findViewById(R.id.starLogin);
dButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startActivity(new Intent(MainActivity.this, UploadActivity.class));
}
});
}
}

Categories

Resources