PDFViewCtrl не утилизируется
Мы работаем над приложением, использующим PDFTron. Мы заметили, что при открытии Activity, которая отображает выделенную память, сильно увеличивается. После ухода от действия только часть памяти снова освобождается. Некоторые части PDFtron остались позади.
Проблема в том, что действие не завершается, хотя мы установили для него значение NoHistory и singleTask. Я просмотрел несколько курсов по управлению памятью, но не могу понять, что является причиной того, что активность остается открытой.
Прежде чем мы позвоним Finish();
на деятельность мы располагаем PdfViewCtrl. Тогда в OnDestroy:
protected override void OnDestroy()
{
base.OnDestroy();
if (_mPdfViewCtrl == null) return;
_mPdfViewCtrl.Destroy();
_mPdfViewCtrl = null;
}
Если приложение используется в течение длительного периода времени, это приводит к ошибке нехватки памяти.
--- End of managed Java.Lang.Error stack trace ---
java.lang.OutOfMemoryError
dalvik.system.NativeStart.run(Native Method):0
или же
--- End of managed Java.Lang.Error stack trace ---
java.lang.OutOfMemoryError
android.graphics.Bitmap.nativeCreate(Native Method):0
android.graphics.Bitmap.createBitmap(Bitmap.java:726):0
android.graphics.Bitmap.createBitmap(Bitmap.java:703):0
android.graphics.Bitmap.createBitmap(Bitmap.java:670):0
pdftron.PDF.Utils.SignaturePickerDialog$SignatureView.onSizeChanged(SignaturePickerDialog.java:244):0
android.view.View.sizeChange(View.java:15326):0
android.view.View.setFrame(View.java:15290):0
android.view.View.layout(View.java:15201):0
android.widget.RelativeLayout.onLayout(RelativeLayout.java:1076):0
android.view.View.layout(View.java:15204):0
android.view.ViewGroup.layout(ViewGroup.java:4793):0
android.widget.FrameLayout.onLayout(FrameLayout.java:448):0
android.view.View.layout(View.java:15204):0
android.view.ViewGroup.layout(ViewGroup.java:4793):0
android.widget.RelativeLayout.onLayout(RelativeLayout.java:1076):0
android.view.View.layout(View.java:15204):0
android.view.ViewGroup.layout(ViewGroup.java:4793):0
android.widget.FrameLayout.onLayout(FrameLayout.java:448):0
android.view.View.layout(View.java:15204):0
android.view.ViewGroup.layout(ViewGroup.java:4793):0
android.widget.FrameLayout.onLayout(FrameLayout.java:448):0
android.view.View.layout(View.java:15204):0
android.view.ViewGroup.layout(ViewGroup.java:4793):0
android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677):0
android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531):0
android.widget.LinearLayout.onLayout(LinearLayout.java:1440):0
android.view.View.layout(View.java:15204):0
android.view.ViewGroup.layout(ViewGroup.java:4793):0
android.widget.FrameLayout.onLayout(FrameLayout.java:448):0
android.view.View.layout(View.java:15204):0
android.view.ViewGroup.layout(ViewGroup.java:4793):0
android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677):0
android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531):0
android.widget.LinearLayout.onLayout(LinearLayout.java:1440):0
android.view.View.layout(View.java:15204):0
android.view.ViewGroup.layout(ViewGroup.java:4793):0
android.widget.FrameLayout.onLayout(FrameLayout.java:448):0
android.view.View.layout(View.java:15204):0
android.view.ViewGroup.layout(ViewGroup.java:4793):0
android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2263):0
android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2009):0
android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1251):0
android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6379):0
android.view.Choreographer$CallbackRecord.run(Choreographer.java:791):0
android.view.Choreographer.doCallbacks(Choreographer.java:591):0
android.view.Choreographer.doFrame(Choreographer.java:561):0
android.view.Choreographer$FrameHandler.handleMessage(Choreographer.java:693):0
android.os.Handler.dispatchMessage(Handler.java:99):0
android.os.Looper.loop(Looper.java:137):0
android.app.ActivityThread.main(ActivityThread.java:5493):0
java.lang.reflect.Method.invokeNative(Native Method):0
java.lang.reflect.Method.invoke(Method.java:525):0
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1209):0
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1025):0
dalvik.system.NativeStart.main(Native Method):0
Ниже экрана печати из симулируемого сеанса (мониторинг студии Android):
Отредактировано OnDestroy:
var old_tool = _mPdfViewCtrl.ToolManager;
var old_doc = _mPdfViewCtrl.Doc;
_mPdfViewCtrl?.CloseDoc();
_mPdfViewCtrl?.CloseTool();
_mPdfViewCtrl?.Destroy();
_mPdfViewCtrl = null;
_mPdfViewCtrl?.Dispose();
if (old_doc != null)
{
old_doc.Dispose();
}
if (old_tool != null)
{
old_tool.Dispose();
}
2 ответа
Если ваша активность также имеет ссылку на объект PDFDoc, то вам также следует избавиться от этого. Это рекомендуемый способ.
protected override void OnDestroy()
{
base.OnDestroy();
if (_mPdfViewCtrl == null) return;
_mPdfViewCtrl.Destroy();
_mPdfViewCtrl = null;
if(_mPdfDoc != null)
{
_mPdfDoc.Dispose();
}
}
или в более общем плане
protected override void OnDestroy()
{
base.OnDestroy();
if (_mPdfViewCtrl == null) return;
PDFDoc old_doc = _mPdfViewCtrl.Doc;
_mPdfViewCtrl.Destroy();
_mPdfViewCtrl = null;
if(old_doc != null)
{
old_doc.Dispose();
}
}
По этой ссылке:
"В: У меня есть страница, на которой пользователь выбирает файлы, и страница, на которой пользователь просматривает выбранный PDF-файл. Когда пользователь переходит обратно со страницы просмотра PDF на страницу с файлами, я хочу очистить ресурсы, используемые PDFNet. правильный порядок очистки инструментов, PDFViewCtrl и PDFDoc.
A: Вам нужно сначала вызвать PDFViewCtrl.CloseDoc(), а затем вызвать PDFDoc.Dispose(). Это хорошо, чтобы поместить в OnNavigatedFrom для любой страницы, использующей PDFViewCtrl.
Кроме того, PDFViewCtrl не может быть удален вручную, вместо этого мы добавили PDFViewCtrl.FreeResources, который удаляет все базовые структуры, что означает, что используется меньше памяти, пока не включится сборщик мусора.
Я хотел бы добавить следующее:
PDFDoc doc = this.PDFViewCtrl.GetDoc();
this.PDFViewCtrl.CloseDoc();
if (doc != null)
{
await CloseDocAsync(doc);
}
this.PDFViewCtrl.FreeResources();
Где CloseDocAsync просто вызывает doc.Dispose в фоновом потоке (в случае, если это большой, сложный документ). В этом может не быть необходимости, поскольку все известные мне документы удаляются довольно быстро. Но это не больно. Даже 200 мс задержки в потоке пользовательского интерфейса могут быть разрушительными.
Следует иметь в виду, что сборщик мусора может повторно запросить PDFViewCtrl, чтобы все зарегистрированные в нем события были незарегистрированными. Вот почему ToolManager.Dispose() должен вызываться, когда вы также уходите со страницы. В этом случае просто вызовите ToolManager.Dispose перед любым кодом, который я добавил выше. "
Если ничего из вышеперечисленного не поможет, вы можете попробовать опубликовать свой вопрос на форуме PDFTron. У компании также есть опция технической поддержки, которая обеспечивает приоритетную техническую поддержку. В зависимости от используемой версии, техническая поддержка может быть бесплатной.
РЕДАКТИРОВАТЬ
Ссылки, которые я дал на документацию, были для версии Windows, а не для Android. Вот ссылки на версии Android для PDFViewCtrl и PDFDoc.
Таким образом, код из ссылки, которую я дал выше, не будет работать для вас. Вместо этого вы можете просто закрыть документ. Существует метод CloseDoc, который можно вызывать из класса PDFViewCtrl. Если это не работает, попробуйте вызвать метод Close из метода PDFDoc. Класс PDFViewCtrl имеет метод getDoc, который возвращает текущий документ, который является PDFDoc. В документации сказано, что метод PDFDoc.close является деструктором. Итак, попробуйте эти две идеи.
Еще одна вещь, которую я заметил, заключается в том, что в классе PDFViewCtrl есть методы блокировки и разблокировки. Вы можете попробовать вызвать метод docUnlock перед вызовом метода close, если две вышеуказанные идеи не работают. Идея заключается в том, что, возможно, вызов close либо зависнет, либо закончится, если документ заблокирован.
Теперь, если ничего из этого не работает, то есть ли что-то еще, что вы делаете, жует память? Возможно ли, что вы или кто-то еще в вашей группе забыли очистить память для всего, что вы могли бы делать? Кто-нибудь использует C++ или, возможно, использует API, который использует C++ снизу и игнорирует удаление памяти, или вызывает соответствующий метод close для правильной очистки?
Использование профилировщика может помочь ответить на эти вопросы. Доступны сторонние профилировщики или вы можете попробовать Microsoft.NET Memory Analyzer. Я не знаю, будет ли работать Android. Для этого также требуется версия.NET Framework версии 4.5 или выше.
Существует также профилировщик Xamarin, который, пожалуй, является лучшим выбором для начала.