Timer task makes app crash - java

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.

Related

Android animation only works if I reschedule it from the UI thread to the UI thread

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.

application crash aftet restarting an acticity several times

I have this wierd situation that i can't understand.
I developed an application on android studio, on the first activity i start a second activity, on that second activity i created a SurfaceView child class that extends surfaceView and implemment Runnable and draw on the canvas inside run(), when i exit the thread i call onRestart() to start the canvas thread again.
all good so far, i open the thread, draw some images in a loop, exit the thread loop, restart.
the problem is that after 20 times of restarting, the application crash with no logical reason.
what do you think the problem can be? how to check it and fix it?
this function is the thread work inside the surfaceView child class (of the activity)
#Override
public void run() {
while (!_susspendThread) {
// draw images
}
onRestart();
}
this function is on the parent activity class
protected void onRestart() {
// make some changes, nothing critical to the question
// examp. count++;
surface_view.StartThreadWork();
}
another thing i must say although i dont belive it's related to the problem is that onCreate i read some internal files.
thanks for any help.
You have a memory leak, and this could be from many things. A good place to begin, is to use the onDestroy() methods of your Activities to cleanup any resources you have generated (like the internal files you are creating in your onCreate method).
Also, there are tons of tools for tracking memory usage, in AndroidStudio (AS) there is a "Memory" tab in the "Android" view. Also, you can run the "Monitor" tool (available from the command-line, or within AS to get ton's of details on what type of resource you are leaking.

libgdx updating scene when app goes invisible

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();
}

Android View Canvas onDraw not performed

I am currently working on a custom View which draws some tiles on the canvas.
Those tiles are loaded from several files and will be loaded when needed.
They will be loaded by an AsyncTask. If they are already loaded they will just be painted on the canvas. This is working properly!
If those pictures are loaded the AsyncTask is firing view.postInvalidate()
The problem is that my custom View is not firing onDraw(Canvas canvas) everytime I fire view.postInvalidate().
The view.postInvalidate only fires onDraw() method the first time when a picture is loaded and then only when I fire this.invalidate() in an onTouchEvent inside my CustomView
Is it possible that a View decides wether it will draw the canvas again or not?
Is there a way to FORCE the View to redraw? I think the invalidate method tells the View that it would be cool if the View would think about redrawing -.-
Is it possible that those invalidate methods have a limit?
I hope anyone of you knows more about this problem.
edit:
I just changed every postInvalidate() to invalidate() because the images are all loaded by an AsyncTask executed from the main Thread
But there is still the problem that an executed invalidate() is not executing the onDraw() method.
I found out that the view.invalidate() is fired by overriding the original method:
#Override
public void invalidate() {
super.invalidate();
Log.d(TAG, "invalidate executed");
}
I don't know what to do now. I'm firing view.invalidate() and view.postInvalidate() but nothing works in absolutely no combination.
There's a bit of a misunderstanding here. The invalidate and postInvalidate method are used to tell the View that it needs to be refreshed and redrawn in the earliest drawing cycle. The difference is that the invalidate method should be called from within the UI Thread and the postInvalidate should be called from outside of the UI Thread.
These methods are briefly described here:
http://developer.android.com/reference/android/view/View.html#postInvalidate()
http://developer.android.com/reference/android/view/View.html#invalidate()
The AsyncTask on the other hand is a class devised especially for the problem you're facing. When you need to perform a big task in the background, asynchronously you need the AsyncTask for that, but! the AsyncTask's callback method is run in the UIThread!
Take a look at the explanation of AsyncTask methods here:
When an asynchronous task is executed, the task goes through 4 steps:
onPreExecute(), invoked on the UI thread immediately after the task is executed. This step is normally used to setup the task, for instance by showing a progress bar in the user interface.
doInBackground(Params...), invoked on the background thread immediately after onPreExecute() finishes executing. This step is used to perform background computation that can take a long time. The parameters of the asynchronous task are passed to this step. The result of the computation must be returned by this step and will be passed back to the last step. This step can also use publishProgress(Progress...) to publish one or more units of progress. These values are published on the UI thread, in the onProgressUpdate(Progress...) step.
onProgressUpdate(Progress...), invoked on the UI thread after a call to publishProgress(Progress...). The timing of the execution is undefined. This method is used to display any form of progress in the user interface while the background computation is still executing. For instance, it can be used to animate a progress bar or show logs in a text field.
onPostExecute(Result), invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter.
This means that in the onPostExecute method you should try using the invalidate method instead of the postInvalidate method as it is called from the UIThread.
Have you heard of a method call setWillNotDraw(boolean)?
Check it out here
Set it to false and you'll see
Just to save this for the future and possible google searchers ;)
I made a new Class which provides datas for my View. This class extends Thread and runns in the background. It checks which tiles my view has to display.
Long story short: Each time a new tile has to be displayed my provider class collects some update requests.
If 3 or more update requests are "collected" or it is more than 1 second since the first update request the view will get view.postInvalidate().
The problem was that a view ignores some invalidate requests if you fire several of them at once. If you collect those invalidate requests and fire several of them with just one invalidate() you can see the newest information with just a small lag of time - but you can see it ;)
I've solved the problem requesting view.invalidate() or view.postInvalidate() not for invalidating every child view individually (each of these child views incorporated its own AsyncTask), but for invalidating their common parent ViewGroup.
Now in details. I had
public class mViewGroup extends RelativeLayout {...}
and several Views in it, each View as follows:
public class mView extends View {
private mViewGroup mContext = null;
//with a little bit modified standard constructors:
public mView (Context context, mViewGroup ParentViewGroup) {
super(context);
mContext = ParentViewGroup;
}
//and also with an AsyncTask:
private class mViewAsyncTask extends AsyncTask<Integer, Void, Void> {
public mAsyncTask() {
#Override
protected Void doInBackground(Integer... a) {
//which does its work and sometimes publishes progress:
publishProgress();
return null;
}
protected void onProgressUpdate(Void... progress) {
invalidate();
}
}
The calls to invalidate(); sometimes worked and sometimes didn't (may be in cases if calls were made from different AsyncTasks belonging to different mViews but in very close moments of time).
Changing
invalidate(); //i.e. invalidating individual View
to
mContext.invalidate(); //i.e. invalidating the whole ViewGroup
solved the problem.
In my case, target custom View has height = 0 attribute at that time.(My mistake)
and postInvalidate() and invalidate didn't fired onDraw() and Log too.
When I changed height currectly, all problem solved.

How do I temporarily disable/bypass the Android OnTouchEvent

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.

Categories