Xamarin.Android如何在Dialog dismiss时清除引用解决内存泄漏问题
解答
核心问题根因
1. Dialog重复创建导致内存溢出
你将Dialog.Show()、Adapter创建、ListView赋值逻辑写在了for循环内部,有多少条天体数据就会弹出多少个重叠的AlertDialog,每个Dialog都会持有dialogView、图片资源、列表控件的引用,直接导致内存占用短时间内飙升,触发OOM崩溃。你观察到的崩溃出现在无关代码行,是因为内存耗尽时系统强制杀掉应用,恰好当时执行到该行代码,和该行代码本身无关。
2. 资源释放逻辑错误
Xamarin.Android会自动管理Java层对象的生命周期,你在监听中手动调用Dispose释放Dialog和View,反而可能导致野指针问题,增加崩溃概率。
修复方案
调整代码逻辑,把列表数据处理、Adapter创建、Dialog显示的逻辑移到for循环外部:
// 仅保留数据处理逻辑在循环内 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}"; } } // 循环结束后再初始化Adapter、显示Dialog var adapter = new CompareAllAdapter(currentObjects); listView.Adapter = adapter; Dialog.SetView(dialogView); Dialog.SetOnDismissListener(new OnDismissListener()); Dialog.Show();
额外优化建议
- 去掉手动Dispose Dialog和View的逻辑,只需要在Dialog关闭后清空类层面持有的Dialog、dialogView引用即可。
- 若Dialog中使用的图片资源较大,可以在Dialog关闭时主动回收ImageView的Drawable资源,进一步降低内存占用。
- 列表TextView展示HTML内容时,使用
Html.FromHtml方法处理,避免字符串拼接带来的性能损耗。
内容的提问来源于stack exchange,提问作者Federico Navarrete
相关产品推荐
相关产品推荐

