Reputation: 7188
I was surprised that convenient System.Threading.Timer class does not exist in Profile 78 libraries. To use this class I created another PCL which targets 4.0 framework and wrote a simple wrapper around (as it was suggested in one blog post):
public class PCLTimer
{
private Timer timer;
private Action<object> action;
public PCLTimer (Action<object> action, object state, int dueTimeMilliseconds, int periodMilliseconds)
{
this.action = action;
timer = new Timer (PCLTimerCallback, state, dueTimeMilliseconds, periodMilliseconds);
}
private void PCLTimerCallback (object state)
{
action.Invoke (state);
}
public bool Change (int dueTimeMilliseconds, int periodMilliseconds)
{
return timer.Change (dueTimeMilliseconds, periodMilliseconds);
}
}
Now I can reference this 4.0 library and use PCLTimer in main PCL library. But when I try to build my main Android project I get following warnings:
Warning CS1684: Reference to type 'System.Threading.Timer' claims it is defined in 'c:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.5\Profile\Profile78\mscorlib.dll', but it could not be found (CS1684) (Prototype.Core)
Warning MSB3247: Found conflicts between different versions of the same dependent assembly. (MSB3247) (Prototype.Droid)
How to get rid of these warnings properly?
Upvotes: 3
Views: 1505
Reputation: 856
Here is a complete re-implementation of the Timer class that temporarily disappeared in Profile 78, using asynchronous tasks:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Quantum
{
public delegate void TimerCallback(object state);
public sealed class Timer : IDisposable
{
private static Task CompletedTask = Task.FromResult(false);
private TimerCallback Callback;
private Task Delay;
private bool Disposed;
private int Period;
private object State;
private CancellationTokenSource TokenSource;
public Timer(TimerCallback callback, object state, int dueTime, int period)
{
Callback = callback;
State = state;
Period = period;
Reset(dueTime);
}
public Timer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
: this(callback, state, (int)dueTime.TotalMilliseconds, (int)period.TotalMilliseconds)
{
}
~Timer()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool cleanUpManagedObjects)
{
if (cleanUpManagedObjects)
Cancel();
Disposed = true;
}
public void Change(int dueTime, int period)
{
Period = period;
Reset(dueTime);
}
public void Change(TimeSpan dueTime, TimeSpan period)
{
Change((int)dueTime.TotalMilliseconds, (int)period.TotalMilliseconds);
}
private void Reset(int due)
{
Cancel();
if (due >= 0)
{
TokenSource = new CancellationTokenSource();
Action tick = null;
tick = () =>
{
Task.Run(() => Callback(State));
if (!Disposed && Period >= 0)
{
if (Period > 0)
Delay = Task.Delay(Period, TokenSource.Token);
else
Delay = CompletedTask;
Delay.ContinueWith(t => tick(), TokenSource.Token);
}
};
if (due > 0)
Delay = Task.Delay(due, TokenSource.Token);
else
Delay = CompletedTask;
Delay.ContinueWith(t => tick(), TokenSource.Token);
}
}
private void Cancel()
{
if (TokenSource != null)
{
TokenSource.Cancel();
TokenSource.Dispose();
TokenSource = null;
}
}
}
}
Upvotes: 4
Reputation: 71
Instead of putting the Timer wrapper implementation into a separate .net 4.0 project, I have solved this issue a different way:
I created an ITimerWrapper interface in the core project, and put sepearate implementations in the Droid and WinPhone projects. Then I use IoC to set up the required implementation at app start.
There is no conflict between the different PCL versions of Timer using this method.
Another advantage is I can now use DispatcherTimers in the WinPhone project - I could only use System.Threading.Timers when I was sharing the code between Android and WinPhone.
Upvotes: 1
Reputation: 4652
Do you need to add a binding to the app.config? I had to do something similar for HttpClient when I added an WP8 project.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Net.Http"
publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="2.0.5.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Upvotes: 3