androidandroid-recyclerviewmemory-leaksandroid-memory

Android: my app memory over 300MB of ram just on idle


i noticed that my app uses alot of memory so i used the profiler and found that as soon as the app starts and splash screen appear the app get over 150+MB even on splash before opening main or anyother activity.

Update: i solved all memory leaks, yet my app still my app uses on my real phone nearly 300+ MB of ram just while browsing the MainActivity with recycleview.

LeakCanary and i keeps giving me error that this is a cause of memory leaks so anyone please tell me how to solve it >

MainActivity

if you clicked any recyclerview items:

itemclicked

[LeakCanary]

Activity Code:

public class WorkDetailsActivity extends AppCompatActivity {

    ArrayList<String> imagesFromURL = new ArrayList<String>();
    ActivityWorkDetailsBinding binding;
    DatabaseReference databaseReference;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityWorkDetailsBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

        String workUid_details = getIntent().getExtras().getString("UID_Details");
        String title = getIntent().getExtras().getString("name");
        String description = getIntent().getExtras().getString("description");
        String location = getIntent().getExtras().getString("location");
        String path = getIntent().getExtras().getString("path");


        databaseReference = FirebaseDatabase.getInstance().getReference().child("Work").child(path);
        databaseReference.child(workUid_details).addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                binding.workDetailsTitle.setText(title);
                binding.workDetailsDescription.setText(description);
                binding.workDetailsLocation.setText(location);
                binding.getUIDDetails.setText(workUid_details);

                for (DataSnapshot dataSnapshot : snapshot.child("images").getChildren()) {
                    String value = String.valueOf(dataSnapshot.child("image").getValue());
                    imagesFromURL.add(value);
                    //Log.i("Value", String.valueOf(imagesFromURL));
                }
                initRecyclerView();
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
            }
        });
    }

    private void initRecyclerView(){
        binding.workDetailsImage.setNestedScrollingEnabled(false);
        binding.workDetailsImage.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
        binding.workDetailsImage.setHasFixedSize(true);
        PrivateRecyclerAdapter adapter = new PrivateRecyclerAdapter(this, imagesFromURL);
        binding.workDetailsImage.setAdapter(adapter);
        binding.progressBar.setVisibility(View.VISIBLE);

       /* //Add Divider between recyclerView items
        DividerItemDecoration itemDecorator = new DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL);
        binding.workDetailsImage.addItemDecoration(itemDecorator);

        final int radius = getResources().getDimensionPixelSize(R.dimen.radius);
        final int dotsHeight = getResources().getDimensionPixelSize(R.dimen.dots_height);
        final int color = ContextCompat.getColor(this, R.color.green);
        binding.workDetailsImage.addItemDecoration(new DotsIndicatorDecoration(radius, radius * 2, dotsHeight, color, color));
        binding.workDetailsImage.setOnFlingListener(null);
        new PagerSnapHelper().attachToRecyclerView(binding.workDetailsImage);*/
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        imagesFromURL = null;
        binding.workDetailsImage.setAdapter(null);
        binding.workDetailsTitle.setText(null);
        binding.workDetailsLocation.setText(null);
        binding.workDetailsDescription.setText(null);
        binding.getUIDDetails.setText(null);
        binding.workDetailsImage.setAdapter(null);
    }

Solution

  • The problem with the above code is that you are registering value event listener as anonymous implementation. Which will hold the reference of the activity. And based on the LeakCanary stack trace your activity was in destroyed state but due to the listener the activity instance is not being able to be garbage collected and hence it is being leaked. What you need to do is add and remove the listener as below.

    1. Create the instance of ValueEventListener and store it in variable

      ValueEventListener valueEventListener = new ValueEventListener() {
          @Override
          public void onDataChange(@NonNull DataSnapshot snapshot) {
              binding.workDetailsTitle.setText(title);
              binding.workDetailsDescription.setText(description);
              binding.workDetailsLocation.setText(location);
              binding.getUIDDetails.setText(workUid_details);
      
              for (DataSnapshot dataSnapshot : snapshot.child("images").getChildren()) {
                  String value = String.valueOf(dataSnapshot.child("image").getValue());
                  imagesFromURL.add(value);
                  //Log.i("Value", String.valueOf(imagesFromURL));
              }
              initRecyclerView();
          }
      
          @Override
          public void onCancelled(@NonNull DatabaseError error) {
          }
      });
      
    2. Register it in the onStart method of your activity lifecycle using

      @Override
      protected void onStart() {
      databaseReference.child(workUid_details).addValueEventListener(valueEventListener);
      }
      
    3. Remove the listener in onStop method of your activity lifecycle using

      @Override
      protected void onStop() {
      databaseReference.child(workUid_details).removeEventListener(valueEventListener);
      }