如何在 Xamarin.Android 中删除对话框中的所有引用(潜在的内存泄漏)?
How to remove all references from a Dialog on dismiss in Xamarin.Android (potential memory leak)?
我想我有内存泄漏,因为在我打开一个特定的 Dialog
并更改为一个新的 Activity
后,应用程序在没有任何警告的情况下崩溃并且 Visual Studio/App 没有任何警告就停止了如动画 gif 中所示,出现警告或特定行:
第二个动画 gif 是我在 Visual Studio 中调试应用程序的地方,然后突然停止。该应用程序在没有意义的行中完成 base.OnDrawerSlide(drawerView, slideOffset)
:
现在,对话框非常重,因为它实时进行一些数学计算:
对话:
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();
}
}
我用这个 class 来“处理”对话框:
关闭 class:
private class OnDismissListener : Java.Lang.Object, IDialogInterfaceOnDismissListener
{
public void OnDismiss(IDialogInterface dialog)
{
dialog.Dispose();
}
}
奇怪的是,它似乎没有得到妥善处理,因为我可以打开其他对话框但没有任何反应,这是唯一导致这种异常行为的对话框。
此外,我试图通过将对话框的值设为空同时将其设为 属性 来删除它的引用,但失败了。我也尝试过使用垃圾收集器,但也失败了,因为它使应用程序崩溃了。
我什至尝试避免在 MainActivity
class:
中使用它来存储任何历史记录
[Android.App.Activity(Label = "Gravity Now!", MainLauncher = true, Icon = "@drawable/ic_icon", NoHistory = true)]
我什至创建了一个额外的 OnCancelListener
,我什至在其中处理 DialogView
而没有任何结果:
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;
}
}
如需了解更多信息,请查看完整源代码:
https://bitbucket.org/supernovaic/gnow-android/src/master/
此外,我得到了这个崩溃文件,但找不到任何有用的东西:
https://drive.google.com/file/d/10-wmY337mG_k6ZoS7GAcFI1reR6eqQRy/view?usp=sharing
这些是最后几行:
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}}
此时,我不知道还能做什么。知道我做错了什么吗?
P.S.:
- 我也在物理设备上测试过它(Oppo A91 和 Nexus 7 - 2013)。
- 这里是 link 如果你想在你的物理设备中测试错误:https://play.google.com/store/apps/details?id=tk.supernova.gnow
更新:
我从另一个人那里得到了这个答案 forum:
LeonLu-MSFT
In the
DefinitionFragment.cs file on line 36 in the LoadGif()
function, you
could try to use the GetBuffer()
method in-place of the ToArray()
method.
Please refer to this document for further explanations:
MemoryStream.ToArray Method (System.IO) | Microsoft Docs
现在,它加载了帮助,但在选项卡之间再次无故崩溃。顺便说一句,我在两个选项卡中都更改为GetBuffer()
。
我发现错误在这里:
List<DrawerData> currentObjects = compareAllList.DeepClone();
我不得不更改逻辑并以不同的方式创建临时数据:
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();
似乎无法删除创建的 Deep Cloner 数据。
我想我有内存泄漏,因为在我打开一个特定的 Dialog
并更改为一个新的 Activity
后,应用程序在没有任何警告的情况下崩溃并且 Visual Studio/App 没有任何警告就停止了如动画 gif 中所示,出现警告或特定行:
第二个动画 gif 是我在 Visual Studio 中调试应用程序的地方,然后突然停止。该应用程序在没有意义的行中完成 base.OnDrawerSlide(drawerView, slideOffset)
:
现在,对话框非常重,因为它实时进行一些数学计算:
对话:
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();
}
}
我用这个 class 来“处理”对话框:
关闭 class:
private class OnDismissListener : Java.Lang.Object, IDialogInterfaceOnDismissListener
{
public void OnDismiss(IDialogInterface dialog)
{
dialog.Dispose();
}
}
奇怪的是,它似乎没有得到妥善处理,因为我可以打开其他对话框但没有任何反应,这是唯一导致这种异常行为的对话框。
此外,我试图通过将对话框的值设为空同时将其设为 属性 来删除它的引用,但失败了。我也尝试过使用垃圾收集器,但也失败了,因为它使应用程序崩溃了。
我什至尝试避免在 MainActivity
class:
[Android.App.Activity(Label = "Gravity Now!", MainLauncher = true, Icon = "@drawable/ic_icon", NoHistory = true)]
我什至创建了一个额外的 OnCancelListener
,我什至在其中处理 DialogView
而没有任何结果:
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;
}
}
如需了解更多信息,请查看完整源代码:
https://bitbucket.org/supernovaic/gnow-android/src/master/
此外,我得到了这个崩溃文件,但找不到任何有用的东西:
https://drive.google.com/file/d/10-wmY337mG_k6ZoS7GAcFI1reR6eqQRy/view?usp=sharing
这些是最后几行:
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}}
此时,我不知道还能做什么。知道我做错了什么吗?
P.S.:
- 我也在物理设备上测试过它(Oppo A91 和 Nexus 7 - 2013)。
- 这里是 link 如果你想在你的物理设备中测试错误:https://play.google.com/store/apps/details?id=tk.supernova.gnow
更新:
我从另一个人那里得到了这个答案 forum:
LeonLu-MSFT
In the DefinitionFragment.cs file on line 36 in the
LoadGif()
function, you could try to use theGetBuffer()
method in-place of theToArray()
method.Please refer to this document for further explanations: MemoryStream.ToArray Method (System.IO) | Microsoft Docs
现在,它加载了帮助,但在选项卡之间再次无故崩溃。顺便说一句,我在两个选项卡中都更改为GetBuffer()
。
我发现错误在这里:
List<DrawerData> currentObjects = compareAllList.DeepClone();
我不得不更改逻辑并以不同的方式创建临时数据:
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();
似乎无法删除创建的 Deep Cloner 数据。