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.
Related
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.
I'm learning RxJava so please be gentle. I've watched the tutorials, done the reading, searched SO, however, I'm still having some problems transforming my AsyncTaskLoader. For some reason, I can't find a pattern of operators to achieve my task (although I think it's a common one). What I'm trying to do is the following: return an Observable my fragment could subscribe to. The observable should do the following on subscribe:
1) Fetch data from the local database by doing 2 queries, running some logic and returning results;
2) Fetching data from API;
3) Synchronising the new API data with the database;
4) Repeating step one and returning results;
So far I've transformed my db calls and my API calls to return observables. I'm trying to understand how I can emit the cold results and continue with the chain. I could probably keep the two operations separately, and use the same subscriber to subscribe to both? But I'm not sure how that would work if my new loader-replacement class returns an observable... Also I don't really need to process the results from the second observable - I just need for the first one to replay when the second one finished.
So far I have the following:
public Observable<StuffFetchResult> getColdStuff() {
return Observable.zip(mDataSource.listStuff(), mDataSource.listOtherStuff(),
(stuff, moreStuff) -> {
List<Stuff> mergedList = new ArrayList<>();
// do some merging stuff
return new StuffFetchResult(mergedList);
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
Assume I also have getHotStuff() that will do the API call and the synchronisation with the database, if that's the right approach, and return the same Observable. However, I'm stuck on the next step - how can I restart the first observable to replay once hotStuff has completed, without adding another subscriber?
EDIT:
I've made some progress and I think all I need now is to join it all up. I have my two methods:
1) getColdStuff() is pretty much as described above
2) getHotStuff() will do call to the API, synchronise with the database, and return an Observable. The idea was to call getColdStuff() again after getHotStuff() has finished in order to refresh the UI, so actual result returned from getHotStuff() can be ignored. All it needs to do is to trigger getColdStuff() once done.
I've tried the suggestion in the answer to and created the following:
BehaviorRelay<Observable<StuffFetchResult>> callSequence = BehaviorRelay.create();
Observable<StuffFetchResult> valueSequence = Observable.switchOnNextDelayError(callSequence.toSerialized());
valueSequence.subscribe(new Subscriber<StuffFetchResult>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(StuffFetchResult result) {
// UI stuff
}
});
callSequence.call(loader.getColdStuff());
I can subscribe to valueSequence here and use callSequence.call(loader.getColdStuff());, which will run the first method and produce results in onNext() of my subscription, which I can use for my UI. However, I'm not sure how to run getHotStuff() in parallel and also do a different action on it when it returns. Also getHotStuff() returns a different type of Observable so I can't really use the same callSequence?
EDIT 2
Using two subscribers, I can achieve the required behaviour I think. Not really sure if that's the right way to go about it though.
loader.getHotStuff()
.subscribeOn(Schedulers.io())
.subscribe( new Subscriber<Object>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(Object stuffWeDontCareAbout) {
callSequence.call(loader.getColdStuff());
}
});
if i understand your scenario correctly, you may want something like that -
BehaviorSubject<Observable<T> callSequence = BehaviorSubject.create();
Observable<T> valueSequence = Observable.swithOnNextDelayError(callSequence.toSerialized());
your subscriber will be listening to the valueSequence, and whenever you need to "restart", you will call this -
callSequence.onNext(call.cache()); // *call* is Observable<T>
(i leave the .subscribeOn/.observeOn configuration to you)
i have an android mobile app and im trying to check if a specific LatLng is at water, so im using google static map api to get an image of the location, and then to check if the image is blue.
im using this code -
private boolean result;
public boolean IsWater(LatLng position)
{
imageView = (ImageView) this.findViewById(R.id.imageView);
checkText= (TextView) this.findViewById(R.id.checkText);
String lati = Double.toString(position.latitude);
String longi = Double.toString(position.longitude);
String url = "http://maps.googleapis.com/maps/api/staticmap?center="+lati+"," + longi + "&zoom=20&size=1x1&style=element:labels%7Cvisibility:off&style=element:geometry.stroke%7Cvisibility:off";
Picasso.with(MainActivity.this).load(url)
.into(imageView, new com.squareup.picasso.Callback() {
#Override
public void onSuccess() {
Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
int pixel = bitmap.getPixel(0, 0);
int blueValue = Color.blue(pixel);
if(blueValue>250)
result =true;
}
#Override
public void onError() {
result =false;
}
});
return result;
}
the problem, i think, is that it is not synchronized, and IsWater get to the last line and return a null for result before the onSuccess kicks in...
any thoughts?
Picasso loads images on a background thread by default. The operation you are running is asynchronous. Therefore, it does not block your method from returning result before the onSuccess callback has been called.
The problem is Picasso is running Async. within the calling method "isWater", so what ends up happening is the method will return 'false' because instead of waiting on Picasso to finish because it isn't in serial execution. This is due to the function call's stack frame being popped off the stack once it reaches the return statement.
What you need to do is the following by using a Target.
// make sure to set Target as strong reference
private Target loadtarget;
public void loadBitmap(String url) {
if (loadtarget == null){
loadtarget = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
// do something with the Bitmap
handleLoadedBitmap(bitmap);
}
#Override
public void onBitmapFailed() {
}
};
}
Picasso.with(this).load(url).into(loadtarget);
}
public void handleLoadedBitmap(Bitmap b) {
// do something here
}
This code was taken from here, and should offer you some insight on how to get it work for your goal.
A Target is essentially an object that holds the bitmap you need so it is still in memory. Generally used for custom view objects though as a field. Here is documentation Target docs
Asynchronous execution is one of the hardest things to wrap ones head (and subsequently ones code) around. In all of the JavaScript frameworks I've used, the network communication is done in a background thread. The intended effect is that the User Interface thread is left free to keep the user from thinking that things locked up. Mouse-overs and tool-tips will all still work, while a background thread is dragging data out of a slow server.
The code patterns, on the other hand, aren't as nicely shaped.
My problem is/was still basically thinking linearly, or functionally, instead of embracing the event-driven nature of modern JavaScript: Passing a function to an asynchronous method to completely handle that response. Not just return a value, but perform the full task that the value was needed for. The callback can call the other functions to assist with that task, and may be able to fill in a cache (of whatever sort) so that other functions that may need this data do not necessarily have to wait for another response. This often (to me) feels backwards from the logic pattern I was following to solve the original purpose of the code.
I've stumbled on this pattern-flip many times, coming from C/C++ as my first programming language. It can sometimes help to avoid the anonymous function pattern of callback definition and define one's callback functions with names, then pass the name to the asynchronous call, but that is extra steps and extra memory use in the long run. The big hurdle is thinking in terms of Event and EventHandler, versus function and data.
I hope this helps a little.
I am getting this error in my Activity where I use Parse SDK. The whole code is here, but the code is huge and the crash is not even giving me the line of code where it is occuring. I searched a lot but found nothing about this error. Can anyone tell me what exactly this error means?
Screenshot:
Since adding a code example, I will write an answer.
You are using AsyncTask to do multiple queries and to know when these finish.
Parse recently added Bolts to their API (1.7.0 or 1.7.1) https://github.com/BoltsFramework/Bolts-Android
With Bolts you can do the same as you can with Promises in javascript, in case you are familiar with that.
A simple example deleting all objects matching a query:
findAsync(query).continueWithTask(new Continuation<List<ParseObject>, Task<Void>>() {
public Task<Void> then(Task<List<ParseObject>> results) throws Exception {
// Collect one task for each delete into an array.
ArrayList<Task<Void>> tasks = new ArrayList<Task<Void>>();
for (ParseObject result : results) {
// Start this delete immediately and add its task to the list.
tasks.add(deleteAsync(result));
}
// Return a new task that will be marked as completed when all of the deletes are
// finished.
return Task.whenAll(tasks);
}
}).onSuccess(new Continuation<Void, Void>() {
public Void then(Task<Void> ignored) throws Exception {
// Every comment was deleted.
return null;
}
});
The return Task.whenAll(tasks); returns a task that fires onSuccess only when all the tasks in the tasks arraylist has completed.
Not only does this rely on ParseĀ“ own background management, this example also makes all the tasks run in parallel, so is generally faster.
In your situation, you would simple need to create an ordinary method that:
Use the new built-in functions to return a task for both query1 and query2
Add those to an arraylist of tasks
return Task.whenAll(tasks)
Lets say this method is loadPicsInBg, then to use it:
loadPicsInBg().onSuccess(new Continuation<Void, Void>() {
public Void then(Task<Void> ignored) throws Exception {
// all the queries completed
return null;
}
});
I know this is a huge refactor and maybe you can do fine with the simpler callback approach, but Bolt indeed gives more power over the complex queries if used correctly. Furthermore it avoids the problem with nested queries creating a ever increasing indentation in the code making it difficult to read.
I'm trying to get an AutoCompleteTextView(ACTV) to display results I'm a getting from a network resource. I have set the completion-treshold to 2 and I can see that the request is fired when I enter to characters.
The result I am getting is the correct one. Lets say I write "ca", and I get the result "car" as an autocompletion. I have a callback function which receives the result from an AsyncTask and puts the result into the ArrayAdapter. Then I call .showDropDown() on the ACTV and an empty dropdown is shown (half the size of a normal element). Then if I enter the last letter "r" and the ACTV shows "car", the dropdown is shown and the result is suddenly in the list.
The same happens if I have entered two characters (which returns a valid result), and the remove the last letter. When the letter is removed, "car" is shown as an autocompletion value.
Has anyone had this problem? It looks like the adapter is filled with the result, but the result does not show until the next action I do. I have also tried to run .notifyDataSetChanged() after I have added the result to the adapter, but that should not be needed, or?
Without seeing your code, it's hard to tell what could be going on. But the first thing that comes to mind is that your network request is happening on a different thread, and therefore your performFiltering() may be returning an empty result set prematurely. At that point, publishResults() is returning the empty result, and your dropdown is empty. Later, your AsyncTask will get its result back, and you add the results into the adapter's list, but for one reason or another, it doesn't get displayed yet.
I think you may be mistaken about the need for AsyncTask though. The Filter object is already doing something similar to AsyncTask: performFiltering() is done in a background thread, and publishResults() is called from the UI thread, after performFiltering() is finished. So you can do your network request directly in performFiltering(), and set the results into the FilterResults object, and you won't have to worry about the network request being too slow and causing problems in your UI.
An alternative solution, which is slightly more complicated, but it's what I'm doing in my Filter object (due to existing architecture that does API calls in the background, using an asynchronous callback instead of the blocking/synchronous step as required for performFiltering()), is to use a synchronized object with wait()/notify() for doing cross-thread monitoring, so the effect is the same as doing the network request directly in performFiltering(), but it's actually happening in multiple threads:
// in Filter class..
protected FilterResults performFiltering(CharSequence constraint) {
APIResult response = synchronizer.waitForAPI(constraint);
// ...
}
// callback invoked after the API call finishes:
public void onAPIComplete(APIResult results) {
synchronizer.notifyAPIDone(results);
}
private class Synchronizer {
APIResult result;
synchronized APIResult waitForAPI(CharSequence constraint) {
someAPIObject.startAsyncNetworkRequest(constraint);
// At this point, control returns here, and the network request is in-progress in a different thread.
try {
// wait() is a Java IPC technique that will block execution until another
// thread calls the same object's notify() method.
wait();
// When we get here, we know that someone else has just called notify()
// on this object, and therefore this.result should be set.
} catch(InterruptedException e) { }
return this.result;
}
synchronized void notifyAPIDone(APIResult result) {
this.result = result;
// API result is received on a different thread, via the API callback.
// notify() will wake up the other calling thread, allowing it to continue
// execution in the performFiltering() method, as usual.
notify();
}
}
However, I think you may find that the easiest solution is to just do your network request synchronously, directly in the performFiltering() method. The above code example is just one possibility, if you already have the architecture in place for asynchronous/callback-driven API calls, and you don't want to change that behavior in order to get synchronous results in performFiltering().
I think Joe's answer is the way to go. However, I think you should use CountDownLatch instead of wait/notify.
The reason is, with wait/notify, you risk a race condition if your API actually return super fast before you start "wait()"... in this case, notify won't have an effect and wait() will wait indefinitely.
With Latch, the code will look like this (copied from Joe and modified):
// in Filter class..
protected FilterResults performFiltering(CharSequence constraint) {
APIResult response = synchronizer.waitForAPI(constraint);
// ...
}
// callback invoked after the API call finishes:
public void onAPIComplete(APIResult results) {
synchronizer.notifyAPIDone(results);
}
private class Synchronizer {
APIResult result;
CountDownLatch latch;
synchronized APIResult waitForAPI(CharSequence constraint) {
latch = new CountDownLatch(1);
someAPIObject.startAsyncNetworkRequest(constraint);
// At this point, control returns here, and the network request is in-progress in a different thread.
try {
// Will wait till the count is 0...
// If the count is already 0, it'll return immediately.
latch.await();
// When we get here, we know that someone else has just called notify()
// on this object, and therefore this.result should be set.
} catch(InterruptedException e) { }
return this.result;
}
synchronized void notifyAPIDone(APIResult result) {
this.result = result;
// API result is received on a different thread, via the API callback.
// countDown() will wake up the other calling thread, allowing it to continue
// execution in the performFiltering() method, as usual.
latch.countDown();
}
}
Lastly, I don't have enough credit to post a comment, otherwise I would have...