I get random memory crashes with no memory warning. I have updated my ios to 10.0.2, my Xamarin to 6.2. I added camera usage description and photo library usage description, as suggested.
I have the following code in my FinishedPickingMedia callback
public async override void FinishedPickingMedia(UIImagePickerController picker, NSDictionary info)
{
try
{
// determine what was selected, video or image
bool isImage = false;
switch (info[UIImagePickerController.MediaType].ToString())
{
case "public.image":
isImage = true;
break;
case "public.video":
break;
}
if (isImage)
{
UIImage originalImage = info[UIImagePickerController.OriginalImage] as UIImage;
if (originalImage != null)
{
UIImageOrientation OrIn = originalImage.Orientation;
Debug.WriteLine("scaling image");
var originalImage1 = await Task.Run(() => ScaleAndRotateImage.ScaleAndRotateImageView(originalImage, OrIn));
if (originalImage1 != null)
{
var Data = originalImage1.AsJPEG(0.0f);
if (Data != null)
{
UIImage resizedImage = UIImage.LoadFromData(Data);
if (originalImage != null)
{
originalImage.Dispose();
originalImage = null;
}
originalImage1.Dispose();
originalImage1 = null;
Data.Dispose();
Data = null;
GC.Collect();
#if DEBUG
var Process1 = Process.GetCurrentProcess();
if (Process1 != null)
{
Debug.WriteLine(string.Format("memory allocated by FinishedPickingMedia = {0}", Process1.WorkingSet64));
}
#endif
camController.ShowPhoto(resizedImage);
}
if (info != null)
{
info.Dispose();
info = null;
}
GC.Collect();
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine("Exception Occured in FinishedPickingMedia method due to " + ex.Message);
}
finally
{
// dismiss the picker
picker.DismissModalViewController(true);
picker.Dispose();
picker = null;
GC.Collect();
}
}
This seams to happen randomly, my application needs to capture more than 200 photos (archive) I researched on the internet and this seams to be an issue with iOS 10 and this control
Related
I am taking a picture with the iOS camera and trying to extract metadata from the image. This is my code:-
partial void BtnCamera_TouchUpInside(UIButton sender)
{
UIImagePickerController imagePicker = new UIImagePickerController();
imagePicker.PrefersStatusBarHidden();
imagePicker.SourceType = UIImagePickerControllerSourceType.Camera;
// handle saving picture and extracting meta-data from picture //
imagePicker.FinishedPickingMedia += Handle_FinishedPickingMedia;
// present //
PresentViewController(imagePicker, true, () => { });
}
protected void Handle_FinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e)
{
try
{
// determine what was selected, video or image
bool isImage = false;
switch (e.Info[UIImagePickerController.MediaType].ToString())
{
case "public.image":
isImage = true;
break;
}
// get common info
NSUrl referenceURL = e.Info[new NSString("UIImagePickerControllerReferenceURL")] as NSUrl;
if (referenceURL != null)
Console.WriteLine("Url:" + referenceURL.ToString());
I am able to initiate the camera, take the picture and then however when I click 'use photo'... The referenceURL comes back as NULL... How can I get the url, such that to extract GPS coordinates of the photo and such other attributes ?
I had a tremendous amount of trouble with the URL. It can be a file, it can be a web url and it acts different on every device. My app crashed and burned so many times with my test group. I finally found a way to get the Metadata from the data. There are multiple ways to get the DateTaken, Width and Height as well as GPS Coordinates. In addition, I needed the Camera MFG and the Model.
string dateTaken = string.Empty;
string lat = string.Empty;
string lon = string.Empty;
string width = string.Empty;
string height = string.Empty;
string mfg = string.Empty;
string model = string.Empty;
PHImageManager.DefaultManager.RequestImageData(asset, options, (data, dataUti, orientation, info) => {
dateTaken = asset.CreationDate.ToString();
// GPS Coordinates
var coord = asset.Location?.Coordinate;
if (coord != null)
{
lat = asset.Location?.Coordinate.Latitude.ToString();
lon = asset.Location?.Coordinate.Longitude.ToString();
}
UIImage img = UIImage.LoadFromData(data);
if (img.CGImage != null)
{
width = img.CGImage?.Width.ToString();
height = img.CGImage?.Height.ToString();
}
using (CGImageSource imageSource = CGImageSource.FromData(data, null))
{
if (imageSource != null)
{
var ns = new NSDictionary();
var imageProperties = imageSource.CopyProperties(ns, 0);
if (imageProperties != null)
{
width = ReturnStringIfNull(imageProperties[CGImageProperties.PixelWidth]);
height = ReturnStringIfNull(imageProperties[CGImageProperties.PixelHeight]);
var tiff = imageProperties.ObjectForKey(CGImageProperties.TIFFDictionary) as NSDictionary;
if (tiff != null)
{
mfg = ReturnStringIfNull(tiff[CGImageProperties.TIFFMake]);
model = ReturnStringIfNull(tiff[CGImageProperties.TIFFModel]);
//dateTaken = ReturnStringIfNull(tiff[CGImageProperties.TIFFDateTime]);
}
}
}
}
}
}
The little helper function
private string ReturnStringIfNull(NSObject inObj)
{
if (inObj == null) return String.Empty;
return inObj.ToString();
}
You can request a PHAsset from the reference Url and that will contain some metadata. You can request the image data to obtain more.
Note: If you need full EXIF, you need to check to ensure the image on on the device (could be iCloud-based), download it if needed, and then load the image data with the ImageIO framework (lots of SO postings cover this).
public void ImagePicker_FinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e)
{
void ImageData(PHAsset asset)
{
if (asset == null) throw new Exception("PHAsset is null");
PHImageManager.DefaultManager.RequestImageData(asset, null, (data, dataUti, orientation, info) =>
{
Console.WriteLine(data);
Console.WriteLine(info);
});
}
PHAsset phAsset;
if (e.ReferenceUrl == null)
{
e.OriginalImage?.SaveToPhotosAlbum((image, error) =>
{
if (error == null)
{
var options = new PHFetchOptions
{
FetchLimit = 1,
SortDescriptors = new[] { new NSSortDescriptor("creationDate", true) }
};
phAsset = PHAsset.FetchAssets(options).FirstOrDefault() as PHAsset;
ImageData(phAsset);
}
});
}
else
{
phAsset = PHAsset.FetchAssets(new[] { e.ReferenceUrl }, null).FirstOrDefault() as PHAsset;
ImageData(phAsset);
}
}
Note: Make sure you have request runtime photo library authorization PHPhotoLibrary.RequestAuthorization) and have set the Privacy - Photo Library Usage Description string in your info.plist to avoid a nasty privacy crash
I am using the below code to share the files to other apps using xamarin iOS. but i didnt get any response. Is there any other option to share files to other apps using Xamarin iOS?
Ref: http://xamarinhelp.com/share-dialog-xamarin-forms/
public async Task Show(string title, string message, string filePath)
{
var items = new NSObject[] { NSObject.FromObject(title), NSUrl.FromFilename(filePath) };
var activityController = new UIActivityViewController(items, null);
var vc = GetVisibleViewController();
NSString[] excludedActivityTypes = null;
if (excludedActivityTypes != null && excludedActivityTypes.Length > 0)
activityController.ExcludedActivityTypes = excludedActivityTypes;
if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
if (activityController.PopoverPresentationController != null)
{
activityController.PopoverPresentationController.SourceView = vc.View;
}
}
await vc.PresentViewControllerAsync(activityController, true);
}
UIViewController GetVisibleViewController()
{
var rootController = UIApplication.SharedApplication.KeyWindow.RootViewController;
if (rootController.PresentedViewController == null)
return rootController;
if (rootController.PresentedViewController is UINavigationController)
{
return ((UINavigationController)rootController.PresentedViewController).TopViewController;
}
if (rootController.PresentedViewController is UITabBarController)
{
return ((UITabBarController)rootController.PresentedViewController).SelectedViewController;
}
return rootController.PresentedViewController;
}
I'm selecting an image form the gallery using the UIImagePickerController. After selecting an image, I would like to update the real image file path on a text field.
Can I get the file path from the referenceUrl? The referenceUrl in my case always returns null.
protected void Handle_FinishedPickingMedia (object sender, UIImagePickerMediaPickedEventArgs e)
{
try{
//determine what was selected, video or image
bool isImage = false;
switch(e.Info [UIImagePickerController.MediaType].ToString()) {
case "public.image":
Console.WriteLine("Image selected");
isImage = true;
break;
}
// get common info (shared between images and video)
NSUrl referenceURL = e.Info[new NSString("UIImagePickerControllerReferenceUrl")] as NSUrl;
if (referenceURL != null)
Console.WriteLine("Url:"+referenceURL.ToString ());
// if it was an image, get the other image info
if(isImage) {
// get the original image
UIImage originalImage = e.Info[UIImagePickerController.OriginalImage] as UIImage;
if(originalImage != null) {
// do something with the image
new Thread(new System.Threading.ThreadStart(() => {
Thread.Sleep(350);
BeginInvokeOnMainThread (() => {
var tag = ((UIButton)sender).Tag;
//UIButton senderButton = (UIButton)sender;
switch(tag)
{
case 0:
// do something here
break;
case 1:
// do something here
break;
});
})).Start();
}
}
// dismiss the picker
imagePicker.DismissModalViewController (true);
}catch(Exception ex)
{
ShowAlert ("Failed !", "Unable to select image", "");
Console.WriteLine(ex.Message + ex.StackTrace);
}
}
For anyone else facing this issue, the solution was a simple typo.I printed out the details of the NSDictionary and noticed that the "Url" part of the
UIImagePickerControllerReferenceUrl was all caps. This is what worked for me.
Change this line:
NSUrl referenceURL = e.Info[new NSString("UIImagePickerControllerReferenceUrl")] as NSUrl;
To this :
NSUrl referenceURL = e.Info[new NSString("UIImagePickerControllerReferenceURL")] as NSUrl;
To get the filename of the selected image, I added the AssestsLibrary and used that to extract the necessary metadata.
Here's my full implementation:
protected void Handle_FinishedPickingMedia (object sender, UIImagePickerMediaPickedEventArgs e)
{
try{
//determine what was selected, video or image
bool isImage = false;
switch(e.Info [UIImagePickerController.MediaType].ToString()) {
case "public.image":
Console.WriteLine("Image selected");
isImage = true;
break;
}
// get common info (shared between images and video)
NSUrl referenceURL = e.Info[new NSString("UIImagePickerControllerReferenceURL")] as NSUrl;
if (referenceURL != null)
Console.WriteLine("Url:"+referenceURL.ToString ());
ALAssetsLibrary assetsLibrary = new ALAssetsLibrary();
assetsLibrary.AssetForUrl(referenceURL,delegate (ALAsset asset){
ALAssetRepresentation representation = asset.DefaultRepresentation;
if (representation == null)
{
return;
}else{
string fileName = representation.Filename;
Console.WriteLine("Image Filename :" + fileName);
}
},delegate(NSError error) {
Console.WriteLine ("User denied access to photo Library... {0}", error);
});
// if it was an image, get the other image info
if(isImage) {
// get the original image
UIImage originalImage = e.Info[UIImagePickerController.OriginalImage] as UIImage;
if(originalImage != null) {
// do something with the image
new Thread(new System.Threading.ThreadStart(() => {
Thread.Sleep(350);
BeginInvokeOnMainThread (() => {
var tag = ((UIButton)sender).Tag;
//UIButton senderButton = (UIButton)sender;
switch(tag)
{
case 0:
// do something here
break;
case 1:
// do something here
break;
});
})).Start();
}
}
// dismiss the picker
imagePicker.DismissModalViewController (true);
}catch(Exception ex)
{
ShowAlert ("Failed !", "Unable to select image", "");
Console.WriteLine(ex.Message + ex.StackTrace);
}
}
I have a 3 GSM phones and a 3 verizon (CDMA) phones. I have a BB application in which the location listener is set to a 5 minute interval.
For 2 of the verizon phones the application's location update method gets called frequently. For the rest, the location listener gets called at a regular 5 minute interval.
What could be causing this difference in behavior?
public synchronized void locationUpdated(LocationProvider locationProvider, Location location) {
if (enabled) {
if (blackberryProvider != null) {
try {
constructCriteria(GPSInfo.GPS_MODE_CELLSITE);
gpsUpdate();
} catch (LocationException e) {
log stuff//
}
}
}
}
private void gpsUpdate() throws LocationException, InterruptedException {
try {
String gpsMode = null;
if (bbCriteria.getMode() == GPSInfo.GPS_MODE_CELLSITE) {
gpsMode = "cellsiteMode";
}
if (gpsMode == "cellsiteMode" && gpsMode.length() > 0 && bbProvider != null) {
// variable declaration
try {
bbLocation = (BlackBerryLocation) bbProvider.getLocation(10);
} catch (LocationException e) {
bbLocation = null;
}
if (bbLocation != null) {
// do stuff
// store location in the database
}
}
}
}
}
}
private void constructCriteria(final int mode) {
blackberryCriteria = null;
blackberryProvider = null;
blackberryCriteria = new BlackBerryCriteria();
blackberryCriteria.setSatelliteInfoRequired(true, false);
if (mode == GPSInfo.GPS_MODE_CELLSITE) {
setCriteraForCellSite();
}
try {
blackberryProvider = (BlackBerryLocationProvider) LocationProvider.getInstance(blackberryCriteria);
if (iLocationListner == null) {
iLocationListner = new ILocationListner();
blackberryProvider.setLocationListener(iLocationListner, locationInterval == 0 ? 300 : locationInterval, -1, -1);
} else {
blackberryProvider.setLocationListener(iLocationListner, locationInterval == 0 ? 300 : locationInterval, -1, -1);
}
} catch (LocationException lex) {
Logger.log("LocationEventSource constructor", lex);
return;
}
}
You are setting your criteria to update every 300 seconds if locationInterval == 0 or at the default rate (once per second) otherwise. Is this really what you want? Where is locationInterval initialized? How does its value change as the program runs?
My C# application is shooting an image every 3 minutes and I get the image from the EDSDK as expected every time. My problem is that the application is leaking about 5 mb for every shot and Iøm very sure that the problem is the EDSDK.
Code:
private uint CameraObjectEvent(uint inEvent, IntPtr inRef, IntPtr inContext)
{
switch (inEvent)
{
case EDSDK.ObjectEvent_DirItemRequestTransfer:
GetCapturedItem(inRef);
break;
}
return EDSDKErrorCodes.EDS_ERR_OK;
}
private void GetCapturedItem(IntPtr directoryItem)
{
uint error = EDSDKErrorCodes.EDS_ERR_OK;
IntPtr stream = IntPtr.Zero;
//Get information of the directory item.
EDSDK.EdsDirectoryItemInfo dirItemInfo;
error = EDSDK.EdsGetDirectoryItemInfo(directoryItem, out dirItemInfo);
if (error != EDSDKErrorCodes.EDS_ERR_OK)
{
OnCameraErrorRaised(error, "EDSDK.EdsGetDirectoryItemInfo failed.");
return;
}
//Create a file stream for receiving image.
error = EDSDK.EdsCreateMemoryStream(dirItemInfo.Size, out stream);
if (error != EDSDKErrorCodes.EDS_ERR_OK)
{
OnCameraErrorRaised(error, "EDSDK.EdsCreateMemoryStream failed");
return;
}
//Fill the stream with the resulting image
error = EDSDK.EdsDownload(directoryItem, dirItemInfo.Size, stream);
if (error != EDSDKErrorCodes.EDS_ERR_OK)
{
OnCameraErrorRaised(error, "EDSDK.EdsDownload failed.");
return;
}
error = EDSDK.EdsDownloadComplete(directoryItem);
if (error != EDSDKErrorCodes.EDS_ERR_OK)
{
OnCameraErrorRaised(error, "EDSDK.EdsDownloadComplete failed.");
return;
}
//Copy the stream to a byte[]
IntPtr pointerToBytes = IntPtr.Zero;
EDSDK.EdsGetPointer(stream, out pointerToBytes);
MemoryStream imageStream = null;
Image image = null;
try
{
byte[] buffer = new byte[dirItemInfo.Size];
GCHandle gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
Marshal.Copy(pointerToBytes, buffer, 0, (int)dirItemInfo.Size);
//Create a MemoryStream and return the image
imageStream = new MemoryStream(buffer);
image = Image.FromStream(imageStream);
}
catch (Exception ex)
{
OnCameraErrorRaised(999999, string.Format("Failed while retrieving image from camera. Exception: {0}.", ex.Message));
}
finally
{
if (imageStream != null)
imageStream.Dispose();
}
//If image was captured then send ImageCaptured event
if (image != null)
OnImageCaptured(image);
//Clean up
EDSDK.EdsRelease(pointerToBytes);
pointerToBytes = IntPtr.Zero;
EDSDK.EdsRelease(stream);
stream = IntPtr.Zero;
EDSDK.EdsRelease(directoryItem);
directoryItem = IntPtr.Zero;
}
The line OnImageCaptured(image) just sends the image to a controller which merges the image from the camera with another image and then disposes both images after saving the finale merged image:
private void ImageCaptured(Image originalImage)
{
Image watermark = null;
//Merge images
try
{
watermark = Image.FromFile(Settings.Default.ImageWatermarkFilename);
_imageController.Merge(originalImage, watermark);
_imageController.SaveImage(originalImage);
}
catch (Exception ex)
{
LogManager.Instance.UpdateLog(string.Format("Error - Failed to merge and save images. Exception: {0}.", ex.Message));
//HACK:
System.Windows.Forms.Application.Restart();
App.Current.Shutdown();
}
finally
{
originalImage.Dispose();
if (watermark != null)
watermark.Dispose();
}
}
So why does the app memory leak - any ideas?
/Cheers
release your GCHandle. it is the culprit eating memory every time when you are taking shoot
gcHandle.Free()