Monotouch:在单独的线程中渲染PDF页面预览,有时会崩溃
本文关键字:崩溃 PDF 单独 线程 Monotouch | 更新日期: 2023-09-27 18:11:01
我有一个UISlider
。它用于快速浏览PDF。每当达到下一页的阈值时,我就在滑块的旋钮旁边显示一个UIView
,其中包含目标页面的一个小预览。滑块代码看起来在下面(一些部分被剥离)。如果到达下一页,则生成一个新的预览,否则,现有的预览将沿着滑块移动。
我得到了各种各样的效果:
-
如果预览很多页面,应用程序在
崩溃MonoTouch.CoreGraphics.CGContext。处置(bool) <0x00047>UIKitApplication:com.brainloop。brainloopbrowser[0x1a2d][2951]: at MonoTouch.CoreGraphics.CGContext.Finalize () <</p>
-
或者如果我在最后一个方法中删除对Dispose()的调用:
[NSAutoreleasePool release]: This pool has already been released, do not drain it (double release).
从查看代码,有人知道出了什么问题吗?或者使用线程的整个方法是错误的?
this.oScrollSlider = new UISlider ();
this.oScrollSlider.TouchDragInside += delegate( object oSender, EventArgs oArgs )
{
this.iCurrentPage = (int)Math.Round (oScrollSlider.Value);
if (this.iCurrentPage != this.iLastScrollSliderPage)
{
this.iLastScrollSliderPage = this.iCurrentPage;
this.RenderScrollPreviewImage(this.iCurrentPage);
}
};
this.oScrollSlider.ValueChanged += delegate
{
if (this.oScrollSliderPreview != null)
{
this.oScrollSliderPreview.RemoveFromSuperview ();
this.oScrollSliderPreview.Dispose();
this.oScrollSliderPreview = null;
}
// Go to the selected page.
};
创建预览的方法正在启动一个新线程。如果用户在线程还在运行的时候更改了页面,那么线程将被终止,并预览下一页:
private void RenderScrollPreviewImage (int iPage)
{
// Create a new preview view if not there.
if(this.oScrollSliderPreview == null)
{
SizeF oSize = new SizeF(150, 200);
RectangleF oFrame = new RectangleF(new PointF (this.View.Bounds.Width - oSize.Width - 50, this.GetScrollSliderOffset (oSize)), oSize);
this.oScrollSliderPreview = new UIView(oFrame);
this.oScrollSliderPreview.BackgroundColor = UIColor.White;
this.View.AddSubview(this.oScrollSliderPreview);
UIActivityIndicatorView oIndicator = new UIActivityIndicatorView(UIActivityIndicatorViewStyle.Gray);
oIndicator.Center = new PointF(this.oScrollSliderPreview.Bounds.Width/2, this.oScrollSliderPreview.Bounds.Height/2);
this.oScrollSliderPreview.AddSubview(oIndicator);
oIndicator.StartAnimating();
}
// Remove all subviews, except the activity indicator.
if(this.oScrollSliderPreview.Subviews.Length > 0)
{
foreach(UIView oSubview in this.oScrollSliderPreview.Subviews)
{
if(!(oSubview is UIActivityIndicatorView))
{
oSubview.RemoveFromSuperview();
}
}
}
// Kill the currently running thread that renders a preview.
if(this.oRenderScrollPreviewImagesThread != null)
{
try
{
this.oRenderScrollPreviewImagesThread.Abort();
}
catch(ThreadAbortException)
{
// Expected.
}
}
// Start a new rendering thread.
this.oRenderScrollPreviewImagesThread = new Thread (delegate()
{
using (var oPool = new NSAutoreleasePool())
{
try
{
// Create a quick preview.
UIImageView oImgView = PdfViewerHelpers.GetLowResPagePreview (this.oPdfDoc.GetPage (iPage), new RectangleF (0, 0, 150, 200));
this.InvokeOnMainThread(delegate
{
if(this.oScrollSliderPreview != null)
{
oImgView.Center = new PointF(this.oScrollSliderPreview.Bounds.Width/2, this.oScrollSliderPreview.Bounds.Height/2);
// Add the PDF image to the preview view.
this.oScrollSliderPreview.AddSubview(oImgView);
}
});
}
catch (Exception)
{
}
}
});
// Start the thread.
this.oRenderScrollPreviewImagesThread.Start ();
}
要渲染PDF图像,我使用以下命令:
internal static UIImageView GetLowResPagePreview (CGPDFPage oPdfPage, RectangleF oTargetRect)
{
RectangleF oPdfPageRect = oPdfPage.GetBoxRect (CGPDFBox.Media);
// If preview is requested for the PDF index view, render a smaller version.
float fAspectScale = 1.0f;
if (!oTargetRect.IsEmpty)
{
fAspectScale = GetAspectZoomFactor (oTargetRect.Size, oPdfPageRect.Size, false);
// Resize the PDF page so that it fits the target rectangle.
oPdfPageRect = new RectangleF (new PointF (0, 0), GetFittingBox (oTargetRect.Size, oPdfPageRect.Size));
}
// Create a low res image representation of the PDF page to display before the TiledPDFView
// renders its content.
int iWidth = Convert.ToInt32 ( oPdfPageRect.Size.Width );
int iHeight = Convert.ToInt32 ( oPdfPageRect.Size.Height );
CGColorSpace oColorSpace = CGColorSpace.CreateDeviceRGB();
CGBitmapContext oContext = new CGBitmapContext(null, iWidth, iHeight, 8, iWidth * 4, oColorSpace, CGImageAlphaInfo.PremultipliedLast);
// First fill the background with white.
oContext.SetFillColor (1.0f, 1.0f, 1.0f, 1.0f);
oContext.FillRect (oPdfPageRect);
// Scale the context so that the PDF page is rendered
// at the correct size for the zoom level.
oContext.ScaleCTM (fAspectScale, fAspectScale);
oContext.DrawPDFPage (oPdfPage);
CGImage oImage = oContext.ToImage();
UIImage oBackgroundImage = UIImage.FromImage( oImage);
oContext.Dispose();
oImage.Dispose ();
oColorSpace.Dispose ();
UIImageView oBackgroundImageView = new UIImageView (oBackgroundImage);
oBackgroundImageView.Frame = new RectangleF (new PointF (0, 0), oPdfPageRect.Size);
oBackgroundImageView.ContentMode = UIViewContentMode.ScaleToFill;
oBackgroundImageView.UserInteractionEnabled = false;
oBackgroundImageView.AutoresizingMask = UIViewAutoresizing.None;
return oBackgroundImageView;
}
避免Thread.Abort()
。
是的,这里有一些关于它的链接:
http://www.interact-sw.co.uk/iangblog/2004/11/12/cancellation http://haacked.com/archive/2004/11/12/how-to-stop-a-thread.aspx如果你可以使用。net 4.0的特性,那就使用它们。在您的情况下,使用Task<T>
可能更容易工作。
此外,我认为创建一些节流并仅在用户不活动25-100毫秒后启动新线程会有所帮助。
按照Jonathan的建议去掉Thread.Abort()。
不要为每个图像启动一个新线程,而是使用一个带有工作队列的后台线程。然后简单地将最重要的页面放在队列的前面,以便它尽快呈现。您还可以选择限制队列的大小或从中删除不需要的项。