I'm currently using the event
#Override
public boolean onTouchEvent(MotionEvent ev)
{
To provide feedback from the user for game I'm writing. This event fires off some game control events that take some time to complete (because there is animation involved). The problem I'm having is during the animation if I keep touching the screen (AKA firing this event), I will get a timeout error:
10-02 21:58:30.776: ERROR/ActivityManager(67): Reason: keyDispatchingTimedOut
Basically while the animation is running, I do not need this event, although it would be nice to queue it.
What can I do to prevent the timeout and either queue the event, or temporarily disable it?
Thanks.
Are you by any chance blocking the UI thread? There should be absolutely no reason to do what you are asking for (and it's not even possible :)
Beside what Romain said.
You might want to sleep() on your onTouchEvent.
Related
I perform a network call in which I
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
and then I pass the result to an io.reactivex.function.Consumer, which attempts to animate a view.
These animations worked when I was loading the data locally and synchronously, but broke when I introduced this server call.
Now when I animate the views, it does not change any of the properties at all, but instead just instantly calls the animation complete listener.
I verified that I'm on the main thread with Looper.getMainLooper().isCurrentThread()
Yet the only way I've been able to get the animation working again is to wrap it in AndroidSchedulers.mainThread().scheduleDirect(() -> {})
Which as shown by this piece of code shouldn't actually change anything
final Thread before = Thread.currentThread();
AndroidSchedulers.mainThread().scheduleDirect(() -> {
boolean nothingChanged = before == Thread.currentThread();
//nothingChanged is true
});
Why do I have to reschedule the animation back onto the thread it is already on in order to get it to work?
Edit: RxJava is not involved, something even weirder is going on.
I cut out RxJava and the issue is reproducing even though I put the animation code in onNavigationItemSelected(), which is called by android when the user clicks something, so its definitely in the right thread.
My animation looks like this.
alertBanner.clearAnimation();
alertBanner.animate()
.alpha(0)
.translationY(alertBanner.getHeight())
.setDuration(500)
.setListener(new AnimatorListenerBuilder().setOnAnimationEnd(animator -> {
alertBanner.setVisibility(GONE);
}))
.start();
AnimatorListenerBuilder is just a utility class that wraps AnimatorListener and lets you only define the callbacks that you want. It works for all the other animations in my app and hasn't been changed in months.
I figured it out. It was a race condition.
When the user clicked the button, two things happened:
A new fragment was created, and an animation was started.
However, the fragment had a callback, onAttach() that would, among other things, call .clearAnimation() on the view I was trying to animate.
By rescheduling the animation back onto the main thread, I was making sure that it happened after the fragment callback.
I solved it by getting rid of the callback from the fragment, and putting that logic in the same place as the logic that starts the animation, allowing me to control the execution order.
I have a Canvas object in my app and I want to update it each 20 ms. I tried Java's TimerTask, but it makes my app crash after few updates. Here's update code;
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
invalidate(); // canvas's update
}
}, 20, 20);
What shall I do?
Edit: Error log:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
In this case, run() is called on a background thread, and invalidate() is called on that background thread. This will lead to the exception you have in your question, as those sorts of updates can only be done on the main application thread.
Since calling invalidate() 50 times/second is unlikely to be the right solution for whatever problem you are trying to solve, you may wish to ask a separate Stack Overflow question where you explain what sorts of changes you are trying to make to your UI every 20ms, to see what the recommended approach is. Or, pick up a book on Android game development — while you may not be building a game, game developers drawing to a Canvas also have these sorts of concerns, so the techniques that they use might be relevant to you as well.
If you really want to do this, just change invalidate to postInvalidate.
I am using libgdx's render() function to update scene with time delta, and then draw the scene. However, when the app goes invisible, the render() function is no longer being called, which causes the scene to stop updating, E.g., an animation will stuck at the last frame, until app goes back to foreground.
The problem is that I am building an online real-time game which cannot be 'paused' on client. When the app switches to background, the game must keep updating the scene even without drawing the scene, so that when the user switches back to the app, he can see the most up-to-date scene, instead of a pause-and-resume scene that lags back.
My question is how to achieve this in libgdx, so that the scene keeps updating even when app goes background? Does anyone experience the same problem as mine?
In a Libgdx app, the render thread is only invoked when rendering, thus only when it has the screen. For most games this is the only time the game should be doing any computations, so combining the update and render makes a lot of sense.
In Android, an app generally only gets CPU cycles assigned to it by the OS when its "foreground". To run an app in the background requires using a different platform API (See, for example, https://developer.android.com/training/best-background.html.) Simply creating additional threads will not be sufficient to keep your app running in the background (the OS may decide to terminate it completely if it wants the RAM back for something else).
Generally, users do not expect an Android application to continue running (and using battery) when not in the foreground.
Additionally, even if your app is coded to run in the background correctly, when the foreground app is using the CPU significantly, your background app may not be run.
You can hook into the suspend and resume events to pause your game and then "fast-forward" your game state as necessary. Or, do your computations in the cloud if you require that they run in real-time.
You can try to update your scene from other thread if it doesn't need GL context in it.
Or you can override onPause() method in your AndroidApplication class in Android projects, and keep using render() method for updating.
In onPause() method of AndroidApplication graphics are told to stop rendering.
Maybe you can create interface class to update your scene, without constant rendering when the app is paused, concerning the battery life.
This is the code from onPause() method.
Maybe this will lead you to correct solution.
#Override
protected void onPause () {
boolean isContinuous = graphics.isContinuousRendering();
graphics.setContinuousRendering(true);
graphics.pause();
input.onPause();
if (isFinishing()) {
graphics.clearManagedCaches();
graphics.destroy();
}
graphics.setContinuousRendering(isContinuous);
graphics.onPauseGLSurfaceView();
super.onPause();
}
I am working on a libgdx game and I'd like to use the preferences for storing player's advances and preferences.
So far so good. In android there are nice events for updating the preferences and storing them into the file system.
I was wondering if something similar does exist on the desktop side. Namely, is there any way of adding a listener for whe user is closing the window?
That way I'll be able to flush data before application closes.
Or there are any other method?
Your main game implements ApplicationListener, which has the dispose() method. This method gets called, if you close your window or exit the game. So you can simply flush() your Preferences in the dispose() method.
But as #noone said you should also think, that your app could crash for some reason. If this happens dispose() won't be called. It won't be that big problem for Preferences like Soundoptions or Graphic settings, as you can easily set them next time, but if a Player looses his progress, he will be frustrated. So i would think about using one of the following methods:
flush() on every change. You can be sure everything is safed. For progress you can flush() on some special events/safepoints
flush() after some time. Lets say every 5 minutes?
flush() on a KeyEvent/Button press in the menu.
I created an android app. The screen never turns off:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Now I want to show a picture if the screen is not pressed for 5 minutes or something else. The app should not be closed, when pressing on the image the app should be open.
How can I realize that?
I would discourage you from taking this approach. Users expect to have a consistent user experience between various apps on their devices, and likely have a preference to how their device sleeps, either by having specified a sleep timeout or displaying a daydream as introduced in Android 4.2.
If you'd like to provide users with the option to display a screensaver associated with your app, I suggest including a Daydream in your app and otherwise acknowledging the user's preferences.
That being said, if you cannot use Daydream, you could observe if the app is being used or not. Two things come to mind:
Have the root view of your activity intercept touch events to observe if any of its children have been touched.
Observe the activity's onPause() and onResume() to acknowledge that the activity is still being displayed.
You could then invoke a Runnable by posting it to a view using postDelayed(Runnable action, long delayMillis), being wary to remove it when the activity is paused or the timer should be reset using removeCallbacks(Runnable).
I solved the problem!!!
I used that event:
public boolean dispatchTouchEvent(MotionEvent ev)
{
super.dispatchTouchEvent(ev);
// cancel my Timer
return true;
}
Thanks!!