Suppose, I have a StatefulWidget, which periodically requests data from a server and this updates its state.
I prepared a Timer.periodic() to make the data get loaded off the main loop.
Now, if the widget leaves the screen, the Timer continues to call its callback.
What is the correct mount point to perform cleanup actions, when the widget get off the screen?
You have to overwrite deactivate() of the State<StatefulWidgeet>:
#override
void deactivate() {
super.deactivate();
...
}
See Flutter docs.
Related
I need push new screen automatic on app startup (if user is login or sign out).
I am use scoped_model for auth so need navigate when user value is change in model.
I am follow Brian Egan suggestion here: https://github.com/brianegan/scoped_model/issues/43#issuecomment-442444143
class LoginScreenState extends State<LoginScreen> {
#override
void didChangeDependencies() {
ScopedModel.of<AuthModel>(context).addListener(_navigationListener);
super.didChangeDependencies();
}
#override
void dispose() {
ScopedModel.of<AuthModel>(context)
.removeListener(_navigationListener);
super.dispose();
}
void _navigationListener() {
switch (ScopedModel.of<AuthModel>(context).AuthStatus) {
case AuthStatus.NotAuth:
Navigator.of(context).pushNamed(‘/Login’);
break;
case AuthStatus.Auth:
Navigator.of(context).pushNamed(‘/Main’);
break;
case AuthStatus.Register:
Navigator.of(context).pushNamed(‘/Register’);
break;
}
AuthStatus is Enum. I change value in Model.
This is push route correct, but have issue:
Same route is push many times. For example, same Login page is push at least 5 times.
How to stop Navigator from push same screen multiple times?
Thanks!
In Brian Egan's example he had a boolean test in the _navigationListener method. His comment was:
// This function will be run every time the model changes! We will use it to
// check the navigate boolean. If it's set to true, we'll push a new screen!
//
// If not, we won't do anything.
So, a similar boolean needs to be used in your code to only navigate once despite how many times the method is called.
There are some scenarios where screens with their respective BLoCs are frequently created and closed. So I'm somewhat concerned about memory safety of the Streams instances created in this process, because it doesn't seem they are disposed somewhere or whether they are GC-ed. This clearly depends on the specific implementation of DART libraries and flutter. So if you know about their behavior, please let me know.
These are some scenarios I have encountered.
Multi-tab browser-like application.
Navigation through screens. (But it's not that harmful.)
showDialog() senarios when there are BLoCs inside the dialog. This is a far more common senario. There could be a lot of dialog popping up frequently in an app.
I wonder if it is necessary to override dispose() function and explicitly close all streams in BLoCProvider. It seems existing tutorials didn't mention it.
Streams will properly be cleaned as long as they aren't used anymore.
The thing is, to simply removing the variable isn't enough to unsure it's unused. It could still run in background.
You need to call Sink.close() so that it stops the associated StreamController, to ensure resources can later be freed by the GC.
To do that, you have to use StatefulWidget.dispose method:
abstract class MyBloc {
Sink foo;
Sink bar;
}
class MyWiget extends StatefulWidget {
#override
_MyWigetState createState() => _MyWigetState();
}
class _MyWigetState extends State<MyWiget> {
MyBloc bloc;
#override
void dispose() {
bloc.bar.close();
bloc.foo.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
// ...
}
}
I'm wondering if anyone has figured out a way to properly handle timeouts in the JavaFX 8 (jdk 1.8.0_31) WebView. The problem is the following:
Consider you have an instance of WebView and you tell it to load a specific URL. Furthermore, you want to process the document once it's loaded, so you attach a listener to the stateProperty of the LoadWorker of the WebEngine powering the web view. However, a certain website times out during loading, which causes the stateProperty to transition into Worker.State.RUNNING and remain stuck there.
The web engine is then completely stuck. I want to implement a system that detects a timeout and cancels the load. To that end, I was thinking of adding a listener to the progressProperty and using some form of Timer. The idea is the following:
We start a load request on the web view. A timeout timer starts running immediately. On every progress update, the timer is reset. If the progress reaches 100%, the timer is invalidated and stopped. However, if the timer finishes (because there are no progress updates in a certain time frame we assume a time out), the load request is cancelled and an error is thrown.
Does anyone know the best way to implement this?
Kind regards
UPDATE
I've produced a code snippet with behavior described in the question. The only thing still troubling me is that I can't cancel the LoadWorker: calling LoadWorker#cancel hangs (the function never returns).
public class TimeOutWebEngine implements Runnable{
private final WebEngine engine = new WebEngine();
private ScheduledExecutorService exec;
private ScheduledFuture<?> future;
private long timeOutPeriod;
private TimeUnit timeOutTimeUnit;
public TimeOutWebEngine() {
engine.getLoadWorker().progressProperty().addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> {
if (future != null) future.cancel(false);
if (newValue.doubleValue() < 1.0) scheduleTimer();
else cleanUp();
});
}
public void load(String s, long timeOutPeriod, TimeUnit timeOutTimeUnit){
this.timeOutPeriod = timeOutPeriod;
this.timeOutTimeUnit = timeOutTimeUnit;
exec = Executors.newSingleThreadScheduledExecutor();
engine.load(s);
}
private void scheduleTimer(){
future = exec.schedule(TimeOutWebEngine.this, timeOutPeriod, timeOutTimeUnit);
}
private void cleanUp(){
future = null;
exec.shutdownNow();
}
#Override
public void run() {
System.err.println("TIMED OUT");
// This function call stalls...
// engine.getLoadWorker().cancel();
cleanUp();
}
}
I don't think that you can handle timeouts properly now. Looks at this method. As you can see it has hardcoded value for setReadTimeout method. Is it mean that SocketTimeoutException exception will be raised after one hour of loading site. And state will be changed to FAILED only after that event.
So, you have only one way now: try to hack this problem use Timers as you described above.
P.S.
Try to create issue in JavaFX issue tracker. May be anyone fixed it after 5 years...
I have the same problem and used a simple PauseTransition. Same behavior, not so complicated. =)
In my app I have a BrowserField2 loading different pages and I want to show a simple spinning progressbar/indicator. As simple as possible really, without percent etc. - just a small animation to indicate to the user that something is happening.
I come from Android development and there such a thing is called Progressbar, though for Blackberry it maybe is called something completely different? (Progressbar for Blackberry seems to always include calculating the progress made).
What should I be looking for?
I solved it in a rather unorthodox way, something I probably wouldn't recommend ANYONE but I'll write it anyway since maybe it will help someone who's in a hurry to get it done. Just remember this is a bad way of doing it.
My app basically consists of 4 buttons and a browserfield.
To display a spinning "load animation" I use alishaik786's tip (see his comments) of the custom PopupScreen triggered by a browserfieldlistener:
// BrowserFieldListener to catch when a page started loading and when it is finished
BrowserFieldListener listener = new BrowserFieldListener() {
public void documentCreated(BrowserField browserField, ScriptEngine scriptEngine, Document document) throws Exception{
displayLoadAnimation();
// see method below
}
public void documentLoaded(BrowserField browserField, Document document) throws Exception{
try{
popUp.close();
}catch(IllegalStateException es){
es.printStackTrace();
}
}
};
// The method for showing the popup
private void displayLoadAnimation(){
popUp = new LoadingPopupScreen();
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
UiApplication.getUiApplication().pushScreen(popUp);
}
});
}
Then in my custom PopupScreen I check where the user is clicking in "protected boolean touchEvent(TouchEvent event)" by checking event.getGlobalY() & event.getGlobalX() of the touch and comparing it to the positions of the buttons. If the user presses within the X&Y of a button then the popup screen is closed and I trigger the button being pressed.
As I said this is a bad way of doing it (many things need to be static), but it works if you want a quick and dirty sollution.
I'm writing a stopwatch application for BlackBerry (which is similar to the BlackBerry built-in StopWatch). There is a timer label displaying current time in the format MM:SS:T (minutes, seconds, tenth of second). The label is refresh each 100 millisecond with TimerTask.
The application works well and the time is display correctly, however, there are some moments the timer label is not updated at the predetermined interval (each 100 milliseconds). The timer label pauses (not counting) for a while and continues counting (while still displays the time correctly)
My thought is the TimerTask is not executed to update the timer label during this pause. Do you know why the app act this way, and how to fix it?
Below are the Thread to update the timer label:
public class ThreadUpdateTime extends Thread
{
private MyMainScreen myMainScreen;
private Timer updateTimerLabelTimer = new Timer();
public ThreadUpdateTime(MyMainScreen parent)
{
myMainScreen=parent;
}
public void run()
{
try {
updateTimerLabelTimer.schedule(new RecordTimer(myMainScreen), TIMER_DELAY, TIMER_INTERVAL);
} catch (Exception e) {
//put alert here
}
}
public void iStop()
{
updateTimerLabelTimer.cancel();
}
}
the timerTask:
public class RecordTimer extends TimerTask
{
private MyMainScreen myMainScreen;
public RecordTimer(MyMainScreen parent)
{
myMainScreen=parent;
}
public void run()
{
myMainScreen.iUpdateTimerLabel();
}
}
and the iUpdateTimerLabel method:
public void iUpdateTimerLabel()
{
//calculate : sign, sMin, sSec, sTenth
synchronized(Application.getEventLock())
{
lblSpotTime.setText(sign+sMin+":"+sSec+"."+sTenth+" ");
}
}
First is to measure it... log the timestamps when your timertask begins and ends, and see if it's really the TimerTask that's really the problem. With that in hand, a couple of things that occur to me are,
Is your task blocking (maybe on
some UI thing)?
Are there other tasks in the same
Timer instance? I don't know if it's specified as such, but tasks probably all run on a single thread, so if another task is getting in the way, your tasks may not run at the exact specified interval.
Is your TimerTask properly synchronized with the UI event loop (i.e., is it updating the label in the correct runLater() or whatever method provided by the blackberry UI)? If you aren't doing this, the UI event loop may not notice that you've changed the label. I think on the Blackberry, the right thing is invokeLater() or maybe invokeAndWait(), depending on what you're trying to accomplish.
Edited after code posted:
A couple of useful and relevant resources are here.
OK, I'd still say to instrument your code with some logging or println calls to output
timestamps when it runs.
Not sure why the schedule() call is inside its own Runnable... you don't need that, but maybe your application is doing that for some reason I can't see. If you think you're creating an explicit thread for the timer, you're not. You can probably just create the Timer and call schedule() from whatever application thread is setting this up. Timer contains a captive thread that will do this work, and introducing Yet Another Thread is probably redundant and confusing.
I still think you may want to do something like:
Another reminder to actually MEASURE what the timer is doing rather than relying on my speculation...
code inside the TimerTask:
public void iUpdateTimerLabel()
{
//calculate : sign, sMin, sSec, sTenth
// synchronized(Application.getEventLock())
UiApplication.getUiApplication().invokeLater(
new Runnable() {
#Override
public void run() {
lblSpotTime.setText(sign+sMin+":"+sSec+"."+sTenth+" ");
}
});
}
Your synchronized call may be enough to keep things from blowing up, but it's not really the preferred means. If the Timer thread is dedicated to this single purpose, as it appears, you can probably replace invokeLater() with invokeAndWait() if you like.
Someone else may be able to elucidate the difference between just holding the UI lock and actually running on the UI thread, but my guess is that the latter forces an invalidate(), and the former does not. This would explain why your label changes are only showing up sporadically.