androidplot
androidplot copied to clipboard
Plot not displayed with background thread if initial size is 0
This is using Androidplot 1.5.10.
Due to various degrees of nesting (e.g. XYPlot within a LinearLayout, within a fragment that replaces a FrameLayout of the fragment of a ViewPager2's page, ...), I've encountered a situation where the one of the dimensions of the XYPlot instance is 0, before being expended to its actual size later on. (Apologies, I'm unable to re-create a small fully contained example.)
In this case, renderOnCanvas
is called with a null
Canvas
, but when we're using a background thread, isIdle
is never set to true
in this case (as it would with non-zero dimensions).
As a result, the background thread is stuck waiting at renderSync.wait()
, and the plot can never be rendered by resizing.
Details
In a situation where rendering is done in background-thread mode, this shows these logs:
plot.addOnLayoutChangeListener(
(view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
XYPlot plot = (XYPlot) view;
Log.d(TAG, String.format(
"Plot layout change: %s/%s/%s/%s -> %s/%s/%s/%s",
oldLeft, oldTop, oldRight, oldBottom,
left, top, right, bottom));
});
D Plot layout change: 0/0/0/0 -> 0/0/1080/0
I Thread started with id 143665258
D Plot layout change: 0/0/1080/0 -> 0/53/1080/824
The problem here is that the background thread, and the first pass at rendering starts with a dimension of 0 height.
In this case, the Canvas
in the pingPong
instance is null
, in Plot.startBackgroundrendering()
.
Indeed, the BufferedCanvas
is resized using (0,0)
, which makes its bgBuffer
null
, and therefore makes getCanvas()
return null
.
As a result, renderOnCanvas
returns straight away:
protected synchronized void renderOnCanvas(@Nullable Canvas canvas) {
if(canvas == null) {
return;
}
The problem here, is that isIdle
is never set to true (as it would with a non-null canvas).
As a result, redraw()
never does anything, since isIdle
is always false
:
public void redraw() {
if (renderMode == RenderMode.USE_BACKGROUND_THREAD) {
// only enter synchronized block if the call is expected to block OR
// if the render thread is idle, so we know that we won't have to wait to
// obtain a lock.
if (renderThread != null && isIdle) {
synchronized (renderSync) {
renderSync.notify();
}
}
Because of that, renderSync.notify()
can never be called, so the background thread is stuck at renderSync.wait()
, so no further redrawing can take place.
Solution
Setting isIdle = true
in Plot.renderOnCanvas
seems to fix the problem:
protected synchronized void renderOnCanvas(@Nullable Canvas canvas) {
if(canvas == null) {
isIdle = true;
return;
}
Thanks for the report @harbulot!
I know you stated in the first sentence that you were unable to make a code demo of the issue, but just being perfectly honest without a demo, it could be a while before I am able to dedicate the time to investigate and fix. Alternatively you're more than welcome to open a PR if you've got a clean solution that will not negatively impact other user's use cases!