The Finalizer is never called after leaving the activity. Does that mean the activity is still alive even though I moved on to the next activity.
namespace XamarinTest {
[Activity(Label = "XamarinTest", Icon = "@drawable/icon")]
public class MainActivity : Activity {
private int count = 1;
private TextView density;
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.ScreenData);
density = FindViewById<TextView>(Resource.Id.Density);
var pendingInent = new Intent();
pendingInent.SetFlags(ActivityFlags.ClearTop);
pendingInent.SetClass(this, typeof(TestActivity));
StartActivity(pendingInent);
Finish();
}
~MainActivity() {
Console.WriteLine("Finalizer called");
}
protected override void Dispose(bool disposing){
if (disposing) {
density.Dispose();
density = null;
}
base.Dispose(disposing);
}
}
}
This is actually remarkably complicated; the short answer, regarding the activity still being alive, is yes and no. Providing you have cleaned up the resources for your Activity
correctly, your activity will be cleaned up (eventually) by the garbage collector.
Regarding cleanup, it's import to know that Xamarin discourages (slide 44 onwards) using finalizers. Here's why:
- They are not guaranteed to run within any deadline.
- They don't run in a specific sequence.
- They make objects live longer.
- The GC doesn't know about unmanaged resources.
Therefore, using a finalizer to perform cleanup is the wrong way of doing things... If you want to ensure that MainActivity
is destroyed, manually dispose the Activity
in it's OnDestroy
callback:
protected override void OnDestroy ()
{
base.OnDestroy ();
this.Dispose (); // Sever java binding.
}
This will cause Mono to break the peer object connection and destroy the activity during the next garbage collection cycle (GC.Collect(GC.MaxGeneration)
). From the docs:
To shorten object lifetime, Java.Lang.Object.Dispose() should be invoked. This will manually "sever" the connection on the object between the two VMs by freeing the global reference, thus allowing the objects to be collected faster.
Note the call order there, this.Dispose()
must be called after any code that invokes back into Android land. Why? All the connections between Java and .NET are now broken to allow Android to reclaim resources so any code that uses Android-land objects (Fragment, Activity, Adapter) will fail.
Now, onto some debugging techniques for Activity leaks. To verify that your activity is being cleaned up, add the following code to the OnCreate
method of your apps entry Activity
:
var vmPolicy = new StrictMode.VmPolicy.Builder ();
StrictMode.SetVmPolicy (vmPolicy.DetectActivityLeaks().PenaltyLog().Build ());
This enables StrictMode
, a useful debugging tool that happily informs you when you've leaked resources. When one of your apps activities isn't released correctly, it will dump something like this to the output stream:
[StrictMode] class activitydispose.LeakyActivity; instances=2; limit=1
[StrictMode] android.os.StrictMode$InstanceCountViolation: class activitydispose.LeakyActivity; instances=2; limit=1
[StrictMode] at android.os.StrictMode.setClassInstanceLimit(StrictMode.java:1)
Combining this with the Dispose()
call, you can check that activities are being released. Here is how you'd typically an Activity
and its resources in Xamarin.Android:
protected override void Dispose (bool disposing)
{
// TODO: Dispose logic here.
base.Dispose (disposing);
GC.Collect(GC.MaxGeneration); // Will force cleanup but not recommended.
}
protected override void OnDestroy ()
{
if (density != null) { // Release Java objects (buttons, adapters etc) here
density.Dispose ();
density = null;
}
base.OnDestroy ();
this.Dispose (); // Sever java binding.
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With