Incremental search in android using Volley library? - android

I have somehow implemented incremental search in android using AsyncTask. In incremental search an API is called for each character entered in edit text to get suggestions from server. For example,
User types a -> API is called.
User types ab -> API is called.
User types abc -> API is called.
This makes three API calls for a, ab and abc respectively. If a user is still typing then all the previous requests (e.g. requests of a and ab) will be cancelled and the last request (abc) will be served only to avoid delays.
Now I want to implement this using Volley library for better performance. Could anybody help me how can I achieve this functionality using volley specially the mechanism of cancelling all the previous request and serve the last request only to get suggestions from server.
NOTE: I couldn't find regarding this that's why posting it here. Please do guide me because I am new to android and really in need of answer.

First you need to implement a TextWatcher to listen to change in edit text.
Depending on the requirement of the changed text, you cancel and add a request to queue.
private RequestQueue queue = VolleyUtils.getRequestQueue();
private static final String VOLLEY_TAG = "VT";
private EditText editText;
...
TextWatcher textChangedListener = new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.length() != 0) {
// first cancel the current request with that tag
queue.cancelAll(VOLLEY_TAG);
// and then add a new one to queue
StringRequest stringRequest =
new StringRequest("http://blabla/servlet?param=" + s,
new Listener<String>() {
// request callbacks
};
stringRequest.setTag(VOLLEY_TAG);
queue.add(stringRequest);
}
}
};
editText.addTextChangedListener(textChangedListener);
Just remember that this design will gobble up bandwidth. A better way is to use Handler.post() to wait for several seconds before firing a request.

Related

How to synchronously wait the Result callback from Android to Flutter using the invokeMethod?

I am trying to synchronously call some Flutter methods from Android (Java) using the Flutter MethodChannel.
invokeMethod(String method, #Nullable Object arguments, MethodChannel.Result callback)
I already tried CountDownLatch, locks and even Threads but would just stop after calling the countdown await or synchronize(lock) or even thread.join.
public class MainActivity extends AppCompatActivity {
// code/methods initializations..
public String testData(){
final String[] result = new String[1];
final CountDownLatch latch = new CountDownLatch(1);
flutterMethods.invokeMethod("getString", "abcdefg", new MethodChannel.Result() {
#Override
public void success(Object o) {
result[0] = o.toString();
Log.e(TAG,"success);
latch.countDown();
}
#Override
public void error(String s, String s1, Object o) {
latch.countDown();
}
#Override
public void notImplemented() {
latch.countDown();
}
}
Log.e(TAG,"about to wait forever!");
latch.await();
Log.e(TAG,"done!");
return result[0];
}
Of course, this will work asynchronously without the waits, but I want to make this into a synchronous library function at some point. How can this be done?
This cannot be done.
Flutter architecture is unique in the sense that communication with the OS always asynchronous.
The principle is that Flutter and the native side sends messages to each others by both listening to the same port on the device.
This allows increased performances as there is no "language bridge" but comes at the cost of being forced to be asynchronous.
First of all, this is a really terrible idea and you likely shouldn't do it. Callbacks are fine in 99% of use cases.
If you still really want to though, a simple way would be to just use a while(!messageRecieved) { // do something } style loop. The key important factor here is that you can't actually suspend the thread, because if you do it will be unable to receive any messages.
If you don't want to cause unneeded CPU usage, you could try to do a small IO operation in the body of the loop.

Leak : Timer and TextWatcher

I'm working on an editText and a recyclerView.
My recyclerView is updated when I write letters in my EditText.
I put a Timer inside my textWatcher in order to avoid sending requests each time user write a letter.
searchDestinationEt.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
//There is nothing to do here
}
#Override
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
if (timer != null) {
timer.cancel();
}
}
#Override
public void afterTextChanged(final Editable s) {
timer = new Timer();
//we schedule this in order to avoid sending useless request.
//We wait the user is finishing writing before sending requests
timer.schedule(new TimerTask() {
#Override
public void run() {
((Activity) context).runOnUiThread(new Runnable() {
#Override
public void run() {
actionsListener.onDestinationSearch(s.toString());
}
});
}
}, DELAY_SEND_REQUEST);
}
});
It works well but leakcanary says that I have a leak in this part of code.
Any idea ?
Sorry for being late with the response, but did you try separating textwatcher like this ?:
TextWatcher for more than one EditText
Why do you use Timer and TimerTask for delayed, not recurring action? The easiest and the common way is to use just a regular Handler with postDelayed():
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//do somthing here
}
}, DELAY_SEND_REQUEST);
The leak occurs because you're starting a thread which has a reference to your context (fragment or activity). And until your thread is done - it won't be garbage collected.
That means, for example, if a user types something and you're waiting for a time to start requesting and meanwhile the user turns the phone and orientation change occurs - your activity/fragment will be recreated - but the old one (which started a thread and should be used when thread is done) is not gone and still present in memory.
Why are doing a request on the UI Thread? It blocks the UI, you know that right? I assume an AsyncTask may fit better.
What should you do?
Replace Timer with Handler and do the requests in a worker thread. Regarding the leak you have 2 options:
a) do nothing since the time for which your activity/fragment will be preserved is very small and it will be GCed after the request is done. (not recommended)
b) Utilize the AsyncTask and in the constructor of the AsyncTask pass the context (your listener) and store it as a weak reference object, like this:
private static class SomeWorkTask extends AsyncTask<Void,Void,Void>{
private WeakReference<ActionsListenerWithContext> weakListener;
public SomeWorkTask(ActionsListenerWithContext listener){
this.weakListener = new WeakReference<>(listener);
}
#Override
protected Void doInBackground(Void... voids) {
//do some work here
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
if(weakListener.get() != null){
weakListener.get().callYourCallbacks();
}
}
}
and then you call it
new SomeWorkTask(listener).execute();
Utilizing WeakReference wrapper is a common and recommended practice.

How can I make network calls very frequently

I have an auto complete feature which means every time the user types a letter, there is a network call to the server, the user can type very quickly and a lot
I usually use thirdparty libraries for network calls but this time they wouldn't work, I need to use AsyncTask by the looks of it.
I read that you can only execute an AsyncTask once. My question is, do I need to create an object of my AsyncTask everytime the user types a letter? is this the best approach?
I am aware of implementing Filterable in my adapter but I need to know about this without Filterable.
Instead of making network call when user type you can make a network call when user stops typing.
I was implement it for same case as yours.I have a AutocompleteextView and when user type i have to show him list of suggestions, but I call webservice when user stops typing.Below is my example -
private final static int DELAY_BEFOR_SEARCH = 2000;
Here is Autocomplete TextView Listener -
myAutocompleteTV.addTextChangedListener(new SearchListener ());
and here is Listener Class -
public class SearchListener implements TextWatcher{
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
#Override
public void afterTextChanged(Editable s) {
try{
mMessageHandler.removeCallbacks(mSearchRunnable);
if(s.length()>0){
if(Utility.isNetworkAvailable(mContext)){
mMessageHandler.postDelayed(mSearchRunnable, DELAY_BEFOR_SEARCH);
}
else{
Toast.makeText(mContext, getResources().getString(R.string.no_network_message),Toast.LENGTH_SHORT).show();
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
and here is Runnable -
Runnable mSearchRunnable = new Runnable() {
public void run() {
//TODO call your webservice here
}
}
for example if user type want to search 'Android' and he types 'Andr' ans stops typing for 2000 ms then my network call execute.

Text change should be detected after

In a AutoCompleteTextView I want to show the auto complete list after there is a pause of 1 sec by user in typing.
I tried using handler
handler.postDelayed( new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
if ( text.length() >= 3 ) {
// do something
} else if ( text.length() == 0 ) {
// do something
}
}
}, 1000 );
This code is a part of onTextChanged. So what it is happening is whenever there is a text change at that moment postDelayed is called and inside code will be called after a second. So how can I prevent that so the inner code is only called when there is a puase of 1 sec by user in typing.
e.g:
If I type Ind (pause of 1 sec) then inner code execute should happen.
But I type India then inner code should not execute for Ind, Indi, India.
Need your suggestions.
Create two static variables holding timestamps.
lastTimeStamp and currentTimeStamp, then you can do something like this:
#Override
public void run() {
if (currentTimeStamp - lastTimeStamp > 1000) {
// TODO Auto-generated method stub
if ( text.length() >= 3 ) {
// do something
} else if ( text.length() == 0 ) {
// do something
}
}
}
New approach:
At the beginning of your onTextChanged method, put a current TimeStamp in a class-variable.
Thereafter create an AsyncTask in the onTextChanged, which is just doing a
Thread.sleep(1000) in the doInBackground-method.
Then you make an if-statement in the postExcute method, checking if the difference between TimeStamp in the class-variable and the current TimeStamp, if this is larger than 1000 post your handler.
I just stood at the same problem.
I am calling Googles Geocode suggestion API onTextChanged() and that caused many issues.
Users typing would cause masses of API requests, most of them not required and that uses up the API (costs money) and reduces the experience the user receives from the app.
I considered the solutions here but I did not like any of them in the end, they seemed unclean or like a hack.
I did not want to make further calls into the UI to get the current value of the View (again) or check system times.
The original question actually already solves the problem, it just causes multiple runnables to be alive when a user types.
So the question should be: "How to remove my anonymous runnable before it causes trouble ?"
All you have to do it use the original approach (the delayed call using the anonymous runnable) and whenever the onTextChanged function is called you REMOVE any previous runnables.
You can also extend the code and check if the value really changed (like using global previous value String) so the runnable is not killed if the user entered a char and removed it again.
So all you need to do:
1. Make the Handler a variable of the class instead of creating it inside the onTextChanged.
2. At the begin of onTextChanged remove the runnables from the handler
Here is a fully functional example:
Handler onchangeHandler = new Handler();
String last_onTextChanged=""; // may speed up some cases of user input, can be removed
#Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3)
{
final String value = charSequence.toString();
if (value.length() < 3) {autoCompleteAdapter.clear();return;} // threshold of 3 chars
if (!last_onTextChanged.equals(value))
onchangeHandler.removeCallbacksAndMessages(null);
else
return;
last_onTextChanged=value;
onchangeHandler.postDelayed(new Runnable()
{
#Override
public void run()
{
// RUN CODE HERE, fill your autoCompleteAdapter etc.
}
},800);
}
There are 2 options:
Use Timer and invoke cancel() on previous tasks when you are going to run the next one.
Extend Runnable and pass in a string for this autocompletion. If this string is not equal to current string in view (that means user changed the string), then you should not display autocompletion.
Code example:
abstract class MyRunnable {
private String str;
public MyRunnable(String str) {
this.str = str;
}
}
...
handler.postDelayed(new MyRunnable(currentViewStr) {
#Override
public void run() {
if (currentViewStr.equals(str)) {
// show autocompletion
}
}
}, 1000);
you can also cancel the previous Runnable msg in message queue first in onTextChanged()

Safe way to stop a thread - Android

I'm trying to implement an auto-suggest like functionality for a part of my application. As a part of this, as a user types into a text field, I ping a web service for the results, parse the xml and update the listview with a list of returned results.
Here's the basic flow for my program. My question is if I'm stopping the thread the right way. Does calling thread.interrupt() suffice?
init() {
// <snip>
searchTxt.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable arg0) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
FetchResults();
}});
}
private void FetchResults()
{
if(mCurrentNetworkThread != null)
{
mCurrentNetworkThread.interrupt();
mCurrentNetworkThread = null;
}
mCurrentNetworkThread = new Thread(
new Runnable() {
public void run() {
List<NameValuePair> paramTable = new ArrayList<NameValuePair>(1);
paramTable.add(new BasicNameValuePair("searchText", searchTxt.getText().toString()));
String methodName = "GetSearchResults";
NetworkAccessClass nac = new NetworkAccessClass(paramTable, IP_ADDR, methodName, 0, this);
nac.startRequest();
}
});
mCurrentNetworkThread.start();
}
#Override
public void requestSucceeded(String responseMessage, int callID) {
parseResponseFromNetwork(responseMessage);
}
#Override
public void requestFailed(String responseCode, int callID) {
}
If that code is executed from an Activity you will most likely run into concurrency problems modifying something in the GUI thread from another thread without use of a Handler. The easy way around this i recommend using an AsyncTask. It solves any of the problems you will have in Android. These tasks can be canceled via the cancel() function and will be safe as far as any Threading exceptions in Android are concerned.
[Edit]
Make sure you check out Jon's comment below about using Loaders, a 3.0 function I wasn't aware of.
Since android 3.0, AsyncTasks will largely be replaced by Loaders
They can even be used down to Android 1.6 with the new compatibility library.
These links should help you along:
android-3-0-what-are-the-advantages-of-using-loadermanager-instances-exactly
Android Loader - Dev Guide
Try this Where to stop/destroy threads in Android Service class?

Categories

Resources