How to fully dismiss a Dialog in Xamarin.Android (potential memory leak)?

Federico Navarrete 621 Reputation points
2021-09-12T09:52:57.993+00:00

I guess I have a memory leak because after I open a specific Dialog and change to a new Activity, the app crashes without any warning and Visual Studio/App stops without any warning or specific line where it happens as you can see in the animated gif:

131269-ezgifcom-gif-maker.gif

This second animated gif is where I'm debugging the app in Visual Studio and then suddenly stops. The app finished in line that doesn't make sense base.OnDrawerSlide(drawerView, slideOffset):

131993-ezgifcom-gif-maker.gif

Now, the Dialog, in fact, is quite heavy because it does some mathematical calculations in real-time:

Dialog:

private void BtnCompareAll_Click(object sender, EventArgs e)  
{  
    GravityPlanets gInfo = new();  
    View dialogView = LayoutInflater.Inflate(Resource.Layout.CompareAll, null);  
    var Dialog = new Android.App.AlertDialog.Builder(Context);  

    int firstCelestialLoc = GetItemPosition(planets, SpinnerFirstCelestial.Text);  
    var gravity = new Supernova.Core.Gravity();  

    var listView = dialogView.FindViewById<ListView>(Resource.Id.lstCelestialObjects);  
    var textObject1 = dialogView.FindViewById<TextView>(Resource.Id.textObject1);  
    var imgView = dialogView.FindViewById<ImageView>(Resource.Id.imgCObject);  
    var yourWeight = dialogView.FindViewById<TextView>(Resource.Id.yourWeight);  

    imgView.SetImageDrawable(compareAllList[firstCelestialLoc].image);  

    textObject1.Text = SpinnerGravityUnits.Text == gUnits[0]  
        ? $"{planets[firstCelestialLoc]}, {GetCelestialObject(gInfo.CelestialObjectType(firstCelestialLoc))}, {gInfo.GetGravity(firstCelestialLoc)} {SpinnerGravityUnits.Text}"  
        : $"{planets[firstCelestialLoc]}, {GetCelestialObject(gInfo.CelestialObjectType(firstCelestialLoc))}, {Math.Round(gravity.ChangeToFeet(gInfo.GetGravity(firstCelestialLoc)), 3)} {SpinnerGravityUnits.Text}";  

    List<DrawerData> currentObjects = compareAllList.DeepClone();  
    currentObjects.RemoveAt(firstCelestialLoc);  

    bool isNumeric = double.TryParse(TxtWeight.Text, out double earthWeight);  
    if (isNumeric && earthWeight > 0)  
    {  
        yourWeight.Visibility = ViewStates.Visible;  
        yourWeight.Text = $"{GetString(Resource.String.lblYourWeight)} {TxtWeight.Text}{SpinnerWeightUnits.Text}";  
    }  
    else  
    {  
        yourWeight.Visibility = ViewStates.Gone;  
        yourWeight.Text = "";  
    }  

    GravityPlanets gPlanets = new();  

    for (int i = 0; i < currentObjects.Count(); i++)  
    {  
        var secondG = GetItemPosition(planets, currentObjects[i].name);  
        var gPercentage = Math.Round(gInfo.PercentageGravity(firstCelestialLoc, secondG), 0);  

        switch (gInfo.ComparedGravity(firstCelestialLoc, secondG))  
        {  
            case 0:  
                gravType = GetString(Resource.String.gravLower);  
                currentObjects[i].color = "#43A047";  
                break;  
            default:  
                gravType = GetString(Resource.String.gravGreater);  
                currentObjects[i].color = "#F44336";  
                break;  
        }  

        var gravVal = SpinnerGravityUnits.Text == gUnits[0] ? gInfo.GetGravity(secondG) : Math.Round(gravity.ChangeToFeet(gInfo.GetGravity(secondG)), 3);  

        currentObjects[i].name = $"<b>{currentObjects[i].name}, {GetCelestialObject(gInfo.CelestialObjectType(secondG))}</b><br>{gravVal} {SpinnerGravityUnits.Text}, {string.Format(gravType, gPercentage)}";  

        if (isNumeric && earthWeight > 0)  
        {  
            currentObjects[i].name += $"<br>{GetString(Resource.String.lblYourWeightCO)} {GetNewWeight(gPlanets, double.Parse(TxtWeight.Text), firstCelestialLoc, secondG)} {SpinnerWeightUnits.Text}";  
        }  

        var adapter = new CompareAllAdapter(currentObjects);  

        listView.Adapter = adapter;  

        Dialog.SetView(dialogView);  

        Dialog.SetOnDismissListener(new OnDismissListener());  

        Dialog.Show();  
    }  
}  

And I use this class to "dispose" of the dialog:

Dismiss class:

private class OnDismissListener : Java.Lang.Object, IDialogInterfaceOnDismissListener  
{  
    public void OnDismiss(IDialogInterface dialog)  
    {  
        dialog.Dispose();  
    }  
}  

Oddly, it seems it wasn't properly disposed of because I can open other dialogs and nothing happens, this is the only one that causes this abnormal behavior.

Additionally, I tried to remove its reference by making the dialog's value null while making it a property and failed. I also tried to use the garbage collector and also failed because it crashed the app.

I even tried avoiding to store any history using this in the MainActivity class:

[Android.App.Activity(Label = "Gravity Now!", MainLauncher = true, Icon = "@drawable/ic_icon", NoHistory = true)]

I even created an additional OnCancelListener where I'm even disposing of the DialogView without any result:

Dialog.SetOnCancelListener(new OnCancelListener(dialogView));  

private class OnCancelListener : Java.Lang.Object, IDialogInterfaceOnCancelListener  
{  
    private View dialogView;  

    public OnCancelListener(View dialogView)  
    {  
        this.dialogView = dialogView;  
    }  

    public void OnCancel(IDialogInterface dialog)  
    {  
        dialogView.Dispose();  
        dialogView = null;  
        dialog.Dispose();  
        dialog = null;  
    }  
}  

For more info, here is the full source code:

https://bitbucket.org/supernovaic/gnow-android/src/master/

Also, I got this crash file, but I cannot find anything useful:

https://drive.google.com/file/d/10-wmY337mG_k6ZoS7GAcFI1reR6eqQRy/view?usp=sharing

These are the last lines:

09-11 18:19:26.889 28140 31014 I ActivityManager: Force stopping
tk.supernova.gnow appid=10156 user=0: from pid 13171 09-11
18:19:26.892 28785 28785 I AppBase : AppBase.onTrimMemory():615
onTrimMemory(): 5 09-11 18:19:26.894 13171 13174 I cmd : oneway
function results will be dropped but finished with status OK and
parcel size 4 09-11 18:19:26.935 28785 28785 I
GoogleInputMethodService: GoogleInputMethodService.onTrimMemory():4225
onTrimMemory(): 5 09-11 18:19:26.951 28140 28158 W ActivityManager:
setHasOverlayUi called on unknown pid: 12844 09-11 18:19:26.953 28140
28158 W ActivityTaskManager: Activity top resumed state loss timeout
for ActivityRecord{3c51759 u0
tk.supernova.gnow/crc64c3564668e399e885.Help t-1 f}}

At this point, I don't know what else to do. Any idea what I'm doing wrong?

P.S.:

Developer technologies | .NET | Xamarin
{count} votes

Accepted answer
  1. Federico Navarrete 621 Reputation points
    2021-10-24T07:52:44.457+00:00

    I found that the bug was here:

    List<DrawerData> currentObjects = compareAllList.DeepClone();
    

    I had to change the logic and create the temp data in a different way:

    private List<DrawerData> GetPlanetsData()
    {
        return new List<DrawerData>()
        {
            new DrawerData() {
                name = GetString(Resource.String.itemSun),
                image = GetCelestialObjectDrawable(Resource.Drawable.sun),
                url = GetString(Resource.String.itemWikiSun)
            },
            new DrawerData() {
                name = GetString(Resource.String.itemMercury),
                image = GetCelestialObjectDrawable(Resource.Drawable.mercury),
                url = GetString(Resource.String.itemWikiMercury)
            },
            new DrawerData() {
                name = GetString(Resource.String.itemVenus),
                image = GetCelestialObjectDrawable(Resource.Drawable.venus),
                url = GetString(Resource.String.itemWikiVenus)
            }
            //...
        };
    }
    
    List<DrawerData> currentObjects = GetPlanetsData();
    

    It seems somehow the Deep Cloner data created cannot be removed somehow.

    0 comments No comments

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.