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...
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'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'm implementing a location suggestion activity which populates suggestions from an external server as the user types in a text view. I'm using an AsyncTask to fetch suggestions each time the text in the text view changes. When a new letter is typed, we cancel the task that already exists and execute a new one. Most of the time doInBackground starts immediately after execute is called, but other times it can take a few seconds. (Once doInBackground starts, performance is fine.)
Set listener:
private void init() {
// respond to any text change
textView.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(final CharSequence s, int start, int b, int c) {
showSuggestions(s.toString());
}
});
}
Here we start a new task and cancel the previous one:
private void showSuggestions(String query) {
// suggestionsTask is an instance variable of the activity
if (suggestionsTask != null) {
suggestionsTask.cancel(true);
}
suggestionsTask = new AsyncTask<String, Void, List<Suggestion>>() {
#Override
protected void onPostExecute(List<Suggestion> resultList) {
// set suggestions with adapter - CHANGES STATE
}
#Override
protected List<Suggestion> doInBackground(String... query) {
// one local db call for recent searches - DOES NOT CHANGE STATE
// one network call to external server - DOES NOT CHANGE STATE
// return results
}
};
suggestionsTask.execute(query);
}
Is there a better threading mechanism to use for this? Do you know why there is a delay between execute and doInBackground?
From the AsyncTask reference:
A task can be cancelled at any time by invoking cancel(boolean). Invoking this method will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object), instead of onPostExecute(Object) will be invoked after doInBackground(Object[]) returns. To ensure that a task is cancelled as quickly as possible, you should always check the return value of isCancelled() periodically from doInBackground(Object[]), if possible (inside a loop for instance.)
So you're actually not canceling the doInBackground() step unless you're manually checking whether isCancelled() is set periodically in doInBackground. In most versions of Android, all AsyncTasks share a single thread, so one has to finish before the next can start. So that's the reason for your delay, but I don't have enough information (i.e., you didn't post the code) from your doInBackground() code to come up with a suggestion on where to check for isCancelled().
If being able to cancel the previous task not possible for some reason, you can also try making your AsyncTasks execute in parallel, using executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR as the same documentation suggests, but with what you're trying to do that seems like it could cause some frustrating threading issues which would probably be worse than the one you're having right now.
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 use AsyncTask to download a string and return the string. I want to use AsyncTask because it might take a while.
One problem is that nowhere on the internet can I find an example of an AsyncTask returning any kind of value. So I took the example in the Commonsware book and modified it to return a value and I get the value as follows:
String mystr = new AddStringTask().execute().get();
While this works, it seem that this line of code is waiting for the return value and therefore synchronous. There must be some way to have an event trigger with the results of the AddStringTask.
How is that done?
Thanks, Gary
An AsyncTask cannot return a value, because to get the returned value you would have to wait before the task is finished. That would make the AsyncTask meaningless.
Instead, you should move your code in onPostExecute() (which runs on the UI thread, if this is what you worry about). This is where you handle the value returned by doInBackground() and typically update the UI or show an error message.
Also if you wanted to implement a more general AsyncTask you could implement something like the following to compartmentalize your code inside the activity.
#Override
protected void onPostExecute(Bitmap r){
if (r != null) {
processListeners(r);
}
}
protected void processListeners(Object data) {
for (final AsyncTaskDone l : listeners) l.finished(data);
}
public void addAsyncTaskListener (final AsyncTaskDone l){
listeners.add(l);
}
Where AsyncTaskListener is an interface with one function called finished implemented in the Activity the same way an onClickListener would be.