I'm about to loose it soon... I have two apps with working background tasks that are updating the live tile. The data for the live tile is downloaded, parsed and then an image is created dynamically and used as the background for the live tile.
Everything is working just fine for a day or two, but then the updating starts behaving very strange. The first one-two days both live tiles for my apps are updating every 28 minutes like clockwork. But then they start skipping updates. Often app A then updates when app B doesn't update the live tile so that they are not updating at the same time and only once an hour. To put it simple they are way off schedule.
This is really frustrating since I need to be able to rely on the tiles beeing updated every 30 minutes (if I have enough battery, good reception and so on).
I would really appreciate if someone could help me out and maybe take a look at my code to see if there might be something messing up the update interval (like not calling NotifyComplete correctly). I have removed some code and and tried to simplify it. Please ask if you need anything else to understand this.
I have been trying to fix this for the last two months, trying different phones and going throughmy code very carefully.
Your help is more appreciated than you can ever know.
Thanks in advance.
My OnInvoke function:
Timer t = null;
ShellToast toast = new ShellToast();
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri(strUrlHBH));
request.Accept = "*/*";
request.AllowAutoRedirect = true;
// disable caching.
request.Headers["Cache-Control"] = "no-cache";
request.Headers["Pragma"] = "no-cache";
t = new Timer(
state =>
{
if (string.Compare(state.ToString(), id, StringComparison.InvariantCultureIgnoreCase) == 0)
{
//logger.Write("Timeout reached for connection [{0}], aborting download.", id);
runNotifyComplete = false;
NotifyComplete();
request.Abort();
t.Dispose();
}
},
id,
timeout,
0);
request.BeginGetResponse(
r =>
{
try
{
if (t != null)
{
t.Dispose();
}
var httpRequest = (HttpWebRequest)r.AsyncState;
var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);
using (var reader = new StreamReader(httpResponse.GetResponseStream()))
{
var response = reader.ReadToEnd();
Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
{
try
{
//Parse the result
if (boolResult) //If we have a result
{
Grid grid = new Grid();
StackPanel sp = new StackPanel();
sp.Height = 173;
sp.Width = 173;
//StreamResourceInfo info = Application.GetResourceStream(new Uri("Pin-to-start2.png", UriKind.Relative));
if ((bool)settings["LiveTileMetro"] == true)
{
// create source bitmap for Image control (image is assumed to be alread 173x173)
/*WriteableBitmap wbmp2 = new WriteableBitmap(1, 1);
wbmp2.SetSource(info.Stream);
Image img = new Image();
img.Source = wbmp2;
// add Image to Grid
grid.Children.Add(img);
strBackBackground = "Pin-to-start2.png";
}
else
{*/
sp.Background = (SolidColorBrush)Application.Current.Resources["PhoneAccentBrush"];
//sp.Background.Opacity = 0.0;
strBackBackground = "";
}
StreamResourceInfo info;
//GC.Collect();
info = Application.GetResourceStream(new Uri("/MyApp;component/images/Icons/livetile/livetile.png", UriKind.Relative));
WriteableBitmap wbmp3 = new WriteableBitmap(1, 1);
try
{
wbmp3.SetSource(info.Stream);
}
catch
{
}
Image img3 = new Image();
img3.Source = wbmp3;
// add Image to Grid
img3.Width = 173;
img3.Height = 173;
img3.Margin = new Thickness { Left = 0, Bottom = 0, Right = 0, Top = 0 };
TextBlock txtTemperature = new TextBlock();
TextBlock txtTemperatureRing = new TextBlock();
txtTemperature.Foreground = new SolidColorBrush(Colors.White);
txtTemperature.Text = strTemp;
txtTemperature.TextAlignment = TextAlignment.Right;
txtTemperatureRing.Style = (Style)Application.Current.Resources["PhoneTextTitle3Style"];
txtTemperatureRing.FontFamily = new FontFamily("Segoe WP Light");
txtTemperatureRing.FontSize = 40;
txtTemperatureRing.Foreground = new SolidColorBrush(Colors.White);
txtTemperatureRing.Text = "°";
txtTemperatureRing.TextAlignment = TextAlignment.Right;
txtTemperature.FontFamily = new FontFamily("Segoe WP Light");
txtTemperature.FontSize = 60;
txtTemperature.Margin = new Thickness { Left = 0, Bottom = 0, Right = 0, Top = -75 };
txtTemperature.Height = 80;
txtTemperature.Width = 145;
txtTemperatureRing.Margin = new Thickness { Left = 128, Bottom = 0, Right = 0, Top = -97 };
txtTemperatureRing.Height = 50;
txtTemperatureRing.Width = 39;
sp.Children.Add(img3);
sp.Children.Add(txtTemperature);
sp.Children.Add(txtTemperatureRing);
//call measure, arrange and updatelayout to prepare for rendering
sp.Measure(new Size(173, 173));
sp.Arrange(new Rect(0, 0, 173, 173));
sp.UpdateLayout();
grid.Children.Add(sp);
WriteableBitmap wbmp = new WriteableBitmap(173, 173);
wbmp.Render(grid, null);
wbmp.Invalidate();
//write image to isolated storage
string sIsoStorePath = #"\Shared\ShellContent\tile.png";
using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
//ensure directory exists
String sDirectory = System.IO.Path.GetDirectoryName(sIsoStorePath);
if (!appStorage.DirectoryExists(sDirectory))
{
appStorage.CreateDirectory(sDirectory);
}
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(sIsoStorePath, System.IO.FileMode.Create, appStorage))
{
wbmp.SaveJpeg(stream, 173, 173, 0, 100);
}
}
/// If application uses both PeriodicTask and ResourceIntensiveTask
//ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("TileID=2"));
ShellTile TileToFind = ShellTile.ActiveTiles.First();
//test if Tile was created
if (TileToFind != null)
{
StandardTileData NewTileData = new StandardTileData
{
BackgroundImage = new Uri("isostore:Shared/ShellContent/tile.png", UriKind.Absolute),
Title = strTitle,
Count = null,
BackTitle = (string)settings["SelectedCityName"],
BackBackgroundImage = new Uri(strBackBackground, UriKind.Relative),
BackContent = strWind + Environment.NewLine + strPrecipitation
};
//ShellTile.Create(new Uri("/MainPage.xaml?TileID=2", UriKind.Relative), NewTileData);
TileToFind.Update(NewTileData);
}
}
if (runNotifyComplete == true)
{
runNotifyComplete = false;
NotifyComplete();
}
}//If matches.count
}
catch
{
if (runNotifyComplete == true)
{
runNotifyComplete = false;
NotifyComplete();
}
}
finally
{
if (runNotifyComplete == true)
{
runNotifyComplete = false;
NotifyComplete();
}
}
}));
}
if (runNotifyComplete == true)
{
runNotifyComplete = false;
NotifyComplete();
}
}
catch
{
// error handling.
if (runNotifyComplete == true)
{
runNotifyComplete = false;
NotifyComplete();
}
}
finally
{
}
},
request);
}
catch
{
if (runNotifyComplete == true)
{
runNotifyComplete = false;
NotifyComplete();
}
}
finally
{
}
EDIT 1: Here is the code for initializing the background task
MessageBox.Show(MyResources.LiveTileToggleMsgBoxText, "Live tile", MessageBoxButton.OK);
//start background agent
PeriodicTask periodicTask = new PeriodicTask("AppPeriodicAgent0440");
periodicTask.Description = "Periodic task for APP that updates the LiveTile .";
periodicTask.ExpirationTime = System.DateTime.Now.AddDays(14);
// If the agent is already registered with the system,
if (ScheduledActionService.Find(periodicTask.Name) != null)
{
ScheduledActionService.Remove("AppPeriodicAgent0440");
}
try
{
//only can be called when application is running in foreground
ScheduledActionService.Add(periodicTask);
}
catch
{
}
EDIT 2:
This code is run everytime I exit the application in order to keep the background task alive. I put it in OnNavigatedFrom to avoid slowing down the start up of the app
//start background agent
PeriodicTask periodicTask = new PeriodicTask("AppPeriodicAgent0440");
periodicTask.Description = "Periodic task for APP that updates the LiveTile.";
periodicTask.ExpirationTime = System.DateTime.Now.AddDays(14);
// If the agent is already registered with the system,
if (ScheduledActionService.Find(periodicTask.Name) != null)
{
ScheduledActionService.Remove("AppPeriodicAgent0440");
}
//only can be called when application is running in foreground
ScheduledActionService.Add(periodicTask);
If you read the Background Agents overview
http://msdn.microsoft.com/en-us/library/hh202942(v=vs.92).aspx you'll see that it details several scenarios where you schedule won't be followed. For example:
Battery Saver mode is an option that the user can enable on the device
to indicate that battery life should be prioritized. If this mode is
enabled, periodic agents may not run, even if the interval has elapsed
There is no guarantee that the scheduled task will run on the expected schedule. It even has a +/- 10 minute clause in the execution timeschedule.
From your question there is no information about how you're recording other events/tasks that may be happenening on the device at the time you're expecting a schedule to run.
In that you have no logging of errors, timeouts or checking of the LastExitReason of the task there could be all sorts of reasons for the behaviour you're seeing.
If I had to guess where the issue is, based on the information you have provided, I'd presume the problem is with the web request (either the network or the server) and an error in the request causes the UI to not be updated.
There is no way to guarantee that the tile will update on a predictable schedule. If you must absolutely have this updated for your circumstances/requirements then this platform isn't going to be able to provide that guarantee.
If you need multiple tiles to be kept in sync I'd approach this by having all the tiles updated by the same process.
This way, even if there is an inconsistency in the updating of the tiles from a time perspective they'll be in sync with each other.
Related
I am working on an application in C# using XAudio2 from SharpDX. I know its outdated but instead of CSCore it offers the better Reverb. The aim itself is simple: load a wav file (48khz,24bit,1channel), get it through the XAudio2 Reverb with several Reverb presets and do a playback.
I managed to implement everything for a normal playback and also built in the Reverb. The audio file will be played back with the defaul reverb settings but as soon as I change the preset or change a specific value (RoomFilterFreq) the file isnt audible anymore and i don´t know why this happens. Does anyone has a clue about this? I worked through the few examples which are still online but could not find a reason for this behaviour.
I´m coding under Visual Studio 2019 with Net 4.7.2. The code looks as followed:
xaudio2 = new XAudio2();
xaudio2.StartEngine();
var masteringVoice = new MasteringVoice(xaudio2, 1, 48000);
var stream = new SoundStream(File.OpenRead(soundFilepath));
SharpDX.Multimedia.WaveFormat waveFormat = stream.Format;
var buffer = new AudioBuffer
{
Stream = stream.ToDataStream(),
AudioBytes = (int)stream.Length,
Flags = BufferFlags.EndOfStream
};
stream.Close();
sourceVoice = new SourceVoice(xaudio2, waveFormat, true);
// Set Loop
if (f1.loopcheckBox.Checked == true)
{
buffer.LoopBegin = buffer.PlayBegin;
buffer.LoopLength = buffer.PlayLength;
buffer.LoopCount = AudioBuffer.LoopInfinite;
}
// Set Reverb
if (f1.reverbenableButton.BackColor == Color.LightGreen)
{
var reverb = new Reverb(xaudio2);
var reverbparameters = (ReverbParameters)ReverbI3DL2Parameters.Presets.Room;
var effectDescriptor = new EffectDescriptor(reverb);
if (waveFormat.Channels == 1)
{
effectDescriptor.OutputChannelCount = 1;
}
else if (waveFormat.Channels == 2)
{
effectDescriptor.OutputChannelCount = 2;
}
else
{
MessageBox.Show("Channelrate not supported!");
return sourceVoice = null;
}
sourceVoice.SetEffectChain(effectDescriptor);
sourceVoice.SetEffectParameters(0, reverbparameters);
sourceVoice.EnableEffect(0);
}
sourceVoice.SubmitSourceBuffer(buffer, stream.DecodedPacketsInfo);
sourceVoice.Start();
I am working on wpf application that uses GATT for bluetooth communication. I am using GattCharacteristic.ValueChanged to handle any value from device. It works for sometime and the event handler doesnt gets called after 1 minute. This problem observed in windows 8.1, but working in windows 10.
I am made GattCharacteristic member variable, so the GC doesnt collect the variable. Still the problem exists.
Can anyone tell me what could be the potential problem here?
private GattCharacteristic m_DataSenderCharacteristics;
foreach (var service in serviceInfo)
{
var uuid = MjGattDeviceService.UuidFromServiceInformation(service);
//Battery Service
if (uuid.ToString() == BatteryUUID)
{
var bService = await GattDeviceService.FromIdAsync(service.Id);
m_Device.batteryService = bService;
}
if (uuid.ToString() == CustomServiceUUID)
{
m_Device.gattService = await GattDeviceService.FromIdAsync(service.Id);
Guid controlGUID = new Guid(ControlUUID);
var controlGattCharacteristics = m_Device.gattService.GetCharacteristics(controlGUID);
Byte[] bsdata = new Byte[] { 0x0b, 0x00, 0x02 };
IBuffer buffUTF8 = CryptographicBuffer.CreateFromByteArray(bsdata);
GattCommunicationStatus status = await controlGattCharacteristics[0].WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
if (status == GattCommunicationStatus.Unreachable)
{
m_Device.isConnected = false;
nextPage = "Unreachable";
}
else if (status == GattCommunicationStatus.Success)
{
var ctrlStatus = await controlGattCharacteristics[0].WriteValueAsync(buffUTF8);
Guid guuid1 = new Guid(DataSenderUUID);
m_DataSenderCharacteristics = m_Device.gattService.GetCharacteristics(guuid1)[0];
GattCommunicationStatus notifyStatus = await m_DataSenderCharacteristics.
WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
//This event handler will be called for some 1 minute. After it falls dead
m_DataSenderCharacteristics[0].ValueChanged += GestureValueChanged;
if (notifyStatus == GattCommunicationStatus.Success)
{
nextPage = "Connected";
}
}
}
}
I used the example here to side load dwgs into memory combined with the example here of Plot From Model Space. I got things working except for the plots are not respecting whether the layers are frozen or not and printing as if they were all thawed. The data coming in from the dwg is correct. I can iterate the layers while debugging and verify the correct ones are either frozen or thawed. Also If I just saveas the dwg to a new name it matches the original concerning the layer state.Any Ideas?
[CommandMethod("PlotLayout")]
public static void PlotLayout()
{
// Get the current document and database, and start a transaction
Document acDoc = Active.Document;
Database acCurDb = Active.Database;
Editor ed = Active.Editor;
var collection = new List<string>() { "C:\\Test\\440001A.dwg", "C:\\Test\\440001B.dwg", "C:\\Test\\440001C.dwg", "C:\\Test\\440001D.dwg" };
for (int i = 0; i < collection.Count; i++)
{
var dwg = collection[i];
var dir = Path.GetDirectoryName(dwg);
var fn = Path.GetFileNameWithoutExtension(dwg);
var filePath = Path.Combine(dir, fn + "-" + i.ToString() + ".pdf");
Database oldDb = HostApplicationServices.WorkingDatabase;
using (Database db = new Database(false, true))
{
db.ReadDwgFile(dwg, FileOpenMode.OpenForReadAndAllShare, false, null);
db.CloseInput(true);
using (Transaction acTrans = db.TransactionManager.StartTransaction())
try
{
HostApplicationServices.WorkingDatabase = db;
LayoutManager acLayoutMgr = LayoutManager.Current;
// Get the current layout and output its name in the Command Line window
Layout acLayout = acTrans.GetObject(acLayoutMgr.GetLayoutId(acLayoutMgr.CurrentLayout),
OpenMode.ForRead) as Layout;
// Get the PlotInfo from the layout
using (PlotInfo acPlInfo = new PlotInfo())
{
acPlInfo.Layout = acLayout.ObjectId;
// Get a copy of the PlotSettings from the layout
using (PlotSettings acPlSet = new PlotSettings(acLayout.ModelType))
{
acPlSet.CopyFrom(acLayout);
// Update the PlotSettings object
PlotSettingsValidator acPlSetVdr = PlotSettingsValidator.Current;
// Set the plot type
acPlSetVdr.SetPlotType(acPlSet, Autodesk.AutoCAD.DatabaseServices.PlotType.Extents);
// Set the plot scale
acPlSetVdr.SetUseStandardScale(acPlSet, true);
acPlSetVdr.SetStdScaleType(acPlSet, StdScaleType.ScaleToFit);
// Center the plot
acPlSetVdr.SetPlotCentered(acPlSet, true);
// Set the plot device to use
acPlSetVdr.SetPlotConfigurationName(acPlSet, "DWF6 ePlot.pc3", "ANSI_B_(11.00_x_17.00_Inches)");
// Set the plot info as an override since it will
// not be saved back to the layout
acPlInfo.OverrideSettings = acPlSet;
// Validate the plot info
using (PlotInfoValidator acPlInfoVdr = new PlotInfoValidator())
{
acPlInfoVdr.MediaMatchingPolicy = MatchingPolicy.MatchEnabled;
acPlInfoVdr.Validate(acPlInfo);
// Check to see if a plot is already in progress
if (PlotFactory.ProcessPlotState == ProcessPlotState.NotPlotting)
{
using (PlotEngine acPlEng = PlotFactory.CreatePublishEngine())
{
// Track the plot progress with a Progress dialog
using (PlotProgressDialog acPlProgDlg = new PlotProgressDialog(false, 1, true))
{
using ((acPlProgDlg))
{
// Define the status messages to display
// when plotting starts
acPlProgDlg.set_PlotMsgString(PlotMessageIndex.DialogTitle, "Plot Progress");
acPlProgDlg.set_PlotMsgString(PlotMessageIndex.CancelJobButtonMessage, "Cancel Job");
acPlProgDlg.set_PlotMsgString(PlotMessageIndex.CancelSheetButtonMessage, "Cancel Sheet");
acPlProgDlg.set_PlotMsgString(PlotMessageIndex.SheetSetProgressCaption, "Sheet Set Progress");
acPlProgDlg.set_PlotMsgString(PlotMessageIndex.SheetProgressCaption, "Sheet Progress");
// Set the plot progress range
acPlProgDlg.LowerPlotProgressRange = 0;
acPlProgDlg.UpperPlotProgressRange = 100;
acPlProgDlg.PlotProgressPos = 0;
// Display the Progress dialog
acPlProgDlg.OnBeginPlot();
acPlProgDlg.IsVisible = true;
// Start to plot the layout
acPlEng.BeginPlot(acPlProgDlg, null);
// Define the plot output
acPlEng.BeginDocument(acPlInfo, acDoc.Name, null, 1, true, "c:\\myplot");
// Display information about the current plot
acPlProgDlg.set_PlotMsgString(PlotMessageIndex.Status, "Plotting: " + acDoc.Name + " - " + acLayout.LayoutName);
// Set the sheet progress range
acPlProgDlg.OnBeginSheet();
acPlProgDlg.LowerSheetProgressRange = 0;
acPlProgDlg.UpperSheetProgressRange = 100;
acPlProgDlg.SheetProgressPos = 0;
// Plot the first sheet/layout
using (PlotPageInfo acPlPageInfo = new PlotPageInfo())
{
acPlEng.BeginPage(acPlPageInfo, acPlInfo, true, null);
}
acPlEng.BeginGenerateGraphics(null);
acPlEng.EndGenerateGraphics(null);
// Finish plotting the sheet/layout
acPlEng.EndPage(null);
acPlProgDlg.SheetProgressPos = 100;
acPlProgDlg.OnEndSheet();
// Finish plotting the document
acPlEng.EndDocument(null);
// Finish the plot
acPlProgDlg.PlotProgressPos = 100;
acPlProgDlg.OnEndPlot();
acPlEng.EndPlot(null);
}
}
}
}
}
}
}
}
catch (System.Exception ex)
{
ed.WriteMessage(ex.ToString());
}
HostApplicationServices.WorkingDatabase = oldDb;
}
}
}
Not ideal but I found a work around. If I iterate the LayerTable, check for frozen then make IsPlottable false it prints correctly. If a better soultion is available please let me know. Thanks
LayerTable _LayerTable = acTrans.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable;
foreach (ObjectId _LayerTableId in _LayerTable)
{
LayerTableRecord _LayerTableRecord =
(LayerTableRecord)acTrans.GetObject(_LayerTableId,
OpenMode.ForWrite);
if (_LayerTableRecord.IsFrozen)
{
_LayerTableRecord.IsPlottable = false;
}
}
I'm using ZXing.net to create a UserControl for scanning barcode into a Windows Phone 8.1 RT app using the camera.
The barcode are decoded well but i'm having freezes on the UI when the method CapturePhotoToStreamAsync is called, even it is awaited.
It takes about 600 ms to execute.
I'm testing the app into the emulator.
The code below is executed in an async method :
// Preview of the camera
await _mediaCapture.InitializeAsync(settings);
VideoCapture.Source = _mediaCapture;
VideoCapture.FlowDirection = Windows.UI.Xaml.FlowDirection.LeftToRight;
await _mediaCapture.StartPreviewAsync();
VideoEncodingProperties res = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview) as VideoEncodingProperties;
ImageEncodingProperties iep = ImageEncodingProperties.CreateBmp();
iep.Height = res.Height;
iep.Width = res.Width;
var barcodeReader = new BarcodeReader
{
TryHarder = true,
AutoRotate = true
};
WriteableBitmap wB = new WriteableBitmap((int)res.Width, (int)res.Height);
while (_result == null)
{
using (var stream = new InMemoryRandomAccessStream())
{
await _mediaCapture.CapturePhotoToStreamAsync(iep, stream);
stream.Seek(0);
await wB.SetSourceAsync(stream);
_result = barcodeReader.Decode(wB);
}
}
await _mediaCapture.StopPreviewAsync();
//callback to handle result
ScanCallback(_result.Text);
What can I do to prevent the UI from freezing ?
Luckily you don't have to capture photo to decode QRCode/Barcode on Windows Phone 8.1 Runtime. It is a quite new solution actually, but it works: https://github.com/mmaitre314/VideoEffect#realtime-video-analysis-and-qr-code-detection
After istalling the nuget package, you can easily decode barcodes realtime, without the need of calling CapturePhotoToStreamAsync. The only drawback is that you can only target ARM. You can find the sample code on the site. Or you can contact me, and I can send you the part of my project where I use this.
i always get better results when i use the camera to take a picture first (lets you focus on the correct place where the barcode is) and then send the picture for barcode recognition.
the stuttering is caused because you try to keep checking the live feed for barcodes which can be hard on the CPU (especially for ARM devices)
var dialog = new CameraCaptureUI();
StorageFile file = await dialog.CaptureFileAsync(CameraCaptureUIMode.Photo);
var stream = await file.OpenReadAsync();
// initialize with 1,1 to get the current size of the image
var writeableBmp = new WriteableBitmap(1, 1);
writeableBmp.SetSource(stream);
// and create it again because otherwise the WB isn't fully initialized and decoding
// results in a IndexOutOfRange
writeableBmp = new WriteableBitmap(writeableBmp.PixelWidth, writeableBmp.PixelHeight);
stream.Seek(0);
writeableBmp.SetSource(stream);
var result = ScanBitmap(writeableBmp);
string barcode = "";
if (result != null)
{
barcode = result.Text;
}
and here the ScanBitmap method:
private Result ScanBitmap(WriteableBitmap writeableBmp)
{
var barcodeReader = new BarcodeReader
{
Options = new ZXing.Common.DecodingOptions()
{
TryHarder = true
},
AutoRotate = true
};
var result = barcodeReader.Decode(writeableBmp);
if (result != null)
{
CaptureImage.Source = writeableBmp;
}
return result;
}
I have the following code:
var lstMusicInfo = new List<MediaFile>();
var LocalMusic = Directory.EnumerateFiles(AppSettings.Default.ComputerMusicFolder, "*.*", SearchOption.AllDirectories).AsParallel().ToList<string>();
LocalMusic = (from a in LocalMusic.AsParallel()
where a.EndsWith(".mp3") || a.EndsWith(".wma")
select a).ToList<string>();
var DeviceMusic = adb.SyncMedia(this.dev, AppSettings.Default.ComputerMusicFolder, 1);
Parallel.ForEach(LocalMusic, new Action<string>(item =>
{
try
{
UltraID3 mFile = new UltraID3();
FileInfo fInfo;
mFile.Read(item);
fInfo = new FileInfo(item);
bool onDevice = true;
if (DeviceMusic.Contains(item))
{
onDevice = false;
}
// My Problem starts here
lstMusicInfo.Add(new MediaFile()
{
Title = mFile.Title,
Album = mFile.Album,
Year = mFile.Year.ToString(),
ComDirectory = fInfo.Directory.FullName,
FileFullName = fInfo.FullName,
Artist = mFile.Artist,
OnDevice = onDevice,
PTDevice = false
});
//Ends here.
}
catch (Exception) { }
}));
this.Dispatcher.BeginInvoke(new Action(() =>
{
lstViewMusicFiles.ItemsSource = lstMusicInfo;
blkMusicStatus.Text = "";
doneLoading = true;
}));
#endregion
}));
The first part of the code gives me almost instant result containing:
Address on computer of 5780 files.
Get list of all music files on an android device compare it with those 5780 files and return a list of files found on computer but not on device (in my case it returns a list with 5118 items).
The block of code below is my problem, I am filling data into a class, then adding that class into a List<T>, doing it for 5780 times takes 60 seconds, how can I improve it?
// My Problem starts here
lstMusicInfo.Add(new MediaFile
{
Title = mFile.Title,
Album = mFile.Album,
Year = mFile.Year.ToString(),
ComDirectory = fInfo.Directory.FullName,
FileFullName = fInfo.FullName,
Artist = mFile.Artist,
OnDevice = onDevice,
PTDevice = false
});
//Ends here.
Update:
Here is the profiling result and I see it's obvious why it's slowing down >_>
I suppose I should look for a different library that reads music file information.
One way to avoid loading everything once, up front, would be to lazy load the ID3 information as necessary.
You'd construct your MediaFile instances thus...
new MediaFile(filePath)
...and MediaFile would look something like the following.
internal sealed class MediaFile
{
private readonly Lazy<UltraID3> _lazyFile;
public MediaFile(string filePath)
{
_lazyFile = new Lazy<UltraID3>(() =>
{
var file = new UltraID3();
file.Read(filePath);
return file;
});
}
public string Title
{
get { return _lazyFile.Value.Title; }
}
// ...
}
This is possibly less ideal than loading them as fast as you can in the background, if you do something like MediaFiles.OrderBy(x => x.Title).ToList() and nothing has been lazy loaded then you'll have to wait for every file to load.
Loading them in the background would make them available for use immediately after the background loading has finished. But you might have to concern yourself with not accessing some items until the background loading has finished.
You biggest bottleneck is new FileInfo(item), but you don't need FileInfo just to get the Directory and File names. You can use Path.GetDirectoryName and Path.GetFileName, which are must faster since no I/O is involved.
UltraID3 mFile = new UltraID3();
//FileInfo fInfo;
mFile.Read(item);
//fInfo = new FileInfo(item);
bool onDevice = true;
if (DeviceMusic.Contains(item))
{
onDevice = false;
}
// My Problem starts here
lstMusicInfo.Add(new MediaFile()
{
Title = mFile.Title,
Album = mFile.Album,
Year = mFile.Year.ToString(),
ComDirectory = Path.GetDirectoryName(item), // fInfo.Directory.FullName,
FileFullName = Path.GetFileName(item), //fInfo.FullName,
Artist = mFile.Artist,
OnDevice = onDevice,
PTDevice = false
});
//Ends here.