fix(android): There were invalidate conflicts due to asynchronous executions #71
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
In android, there is a random bug that makes canvas blank.
There are references about it including mine in issue #67 .
This has discouraged me to use plugin so far because most of our clients have android phones.
Let's start with this. We have android
doFrame
callback to handle canvas invalidation.That is an overridden method inside
TNSCanvas
: https://github.com/NativeScript/canvas/blob/master/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/TNSCanvas.kt#L125Flush execution will indirectly call
GLContext
methodflush
that contains invalidation logic, wrapped insidequeueEvent
method here: https://github.com/NativeScript/canvas/blob/master/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/GLContext.kt#L148The queue will eventually be handled from
GLThread
and execute invalidation logic here: https://github.com/NativeScript/canvas/blob/master/packages/canvas/src-native/canvas-android/canvas/src/main/java/org/nativescript/canvas/GLContext.kt#L599So, where is the problem?
On a first look, this whole logic seems good. However, there is something really deceiving about it.
There is the call of
Choreographer.getInstance().postFrameCallback(this)
at the end ofdoFrame
callback that performs the next call ofdoFrame
itself.The problem here is there is no guarantee that
postFrameCallback
will be executed onceflush
is done. After all,flush
logic is inserted inside aBlockingQueue
and another thread (GLThread
) is pulling the event and running it inside thread's loop cycle.As a result, frame callback can be called anew before
flush
is done and will attempt an additional flush causing a conflict between them.So
The defect of this flaw is that it makes canvas completely blank. This bug is extremely random because the code itself is good but there is a flaw caused by multi-threading. I have personally dealt with Java threads for few years now and I can see the evil in them.
I had to read native code and search a lot until I made the conclusion above, though I'm really excited about the time spent.
In order to fix this, I replaced
pendingInvalidate
withinvalidateState
flag which has 3 enum states (NONE, PENDING, INVALIDATING).This flag keeps the old functionality and additionally lets canvas be aware if it's in the process of invalidating.
These changes seem fine to me regarding conventions but I'm not very familiar with kotlin, so please feel free to correct me if needed.