I am trying to add a delay before proceeding to doing the next thing. Here is my code :
private void startup() {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
Platform.runLater(() -> {
mainPane.setBackground(new Background(new BackgroundFill(Color.rgb(28, 28, 28), null, null)));
logPane.setBackground(new Background(new BackgroundFill(Color.rgb(28, 28, 28), null, null)));
startupAnimation();
});
}
}, 3500);
logMessage(Mavis.LOG_INITIALIZE);
private void startupAnimation() {
FadeTransition ftMain = new FadeTransition(Duration.millis(1200), mainPane);
ftMain.setFromValue(0);
ftMain.setToValue(1);
ftMain.setCycleCount(1);
FadeTransition ftLog = new FadeTransition(Duration.millis(1200), logPane);
ftLog.setFromValue(0);
ftLog.setToValue(1);
ftLog.setCycleCount(1);
ParallelTransition pt = new ParallelTransition();
pt.getChildren().addAll(ftMain, ftLog);
TranslateTransition tt = new TranslateTransition(Duration.millis(800), mavisImgView);
tt.setByX(-210);
tt.setCycleCount(1);
FadeTransition ftOver = new FadeTransition(Duration.millis(1500), overlayPane);
ftOver.setFromValue(0);
ftOver.setToValue(1);
ftOver.setCycleCount(1);
SequentialTransition seq = new SequentialTransition(pt,tt, ftOver);
seq.play();
}
private void logMessage(String msg) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
Platform.runLater(() -> {
logTxtArea.setText(msg);
chatBalloon.setVisible(true);
});
}
}, 1500);
}
What I want to achieve is that, before logging the message the GUI has to startup for 3.5 seconds, then when the GUI is loaded, it will be logging a message. The problem is that the message is already logged along with the startup of the GUI, what seems to be the problem here? The timer used is java.util.Timer.
For initial delay use PauseTransition then add it at the beginning of SequentialTransition.
Related
Here is, what I'm trying to do:
A Switch is turned on, starting a service in another thread (works fine so far)
When this service is successful, it should then start another function within the main thread
I don't mind whether the function is called directly by the service or the service is returning a "success"-value to the main thread, what then starts the next function from there.
Here is, what the important parts of the code looks like:
Main thread:
class SendNotif : AppCompatActivity() {
val context = this
private lateinit var Switch: Switch
// Start LocationService when the switch is on
Switch.setOnCheckedChangeListener { buttonView, isChecked ->
if (isChecked) {
Toast.makeText(context, "Starting LocationService", Toast.LENGTH_SHORT).show()
Intent(applicationContext, LocationService::class.java).apply {
action = LocationService.ACTION_START
startService(this)
}
} else {
Toast.makeText(context, "Stopping LocationService", Toast.LENGTH_SHORT).show()
Intent(applicationContext, LocationService::class.java).apply {
action = LocationService.ACTION_STOP
startService(this)
}
}
}
}
fun InitiateMessage() {
// This is the function, that is supposed to start after the LocationService
}
}
This is the LocationService. After being successful, the function InitiateMessage() should start.
class LocationService: Service() {
private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private lateinit var locationClient: LocationClient
var lat = 0.0F
var long = 0.0F
override fun onBind(p0: Intent?): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
locationClient = DefaultLocationClient(
applicationContext,
LocationServices.getFusedLocationProviderClient(applicationContext)
)
}
// Start or stop the service
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when(intent?.action) {
ACTION_START -> start()
ACTION_STOP -> stop()
}
return super.onStartCommand(intent, flags, startId)
}
private fun start() {
// Starting notification
val notification = NotificationCompat.Builder(this, "location")
.setContentTitle("Tracking location...")
.setContentText("Location: null")
.setSmallIcon(R.drawable.ic_launcher_background)
// Can't swipe this notification away
.setOngoing(true)
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Starting the location updates
locationClient
// Every 10 seconds
.getLocationUpdates(10000L)
.catch { e -> e.printStackTrace() }
.onEach { location ->
lat = location.latitude.toString().toFloat() // .takeLast(3) // taking only the last 3 digits
long = location.longitude.toString().toFloat() // .takeLast(3)
val updatedNotification = notification.setContentText(
"Location: ($lat, $long)"
)
// notificationManager.notify(1, updatedNotification.build())
// Geofence
MyGeofence(lat, long)
}
.launchIn(serviceScope)
// startForeground(1, notification.build())
}
private fun stop() {
// Stopping the notification
stopForeground(true)
// Stopping the location service
stopSelf()
}
override fun onDestroy() {
super.onDestroy()
serviceScope.cancel()
}
companion object {
const val ACTION_START = "ACTION_START"
const val ACTION_STOP = "ACTION_STOP"
}
fun MyGeofence(lat : Float, long : Float){
val context = this
var db = DataBaseHandler(context)
var data = db.readData()
// Setting the accuracy of the geofence
val acc = 2
val safelat : Double = data.get(0).LocLat.toFloat().round(acc)
val safelong = data.get(0).LocLong.toFloat().round(acc) // .take(acc).take(acc)
val h = Handler(context.mainLooper)
if(safelat == lat.toFloat().round(acc) && safelong == long.toFloat().round(acc)){
h.post(Runnable { Toast.makeText(context, "You have reached your safe refuge! " + lat.toFloat().round(acc) + " " + long.toFloat().round(acc), Toast.LENGTH_LONG).show() })
// ToDo: Right hereafter the function InitiateMessage() should start
}
else{
h.post(Runnable { Toast.makeText(context, "You are still in great danger! " + lat.toFloat().round(acc) + " " + long.toFloat().round(acc), Toast.LENGTH_LONG).show() })
}
}
fun Float.round(decimals: Int): Double {
var multiplier = 1.0
repeat(decimals) { multiplier *= 10 }
return round(this * multiplier) / multiplier
}
}
So far, I tried it with a Looper, which did not work.
java.lang.RuntimeException: Can't create handler inside thread Thread[DefaultDispatcher-worker-1,5,main] that has not called Looper.prepare()
But I guess the far easier way would be a returned value by the service. How do I implement this, and how do I start the next function through this returned value?
I solved my problem with an observe-function and a companion object, that is a MutableLiveData.
The companion object is placed inside the main thread:
companion object {
// var iamsafe: Boolean = false
val iamsafe: MutableLiveData<Boolean> by lazy {
MutableLiveData<Boolean>()
}
}
The observe-function is placed within onCreate:
val safeObserver = Observer<Boolean> { newState ->
Toast.makeText(context, "Initiating message to my mate.", Toast.LENGTH_SHORT).show()
InitiateMessage()
}
iamsafe.observe(this, safeObserver)
The companion is changed in the second thread like this:
SendNotif.iamsafe.postValue (true)
I am moving a point every so often, the problem is that to keep the point inside the map and not get lost as it moves, I have to reload the map. How could you avoid recharging it, since the movement occurs every two seconds and the map is reloaded every two seconds is too uncomfortable.
Here the code:
cont++;
final long EXECUTION_TIME = 2000;
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
int aux = 0;
#Override
public void run() {
GraphicsOverlay graphicsOverlay1 = new GraphicsOverlay();
Graphic g1 = new Graphic(getLatLong(aux), attributes, sms);
graphicsOverlay1.getGraphics().add(g1);
mMap.getGraphicsOverlays().add(graphicsOverlay1);
map = new ArcGISMap(basemapType, getLatLong(aux).getY(), getLatLong(aux).getX(), 17);
mMap.setMap(map); //Here is where the map is reloaded, some other way to avoid this burden
handler.postDelayed(this, EXECUTION_TIME);
}
)};
You must use the method: SetViewpointCenterAsync in your mMap and thus avoid loading the map when updating points on the map.
The code would look like this:
map = new ArcGISMap(basemapType, getLatLong(aux).getY(), getLatLong(aux).getX(), 17);
mMap.setMap(map);
cont++;
final long EXECUTION_TIME = 2000;
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
int aux = 0;
#Override
public void run() {
GraphicsOverlay graphicsOverlay1 = new GraphicsOverlay();
Graphic g1 = new Graphic(getLatLong(aux), attributes, sms);
graphicsOverlay1.getGraphics().add(g1);
mMap.getGraphicsOverlays().add(graphicsOverlay1);
mMap.setViewpointCenterAsync(new Point( getLatLong(aux).getX(), getLatLong(aux).getY(),SpatialReferences.getWgs84()),6000.0) ;
handler.postDelayed(this, EXECUTION_TIME); } )};
I have a longish running operation that I want to run synchronously (I will look at async later on).
I am using the overlay code from here - http://docs.xamarin.com/recipes/ios/standard_controls/popovers/display_a_loading_message
Here is my test code -
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
loadingOverlay = new LoadingOverlay (UIScreen.MainScreen.Bounds);
View.Add (loadingOverlay);
int i = 0;
while (i < 5000)
{
Console.WriteLine(i);
i++;
}
loadingOverlay.Hide ();
}
But the initial overlay does not show, only the Hide animation after the while loop completes?
I can produce a hack / workaround like so -
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
loadingOverlay = new LoadingOverlay (UIScreen.MainScreen.Bounds);
View.Add (loadingOverlay);
NSTimer nstimer = NSTimer.CreateScheduledTimer (1, () => {
int i = 0;
while (i < 5000) {
Console.WriteLine (i);
i++;
}
loadingOverlay.Hide ();
});
}
But could someone please explain to me why this is happening, and how to write the code correctly so IOS draws the overlay to screen before it begins the loop.
You're blocking the UI thread, which you should never do. Any long running operation should run in the background thread, even if you want to process it in a synchronous way.
With Xamarin.iOS alpha (supporting async/await), your code would look like this:
public async override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
loadingOverlay = new LoadingOverlay (UIScreen.MainScreen.Bounds);
View.Add (loadingOverlay);
await Task.Factory.StartNew (() => {
int i = 0;
while (i < 5000)
{
Console.WriteLine(i);
i++;
}
});
loadingOverlay.Hide ();
}
If you don't have access to async/await, you can achieve the same result with:
Task.Factory.StartNew (() => {
int i = 0;
while (i < 5000)
{
Console.WriteLine(i);
i++;
}
}).ContinueWith(task => InvokeOnMainThread(() => { loadingOverlay.Hide(); }));
but it's less nice (also, check the closing braces, I just typed this code)
Add this Component to your application and be done with it. No need to re-invent the wheel.
I chose to return Task<T> and Task from my objects methods to provide easy consumation by the gui. Some of the methods simply wait for mutex of other kind of waithandles . Is there a way to construct Task from WaitHandle.Wait() so that I don't have to block one treadpool thread for that.
There is a way to do this: you can subscribe to WaitHandle using ThreadPool.RegisterWaitForSingleObject method and wrap it via TaskCompletionSource class:
public static class WaitHandleEx
{
public static Task ToTask(this WaitHandle waitHandle)
{
var tcs = new TaskCompletionSource<object>();
// Registering callback to wait till WaitHandle changes its state
ThreadPool.RegisterWaitForSingleObject(
waitObject: waitHandle,
callBack:(o, timeout) => { tcs.SetResult(null); },
state: null,
timeout: TimeSpan.MaxValue,
executeOnlyOnce: true);
return tcs.Task;
}
}
Usage:
WaitHandle wh = new AutoResetEvent(true);
var task = wh.ToTask();
task.Wait();
As noted by #gordy in the comments of the accepted answer of Sergey Teplyakov, MSDN proposes an implementation with unsubscription of the registered WaitHandle.
I slightly modified it here to support the result of the callback: if the registration has timed out, the task return false. If the signal has been received, the task return true:
public static class ExtensionMethods
{
public static Task<bool> WaitOneAsync(this WaitHandle waitHandle, int timeoutMs)
{
if (waitHandle == null)
throw new ArgumentNullException(nameof(waitHandle));
var tcs = new TaskCompletionSource<bool>();
RegisteredWaitHandle registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject(
waitHandle,
callBack: (state, timedOut) => { tcs.TrySetResult(!timedOut); },
state: null,
millisecondsTimeOutInterval: timeoutMs,
executeOnlyOnce: true);
return tcs.Task.ContinueWith((antecedent) =>
{
registeredWaitHandle.Unregister(waitObject: null);
try
{
return antecedent.Result;
}
catch
{
return false;
}
});
}
}
Usage is same as the original answer:
WaitHandle signal = new AutoResetEvent(initialState: false);
bool signaled = await signal.WaitOneAsync(1000);
if (signaled)
{
Console.WriteLine("Signal received");
}
else
{
Console.WriteLine("Waiting signal timed out");
}
My somewhat data-intensive wp7 app persists data as follows: I maintain a change journal reflecting all user activity, and every couple of seconds, a thread timer spins up a threadpool thread that flushes the change journal to a database inside a transaction. It looks something like this:
When the user exits, I stop the timer, flush the journal on the UI thread (takes no more than a second or two), and dismount the DB.
However, if the worker thread is active when the user exits, I can't figure out how to react gracefully. The system seems to kill the worker thread, so it never finishes its work and never gives up its lock on the database connection, and the ui thread then attempts to acquire the lock, and is immediately killed by the system. I tried setting a flag on the UI thread requesting the worker to abort, but I think the worker was interrupted before it read the flag. Everything works fine except for this 1 in 100 scenario where some user changes end up not being saved to the db, and I can't seem to get around this.
Very simplified code below:
private Timer _SweepTimer = new Timer(SweepCallback, null, 5000, 5000);
private volatile bool _BailOut = false;
private void SweepCallback(object state) {
lock (db) {
db.startTransaction();
foreach(var entry in changeJournal){
//CRUD entry as appropriate
if(_BailOut){
db.rollbackTransaction();
return;
}
}
db.endTransaction();
changeJournal.Clear();
}
}
private void RespondToSystemExit(){
_BailOut = true; //Set flag for worker to exit
lock(db){ //In theory, should acquire the lock after the bg thread bails out
SweepCallback(null);//Flush to db on the UI thread
db.dismount();//App is now ready to close
}
}
Well, just to close this question, I ended up using a manualresetevent instead of the locking, which is to the best of my understanding a misuse of the manualresetevent, risky and hacky, but its better than nothing.
I still don't know why my original code wasn't working.
EDIT: For posterity, I'm reposting the code to reproduce this from the MS forums:
//This is a functioning console app showing the code working as it should. Press "w" and then "i" to start and then interrupt the worker
using System;
using System.Threading;
namespace deadlocktest {
class Program {
static void Main(string[] args) {
var tester = new ThreadTest();
string input = "";
while (!input.Equals("x")) {
input = Console.ReadLine();
switch (input) {
case "w":
tester.StartWorker();
break;
case "i":
tester.Interrupt();
break;
default:
return;
}
}
}
}
class ThreadTest{
private Object lockObj = new Object();
private volatile bool WorkerCancel = false;
public void StartWorker(){
ThreadPool.QueueUserWorkItem((obj) => {
if (Monitor.TryEnter(lockObj)) {
try {
Log("Worker acquired the lock");
for (int x = 0; x < 10; x++) {
Thread.Sleep(1200);
Log("Worker: tick" + x.ToString());
if (WorkerCancel) {
Log("Worker received exit signal, exiting");
WorkerCancel = false;
break;
}
}
} finally {
Monitor.Exit(lockObj);
Log("Worker released the lock");
}
} else {
Log("Worker failed to acquire lock");
}
});
}
public void Interrupt() {
Log("UI thread - Setting interrupt flag");
WorkerCancel = true;
if (Monitor.TryEnter(lockObj, 5000)) {
try {
Log("UI thread - successfully acquired lock from worker");
} finally {
Monitor.Exit(lockObj);
Log("UI thread - Released the lock");
}
} else {
Log("UI thread - failed to acquire the lock from the worker");
}
}
private void Log(string Data) {
Console.WriteLine(string.Format("{0} - {1}", DateTime.Now.ToString("mm:ss:ffff"), Data));
}
}
}
Here is nearly identical code that fails for WP7, just make a page with two buttons and hook them
using System;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using Microsoft.Phone.Controls;
namespace WorkerThreadDemo {
public partial class MainPage : PhoneApplicationPage {
public MainPage() {
InitializeComponent();
}
private Object lockObj = new Object();
private volatile bool WorkerCancel = false;
private void buttonStartWorker_Click(object sender, RoutedEventArgs e) {
ThreadPool.QueueUserWorkItem((obj) => {
if (Monitor.TryEnter(lockObj)) {
try {
Log("Worker acquired the lock");
for (int x = 0; x < 10; x++) {
Thread.Sleep(1200);
Log("Worker: tick" + x.ToString());
if (WorkerCancel) {
Log("Worker received exit signal, exiting");
WorkerCancel = false;
break;
}
}
} finally {
Monitor.Exit(lockObj);
Log("Worker released the lock");
}
} else {
Log("Worker failed to acquire lock");
}
});
}
private void Log(string Data) {
Debug.WriteLine(string.Format("{0} - {1}", DateTime.Now.ToString("mm:ss:ffff"), Data));
}
private void buttonInterrupt_Click(object sender, RoutedEventArgs e) {
Log("UI thread - Setting interrupt flag");
WorkerCancel = true;
//Thread.Sleep(3000); UNCOMMENT ME AND THIS WILL START TO WORK!
if (Monitor.TryEnter(lockObj, 5000)) {
try {
Log("UI thread - successfully acquired lock from worker");
} finally {
Monitor.Exit(lockObj);
Log("UI thread - Released the lock");
}
} else {
Log("UI thread - failed to acquire the lock from the worker");
}
}
}
}
Your approach should work when you operate from the Application_Deactivated or Application_Closing event. MSDN says:
There is a time limit for the Deactivated event to complete. The
device may terminate the application if it takes longer than 10
seconds to save the transient state.
So if you say it just takes just a few seconds this should be fine. Unless the docs don't tell the whole story. Or your worker thread takes longer to exit than you think.
As Heinrich Ulbricht already said you have <=10 sec to finish your stuff, but you should block MainThread to get them.
It means that even if you have BG thread with much work to do, but your UI thread just does nothing in OnClosingEvent/OnDeactivatingEvent - you will not get your 10 seconds.
Our application actually does eternal wait on UI thread in closing event to allow BG thread send some data thru sockets.