My first attempt at AndroidPlot. The data I want to plot (and update every 5 seconds when a new data point arrives) comes from an ArrayBlockingQueue of up to 720 timestamped points. I have a class that implements the XYSeries and PlotListener interfaces. It has a method updatePlotData that just extracts the data from the queue into an array:
class TempPlotSeries implements XYSeries, PlotListener {
private final static String TAG = TempPlotSeries.class.getSimpleName();
private Pair<Date, Float>[] plotArray;
void updatePlotData( ArrayBlockingQueue<Pair<Date, Float>> dataQueue ) throws InterruptedException {
synchronized ( this ) {
wait(); // don't update data until we're notified that current plot is done (& we can get lock)
plotArray = dataQueue.toArray( new Pair[0] );
if( DEBUG ) Log.d( TAG, "updatePlotData run with " + plotArray.length + " data points" );
notifyAll(); // release lock & let other threads know they can continue
}
}
// XYSeries implementation
#Override
public int size( ) {
return plotArray.length;
}
#Override
public Number getX( int index ) {
return (index - HISTORY_BUFFER_SIZE) / (60/TEMP_UPDATE_SECONDS); // e.g., -60 minutes at left edge of graph, -1/12 min at right
}
#Override
public Number getY( int index ) {
return plotArray[index].second; // the temp value
}
#Override
public String getTitle( ) {
return "Temp History";
}
// PlotListener Implementation
#Override
public void onBeforeDraw( Plot source, Canvas canvas ) {
synchronized ( this ) {
try {
wait(); // wait for data updating to finish if it's in progress on another thread
} catch ( InterruptedException e ) {
// unlikely to be interrupted?
}
}
}
// between these 2 calls the plot is redrawn
#Override
public void onAfterDraw( Plot source, Canvas canvas ) {
synchronized ( this ) {
notifyAll( ); // plot done, OK to update data
}
}
}
I don't have much experience with synchronization--does this look reasonable?
My plot setup is:
tempHistoryPlot = (XYPlot) findViewById(R.id.temp_history);
tempPlotSeries = new TempPlotSeries();
tempHistoryPlot.setRenderMode( Plot.RenderMode.USE_BACKGROUND_THREAD );
tempGraphFormatter = new LineAndPointFormatter(this, R.xml.line_point_formatter_with_labels);
tempHistoryPlot.addSeries(tempPlotSeries, tempGraphFormatter);
tempGraphWidget = tempHistoryPlot.getGraph();
(couldn't find any documentation on the purpose of getGraph() so don't know if I need it.)
I have an Observable (RxJava) that emits the entire data queue when a new sample is available (every 5 seconds). If the queue is full I discard the oldest value. Then I have:
tempPlotSeries.updatePlotData( newTempHistory );
tempHistoryPlot.redraw();
But the plot isn't drawn. When the app first launches, the "dummy" plot appears in its View, but as soon as I try to draw the plot the entire ConstraintLayout containing the XYPlot element (and other UI elements) is completely blanked. What's going on here?
Other questions: it's my understanding that any code affecting the Android UI must run on the main thread. But we're using a background thread to render the plot. How does this work? Do I perhaps need to insert a .observeOn( AndroidSchedulers.mainThread() operator in my Observable chain?
I don't have much experience with synchronization--does this look reasonable?
I don't think you need the wait() inside the synchronized block inside updatePlotData. You can also use SimpleXYSeries as a reference for how to setup synchronization of this sort.
When the app first launches, the "dummy" plot appears in its View, but as soon as I try to draw the plot the entire ConstraintLayout containing the XYPlot element (and other UI elements) is completely blanked.
I'm having trouble visualizing this. Could you add a screenshot of the "dummy" plot and the subsequent blank plot?
it's my understanding that any code affecting the Android UI must run on the main thread. But we're using a background thread to render the plot. How does this work?
The general rules of using the main thread to update the UI still exist, Androidplot is just using a technique to minimize the main thread usage during intensive rendering: A background thread is used to fill a bitmap buffer with the data to be shown, then notifies the main thread when the buffer is ready to be displayed.
Somewhat Unrelated Suggestion: Looking at your TempPlotSeries implementation, I notice that you are modeling your data as a Pair<Date, Float>[] but your getX() implementation does not make use of the Date part. It appears you're trying to model your data using what I assume is your desired display format for your domain, ie. -60 to -1/12 minutes. For simplicity I'd suggest making getX() return the Date's long epoch value instead. You can apply a display format to these values later.
Related
I am using MPAndroidChart to draw some charts in android. The problem I'm facing is that the following code is executed in asynchronous way. E.g:
Log.d("Starting data load","Starting data load");
x1.setValueFormatter(new GraphXAxisValueFormatter(xLabels_nl,chart));
chart.setData(data);
chart.fitScreen();
Log.d("Finished data load","Finished data load");
"Starting data load" is logged to console
setValueFormatter is initiated
"Finished data load" is logged to console
setValueFormatter is still running
The problem is that when setValueFormatter is still running, if the described code is executed second time, the chart does not zoom/drag correctly to it's initial position.
Is there a way to wait till the first execution of "x1.setValueFormatter(new GraphXAxisValueFormatter(xLabels_nl,chart));" is finished when it is started second time ?
The GraphXAxisValueFormatter is class that overrides the following method:
#Override
public String getFormattedValue(float value, AxisBase axis) {
...
return <axis values for each point at position "value">
}
Thanks
The real solution is probably elsewhere, you should raise an issue on the open source library https://github.com/PhilJay/MPAndroidChart/issues
Hack Alert
But you can make arbitrary code wait for you if you want:
final CountDownLatch latch = new CountDownLatch(1);
Log.d("Starting data load","Starting data load");
final GraphXAxisValueFormatter graphFormatter = new GraphXAxisValueFormatter(xLabels_nl,chart)
x1.setValueFormatter(new IValueFormatter() {
#Override
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
String result = graphFormatter.getFormattedValue(value, entry, dataSetIndex, viewPortHandler);
latch.countdown();
return result;
}
});
try {
latch.await(); // this will make this Thread wait until countdown() is called
} catch (InterruptedException e) {
// uh oh, deal with error
}
chart.setData(data);
chart.fitScreen();
Log.d("Finished data load","Finished data load");
Note - you cannot make the MainThread wait too long or you will get an ANR (you shouldn't make it wait at all really)
MPAndroidChart classes like LineChart are subclasses of View and thus should be updated on the UI/main thread.
If you are performing processing of data on another thread (in, say, an AsyncTask or using RxJava) that is fine. However, modifications of the View object itself (such as mutating the DataSet or IValueFormatter for the chart) should be done on the UI/main thread or you will run into synchronisation problems like the one that seems to be occurring above. This is the standard pattern for most standard Android Views, not just MPAndroidChart. Note that none of the methods in LineChart etc. actually spawn another thread so it is the responsibility of the consumer to ensure they are using the correct thread.
Additionally, after you have updated the DataSet you will need to call ChartData#notifyDataSetChanged(); to get the library to recalculate the min/max.
I have an ArrayList of values, and I would like to iterate through the ArrayList. For every new value, I would like to update the chart with that value, and then wait a set amount of time before doing the same thing to the next value.
At the moment, my log says that all of the values are being iterated over. However, on my testing device, the chart does not update until the very end; at that point, all of the values are loaded at once, so there is no desired "slideshow" effect.
When I want to start playing back the values in my ArrayList, this method is called:
public void playback(){
if(ret != null) {
for (int x = 0; x < ret.size(); x++) {
addEntry(ret.get(x));
try {
Thread.sleep(100);
} catch (Exception e){
//Do nothing
}
}
} else {
Log.d(LOG_TAG, "ret was null.");
}
}
What can I do so that the values are displayed on my chart, one after another, with a certain amount of time between each value?
Edit: Here was the solution I ended up implementing with help from Shadab Ansari:
public void playback(){
if(ret != null) {
addEntry(0);
} else {
Log.d(LOG_TAG, "ret was null.");
}
}
private void addEntry(int index) {
final int in = index;
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
yVals1.get(0).setVal(ret.get(in).intValue());
RadarDataSet set1 = new RadarDataSet(yVals1, "Set 1");
// And other UI stuff
// Recursive call!
if(in < ret.size() - 1){
addEntry(in + 1);
}
}
}, 100);
}
In case it was not clear, ret was a global variable that contained the arrays that I was going to be inserting. yVals1 was an ArrayList of Entries to populate the radar chart.
The end result is that, in this example code, the chart is updated with the next value in my ArrayList every 100 milliseconds. During this time I can still zoom in/out of the chart and rotate it with no problems.
If your addEntry() performs a UI operation then let me explain your problem -
Explanation -
Android is an event based system. Something happens on the device (the screen is touched, a key is pressed, etc.) and Android raises an event. An App is notified of an event and when one occurs that it needs to respond to it does so, often running the code that you have written. Your App runs its code in a loop under the control of the Android Operating Systems (OS). This code loop is referred to as the App's thread of execution. There is only one thread and it is responsible for both running the App code and updating the display.
So the UI update does not happen immediately and your making the UI thread sleep for 100 ms every time the loop runs. And when Android tries to update the UI, you make the thread sleep which means during this time period UI thread will not do anything. And this happens till your loop finishes. After your loop ends, the final event gets executed and you will see your UI updated by the call of addEntry() with the latest value passed.
Solution -
You can use postDelayed()-
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//Perform your task and it will be executed after 100 ms
}
},100);
I use functions for canvas like drawCircle and drawPoint in android.
This works fine.
But the problem now is to draw these different items with a delay, so it looks like an animation.
What kind of mechanism should I use? Have tried with async but I dont like that way of doing it.
Should I use some kind of timer that just draw with an interval or is there other clever ways to do this?
I use this strategy, first I declare a Handler and a Runnable that way:
private final Observable mObservable = new Observable();
private final static int TIME_STEP_MS = 5;
private final Handler mHandler = new Handler();
private final Runnable mTimeManager = new Runnable()
{
public void run()
{
mObservable.notifyObservers(TIME_STEP_MS);
mHandler.postDelayed(mTimeManager, TIME_STEP_MS);
}
};
Then when I want to start my time manager I just call the mTimeManager.run() and it will start to notify my Observer s (previously added) periodically.
If you need for some reason stop the timer or something you just do that:
mHandler.removeCallbacks(mTimeManager);
[ EDIT - More complete code ]
Ok than let's make it clearer, first I made a custom Observable object like that [that's optional]:
private final Observable mObservable = new Observable()
{
public void notifyObservers()
{
setChanged();
super.notifyObservers();
};
#Override
public void notifyObservers(Object data)
{
setChanged();
super.notifyObservers(data);
};
};
the reason for that is just because I can't call setChanged() outside Observable class - it's protected, if it's not changed it doesn't notify any observer.
The other declarations keep the same as shown before, now I need to start this TimeManager somewhere, my app is a LiveWallpaper and I make all rendering stuff into a class that extends a Thread but you don't need that necessarily, I made a method called resumeDrawing(), this one is called right after super.start(); at my #Override of public synchronized void start() from Thread class, the method looks like that:
public void resumeDrawing()
{
if (!mTimeManagerRunning) // just a boolean field in my class
{
System.err.println("Resuming renderer."); // just for debug
mTimeManager.run();
mTimeManagerRunning = true;
}
else
{
System.err.println("Renderer already running."); // just for debug
}
}
and it's dual:
public void pauseDrawing()
{
if (mTimeManagerRunning)
{
System.err.println("Pausing renderer.");
mHandler.removeCallbacks(mTimeManager);
mTimeManagerRunning = false;
}
else
{
System.err.println("Renderer already paused.");
}
}
Ok, now we can start and stop the time manager, but who's listening? Nobody! so let's add'em: On the constructor of my Renderer I add some Observer s to my mObservable object, one of those is the Renderer itself, so my renderer extends Thread and implements Observer:
#Override // from Observer interface
public void update(Observable arg0, Object arg1)
{
mElapsedMsRedraw += (Integer) arg1;
if (mElapsedMsRedraw >= mDrawingMsPerFrame)
{
mElapsedMsRedraw = 0;
drawEm(); // refresh the canvas and stuff
}
}
to add observers you simply do mObservable.addObserver(THE_OBJECT - Implements Observer)
you can see that I don't re-render my stuff each time I'm notified, that's because I use this TimeManager for other thinks than just refresh the Canvas like updating the position of the objects I want to draw just internally.
So, what you need to slow down the drawing is to change the way your objects change internally while the time passes, I mean your circles and points etc, or you can chance your time step, I recommend the first one.
Was it clearer? I hope it helps.
I would use a timer, or create Animations. You can create Animations that will do all sorts of things including changing transparency over time.
Here's the Android Documentation for Animation Resources
I believe there may be sophisticated ways of doing this, but for my needs I used a simple method that has a lot of advantages:
I first create records of coordinates (and any other data needed) for every point of the drawing -- instead of drawing the points on the spot -- and then reproduce them using a timer (Android handler, preferably). This also offers a lot of possibilities while actual drawing: pause, go faster/slower, go backwards, ...
I don't know if this method can be used for complicated drawings, but it is fine for drawing shapes, curves, surfaces, etc.
I am working on a simple android game. I came to a point that I would like to optimize my engine with multithreading. I am working in OpenGL ES, Android 2.2
Now UpdateGame() and RenderScene() run in single thread and are executed in onDrawFrame(GL10 gl)
I have a RenderObject class that has Position, Rotation, Scale, and Color members. All RenderObjects are created when I start my game and are stored in RenderObject array.
In UpdateGame() function I go through RenderObjects in RenderObject array and update new Position, Rotation, Scale and Color.
In RenderScene() function I go through RenderObjects in RenderObject array and render them with new Position, Rotation, Scale and Color.
This works ok in single thread.
So then I tried create a thread for UpdateGame().
So my idea is to:
Update Thread:................Render Thread:
Update Frame 0
Update Frame 1................Render Frame 0....Update and Render work in parallel
Update Frame 2................Render Frame 1
Update Frame 3................Render Frame 2
But first I modified RenderObject class so that it has RenderPosition, RenderRotation, RenderScale and RenderColor members.
These members get copied just before rendering previous frame. So that parallel update thread can modify new Position, Rotation, Scale, Color.
Runnable pRunnable;
Thread pThread;
public int renderframe = 0;
piblic int updateframe = 0;
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
// I create a new thread in this function
pRunnable = new Runnable(){
public void run(){
while(true)
{
while( renderframe < updateframe )
{
// WAITING for render to finish so that data does not get corrupterd
}
//
// I update RenderObject Position, Rotation, Scale, Color members here
UpdateGame();
updateframe++;
}
}
};
pThread = new Thread(pRunnable);
pThread.start();
}
public void onDrawFrame(GL10 gl)
{
while( renderframe == updateframe )
{
// Wait for update to finish
}
for(int a=0;a<MAX_RENDER_OBJECTS;a++)
{
RenderObject ro = aRenderObjects[a];
// I do this in a function
ro.RenderPosition[0] = ro.Position[0];
ro.RenderPosition[1] = ro.Position[1];
ro.RenderPosition[2] = ro.Position[2];
ro.RenderPosition[3] = ro.Position[3];
// I do the same for Rotation, Scale, Color
}
renderframe++;
// When rendering scene I use RenderPosition, RenderRotation members when calling OpenGL API
RenderScene();
}
But when I run my game. The graphics are incorrect. It appears as if data is not in sync.
Any suggestion there on how to appropriately sync RenderObject data between Update and Render threads.
Thank you.
This style seems generally inefficient (spin-waiting loops that will block the two threads from working simultaneously) and dangerous (I don't know how Android implements this, but Java memory model does not guarantee that data from one thread is visible to another thread without going through a synchronized block).
My suggestion is this: the main thread is the rendering thread (GL contexts are thread-local, so you can't use it from another thread (though you can create another context on another thread to perform some data sharing)), and spawns the update thread. The main thread then goes on a loop, waiting on a mutex on every iteration. The update thread updates the game logic, and creates a new object representing everything that has to be drawn (e.g. a list of drawing commands). This object is then put on a queue and the main thread is woken (with notify()). The main thread then gets the drawing data and draws it.
You'll probably want to dismiss older sets of data in case the update loop is much faster than the render loop, but this general idea should get you started.
I'm making a game that displays some numbers on a canvas (score, time, etc).
The way that I currently do this is with the drawtext command on a canvas
// score is some int
draw(Canvas c) {
c.drawText(score+"", x, y, paintSyle);
}
I hear that object creation and garbage collection are expensive operations, and I think this is creating a new string every time it is called.
Right now my game with all bitmap drawing and everything jumps around from 25 to 60 fps. I'd like it to stay closer to the higher number and I'm trying to find ways to speed it up.
Would it be faster/better to make(or find?) some mutable subclass of string and work around this problem? Is there another way to solve this issue? Or is this just how it is?
Introduce two new private member variables String renderedScoreString and int rederedScore and rewrite your draw()-method like that:
draw(Canvas c) {
if (this.score != this.renderedScore || this.renderedScoreString == null) {
this.renderedScore = this.score;
this.renderedScoreString = Integer.toString(this.renderedScore);
}
c.drawText(this.renderedScore, x, y, paintStyle);
}
that should save you a lot! of object creations. You could also hide the boilerplate code behind a getter method, e.g. String getScoreString() which does the same, so you don't have it in the draw()-method.
A friend of mine tipped me in on a solution to this problem. When you want to draw something over time, one of the best (and simplest) mechanisms to do so is to split up what you need to do into two completely separate processes.
ie. Only use the draw command exclusively for drawing stuff, keep logic/assignment in Draw() to an absolute minimum.
private final long TIMER_PERIOD = 500;
private String timeString;
private Runnable updateRunnable;
private Handler updateHandler = new Handler();
public void onCreate(Bundle savedInstanceState) {
updateRunnable = new Runnable() {
#Override
public void run() {
timeString = GetTimeString();
updateHandler.postDelayed(updateRunnable, TIMER_PERIOD);
}
}
}
Draw(Canvas c) {
c.drawText(timeString, x, y, paintStyle);
}
In this example the Draw command simply takes timeString in its current state and draws it to the screen. This is highly efficient use of the draw function as it does not require any object creation, and no logic is present that is not immediately required for any drawing to occur. . In the background a Runnable is executing the run() function every 500 miliseconds (approximately). Simply update the Run() function with whatever logic you need to calculate the time (example has a dummy function GetTimeString())
I hope this is helpful.
I know I'm resurrecting a dead thread, but there is one extra optimisation you can add to this which restricts String creation to a one-time thing and thus only triggers the GC once at the start and not during the game (which is quite important for an android game).
Somewhere during the start of your game (onCreate, onResume, as part of a singleton during application startup, etc) create a large String[] which can hold the maximum score (my game fills an array of 10000, so the max score would be 9999). Then loop over it with a for loop, filling each index with a String.valueOf(i).
for (int i = 0; i <scoreStrings.length; i++)
{
scoreStrings[i] = String.valueOf(i);
}
Now, when you need to draw the score, just use the int you use to store the score in as an index to that array, and "hey, presto!", you get the correct string for your score.
canvas.drawText(scoreStrings[score], x, y, paint);