Friday, 30 April 2021

How to fix memory leak issue in FusedLocationApi Fragment?

I am using fused location api to find the current location in the fragment, sometimes getting a memory leak

How to fix this issue?

com.android.zigmaster.ui.home.FragmentSearch instance
    ​     Leaking: YES (ObjectWatcher was watching this because com.android.zigmaster.ui.home.FragmentSearch received
    ​     Fragment#onDestroy() callback and Fragment#mFragmentManager is null)



  ====================================
    HEAP ANALYSIS RESULT
    ====================================
    2 APPLICATION LEAKS
    
    References underlined with "~~~" are likely causes.
    Learn more at https://squ.re/leaks.
    
    4982 bytes retained by leaking objects
    Signature: e3580ed78ace0bf62b73fb0e3e2c66f15be575a
    ┬───
    │ GC Root: Global variable in native code
    │
    ├─ com.google.android.gms.location.zzam instance
    │    Leaking: UNKNOWN
    │    Retaining 756 B in 13 objects
    │    ↓ zzam.zza
    │           ~~~
    ├─ com.google.android.gms.location.zzx instance
    │    Leaking: UNKNOWN
    │    Retaining 153 B in 7 objects
    │    ↓ zzx.zzc
    │          ~~~
    ├─ com.android.zigmaster.ui.home.HomeFragment$proceedAfterPermissionLocation$1 instance
    │    Leaking: UNKNOWN
    │    Retaining 12 B in 1 objects
    │    Anonymous subclass of com.google.android.gms.location.LocationCallback
    │    ↓ HomeFragment$proceedAfterPermissionLocation$1.this$0
    │                                                    ~~~~~~
    ╰→ com.android.zigmaster.ui.home.HomeFragment instance
    ​     Leaking: YES (ObjectWatcher was watching this because com.android.zigmaster.ui.home.HomeFragment received
    ​     Fragment#onDestroy() callback and Fragment#mFragmentManager is null)
    ​     Retaining 5.0 kB in 151 objects
    ​     key = f6ba5269-d905-4614-ac2b-4ff353b6f154
    ​     watchDurationMillis = 5518
    ​     retainedDurationMillis = 518
    
    455760 bytes retained by leaking objects
    Signature: 9dd9e366fbcb994c88d457524161a4dca4407a85
    ┬───
    │ GC Root: Global variable in native code
    │
    ├─ com.google.android.gms.location.zzam instance
    │    Leaking: UNKNOWN
    │    Retaining 456.5 kB in 7825 objects
    │    ↓ zzam.zza
    │           ~~~
    ├─ com.google.android.gms.location.zzx instance
    │    Leaking: UNKNOWN
    │    Retaining 455.9 kB in 7819 objects
    │    ↓ zzx.zzc
    │          ~~~
    ├─ com.android.zigmaster.ui.home.FragmentSearch$proceedAfterPermissionLocation$1 instance
    │    Leaking: UNKNOWN
    │    Retaining 455.8 kB in 7813 objects
    │    Anonymous subclass of com.google.android.gms.location.LocationCallback
    │    ↓ FragmentSearch$proceedAfterPermissionLocation$1.this$0
    │                                                      ~~~~~~
    ╰→ com.android.zigmaster.ui.home.FragmentSearch instance
    ​     Leaking: YES (ObjectWatcher was watching this because com.android.zigmaster.ui.home.FragmentSearch received
    ​     Fragment#onDestroy() callback and Fragment#mFragmentManager is null)
    ​     Retaining 455.8 kB in 7812 objects
    ​     key = 48799cd7-6335-4938-a6b2-71fde55e3507
    ​     watchDurationMillis = 12318
    ​     retainedDurationMillis = 7276
    ====================================
    0 LIBRARY LEAKS
    
    A Library Leak is a leak caused by a known bug in 3rd party code that you do not have control over.
    See https://square.github.io/leakcanary/fundamentals-how-leakcanary-works/#4-categorizing-leaks
    ====================================
    0 UNREACHABLE OBJECTS
    
    An unreachable object is still in memory but LeakCanary could not find a strong reference path
    from GC roots.
    ====================================
    METADATA
    
    Please include this in bug reports and Stack Overflow questions.
    
    Build.VERSION.SDK_INT: 29
    Build.MANUFACTURER: samsung
    LeakCanary version: 2.7
    App process name: com.android.zigmaster
    Stats: LruCache[maxSize=3000,hits=3853,misses=55804,hitRate=6%]
    RandomAccess[bytes=2861728,reads=55804,travel=19994971106,range=16391680,size=20725210]
    Heap dump reason: 10 retained objects, app is visible
    Analysis duration: 34210 ms
    Heap dump file path: /data/user/0/com.android.zigmaster/cache/leakcanary/2021-04-27_12-22-47_274.hprof
    Heap dump timestamp: 1619540608205
    Heap dump duration: 6203 ms
    ====================================

Here is my fragment code:

package com.android.zigmaster.ui.home



class FragmentSearch : Fragment() {
    private var binding : FragmentSearchBinding ?=null

    private lateinit var locationCallback: LocationCallback
    private lateinit var fusedLocationClient: FusedLocationProviderClient
    val locationRequestApi = LocationRequest.create()
    var gpsLatitude: String = "0.0"
    var gpsLongitute: String = "0.0"



    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View {
        binding = FragmentSearchBinding.inflate(inflater)

        binding!!.featureCurrentLocation.setOnClickListener {
            val lm = requireContext().getSystemService(Context.LOCATION_SERVICE) as LocationManager
            if (LocationManagerCompat.isLocationEnabled(lm)) {
                // check permission first
                if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                    // request the permission
                    requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 1001)
                } else {
                    proceedAfterPermissionLocation()  // has the permission.
                }
            }
            else {

                // enable GPS
                try{
                    //https://stackoverflow.com/questions/25175522/how-to-enable-location-access-programmatically-in-android
                    val locationRequest = LocationRequest.create()
                        .setInterval(30000)
                        .setFastestInterval(15000)
                        .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
                    val builder = LocationSettingsRequest.Builder()
                        .addLocationRequest(locationRequest)
                    LocationServices
                        .getSettingsClient(requireContext())
                        .checkLocationSettings(builder.build())
                        .addOnSuccessListener(requireActivity()) { response: LocationSettingsResponse? -> }
                        .addOnFailureListener(requireActivity()) { ex ->
                            if (ex is ResolvableApiException) {
                                // Location settings are NOT satisfied,  but this can be fixed  by showing the user a dialog.
                                try {
                                    // Show the dialog by calling startResolutionForResult(),  and check the result in onActivityResult().
                                    val resolvable = ex as ResolvableApiException
                                    resolvable.startResolutionForResult(requireActivity(), 1002)
                                } catch (sendEx: SendIntentException) {
                                    // Ignore the error.
                                }
                            }
                        }
                }
                catch (e: Exception){
                    Log.d("tag06", "setting page catch " + e.message)
                }
            }

        }


        //mView =binding!!.root
        return binding!!.root
    }


    private fun proceedAfterPermissionLocation() {
        //.......................................start location callback
        locationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult) {
                locationResult ?: return
                for (location in locationResult.locations) {
                    val currentLocation = locationResult.lastLocation
                    gpsLatitude = currentLocation.latitude.toString()
                    gpsLongitute = currentLocation.longitude.toString()
                    Log.d("danger04", "..............$gpsLatitude, $gpsLongitute")

                    try{
                        fusedLocationClient.removeLocationUpdates(locationCallback)
                    }catch (e: Exception){ }
                }
            }
        }
        locationRequestApi.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
        locationRequestApi.interval = 10000
        locationRequestApi.fastestInterval = 5000
        //mLocationRequest!!.smallestDisplacement = 10f // 170 m = 0.1 mile => get accuracy whil travel
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(requireContext().applicationContext)
        if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            fusedLocationClient.requestLocationUpdates(locationRequestApi, locationCallback, null)
        }
    }


    override fun onActivityResult(requestCode: Int, resultCode: Int, @Nullable data: Intent?) {
        if (1002 == requestCode) {
            if (Activity.RESULT_OK == resultCode) {
                //user clicked OK, you can startUpdatingLocation(...);
                proceedAfterPermissionLocation()
            } else {
                //user clicked cancel: informUserImportanceOfLocationAndPresentRequestAgain();
            }
        }
    }
    override fun onRequestPermissionsResult(requestCode: Int,
                                            permissions: Array<String>, grantResults: IntArray) {
        Log.d("calendar", "...........onRequestPermissionsResult code : $requestCode")
        when (requestCode) {

            1001 -> {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // permission was granted.
                    proceedAfterPermissionLocation() // permission was granted.
                    Log.d("location", "...........onRequestPermissionsResult : granted")
                } else {
                    // permission denied.
                    Log.d("location", "...........onRequestPermissionsResult : denied")
                }
                return
            }

        }
    }



    override fun onDestroyView() {
        super.onDestroyView()
        //.......................................stop location
        try{
            fusedLocationClient.removeLocationUpdates(locationCallback)
        }catch (e: Exception){ }

        binding=null
    }
}


from How to fix memory leak issue in FusedLocationApi Fragment?

No comments:

Post a Comment