运行从 RootInActiveWindow
遍历 AccessibilityNodeInfo
的辅助服务会导致内存泄漏。我已在物理设备和模拟器中的 Android 6.x 和 7.x 上对此进行了测试。
查看此问题的最简单方法是打开辅助功能服务并访问不断触发 WindowContentChanged
事件的网页(例如,转到 https://time.is )。
在分析器中查看应用程序,您可以看到辅助服务在内存中永远爬升。更具体地说,来自下面示例中的 GetWindowNodes
方法。
- 我在这里做错了什么还是 Xamarin Android 中的错误?
- 有什么办法可以解决这个问题吗?
示例
元数据配置
<?xml version="1.0" encoding="utf-8" ?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagReportViewIds"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"/>
服务
[Service(Permission = "android.permission.BIND_ACCESSIBILITY_SERVICE", Label = "memtest")]
[IntentFilter(new string[] { "android.accessibilityservice.AccessibilityService" })]
[MetaData("android.accessibilityservice", Resource = "@xml/accessibilityservice")]
public class AccService : AccessibilityService
{
private const string SystemUiPackage = "com.android.systemui";
public override void OnAccessibilityEvent(AccessibilityEvent e)
{
var root = RootInActiveWindow;
if(string.IsNullOrWhiteSpace(e.PackageName) || e.PackageName == SystemUiPackage ||
root?.PackageName != e.PackageName)
{
return;
}
switch (e.EventType)
{
case EventTypes.WindowContentChanged:
case EventTypes.WindowStateChanged:
var nodes = GetWindowNodes(root, e, null);
break;
}
}
public override void OnInterrupt()
{
}
/// <summary>
/// Get a flat list of all nodes in this window.
/// </summary>
private List<AccessibilityNodeInfo> GetWindowNodes(AccessibilityNodeInfo n,
AccessibilityEvent e,
List<AccessibilityNodeInfo> nodes)
{
if (nodes == null)
{
nodes = new List<AccessibilityNodeInfo>();
}
if (n != null)
{
if (n.WindowId == e.WindowId && !(n.ViewIdResourceName?.StartsWith(SystemUiPackage) ?? false))
{
nodes.Add(n);
}
for (var i = 0; i < n.ChildCount; i++)
{
GetWindowNodes(n.GetChild(i), e, nodes);
}
}
return nodes;
}
}
分析器结果
突出显示的区域总是随着每次调用OnAccessibilityEvent
而增加:
最佳答案
做错了什么吗?漏洞?不……
执行快照,刷新浏览器页面,执行另一个快照,启动并重复。您不应该看到任何内存泄漏。 (至少我在 Xamarin.Android v7.1.0.35 上没有使用您的代码。
在 GC 运行至少一次次要回收之前,不会收集这些次要分配,您始终可以放置 GC.Collect(0);
在你的 OnAccessibilityEvent
的末尾立即清理您的List<AccessibilityNodeInfo>
分配,但 GC 将在未来某个时候运行,当它需要内存进行额外分配时...
In the absence of an explicit collection via GC.Collect() collections are on demand, based upon heap allocations. This is not a reference counting system; objects will not be collected as soon as there are no outstanding references, or when a scope has exited. The GC will run when the minor heap has run out of memory for new allocations. If there are no allocations, it will not run.
回复:https://developer.xamarin.com/guides/android/advanced_topics/garbage_collection/
更新:
我一直在发布版本 ( Xamirin.Android 7.1.0.35
) 中运行此修改后的代码,并在 tweeting.net
上打开浏览器。和过滤logcat。
已接听 20,000 次电话并计数到 OnAccessibilityEvent
方法我还没有看到任何内存问题...
尝试并比较结果...
public class AccService : AccessibilityService
{
const string SystemUiPackage = "com.android.systemui";
const string TAG = "MEMTEST";
long originalMemory;
long lastMemory;
long currentMemory;
long stabilizedMemory;
long nodeCount;
long stabilizeCount;
long cycleCount;
public AccService(IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer) : base(javaReference, transfer)
{
originalMemory = GC.GetTotalMemory(false);
}
public AccService()
{
originalMemory = GC.GetTotalMemory(false);
}
public override void OnAccessibilityEvent(AccessibilityEvent e)
{
var root = RootInActiveWindow;
if (string.IsNullOrWhiteSpace(e.PackageName) || e.PackageName == SystemUiPackage ||
root?.PackageName != e.PackageName)
{
return;
}
switch (e.EventType)
{
case EventTypes.WindowContentChanged:
case EventTypes.WindowStateChanged:
var nodes = GetWindowNodes(root, e, null);
nodeCount = nodes.Count;
foreach (var item in nodes)
{
item.Dispose();
}
nodes = null;
currentMemory = GC.GetTotalMemory(true);
if (stabilizeCount < 20)
{
stabilizeCount++;
stabilizedMemory = currentMemory;
}
cycleCount++;
Log.Info(TAG, $"{(currentMemory == lastMemory ? "Stable " : (currentMemory > lastMemory ? "Growing" : "Shrink "))} / C{currentMemory} vs. S{stabilizedMemory} / Change: {currentMemory - lastMemory} / {lastMemory - stabilizedMemory} / {cycleCount}:{nodeCount}");
if (currentMemory > stabilizedMemory * 2)
Log.Error(TAG, $"Runaway memory : {currentMemory} vs. {stabilizedMemory}");
lastMemory = currentMemory;
break;
}
}
~~~
}
日志猫:
Info (5080) / MEMTEST: Stable / C4316936 vs. S4316888 / Change: 0 / 48 / 21015:6
关于.net - Xamarin Android 辅助服务泄漏内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42321065/