I'm trying to implement search autocompletion with android-query library
I have callback instance in my activity:
Callback:
class SearchCompleteCallback extends AjaxCallback<ItemSearchResult> {
public void callback(String url, ItemSearchResult searchResult, AjaxStatus status) {
Log.d("SCB", String.format("Url:%s\n Msg: %s\n Code: %s\n Error: %s",
url,
status.getMessage(),
status.getCode(),
status.getError()));
if (searchResult != null) {
Log.d("SCB", String.format("Status: %s\n Val: %s",
searchResult.getStatus(),
searchResult.getInnGroup().getItems()));
updateSearchResult(searchResult);
}
else {
Log.w("SCB", "Ajax failed");
}
}
}
Search routine, that called on text change:
private void doSearch(String query) {
ppApi.getSearchResult(query, searchCompleteListener);
}
and
APIClass
public class PPServerApi {
private AQuery aq;
private GsonTransformer transformer;
private static class GsonTransformer implements Transformer{
public <T> T transform(String url, Class<T> type, String encoding, byte[] data, AjaxStatus status) {
Gson g = new Gson();
return g.fromJson(new String(data), type);
}
}
public PPServerApi(AQuery newAq){
aq = newAq;
transformer = new GsonTransformer();
AQUtility.setDebug(true);
AjaxCallback.setTransformer(transformer);
}
public void getSearchResult(String itemName, AjaxCallback<ItemSearchResult> cb){
String url = "http://my.api.server/search?q=" + itemName;
aq.ajax(url, ItemSearchResult.class, cb.header("content-type", "application/json"));
}
}
So, the question is how to abort old queries before sending new one ?
(I don't need result of old queries if text in search field changed)
I've tried to call searchCompleteListener.abort() in doSearch(), but it causes exception in next going query:
08-09 20:59:10.551: W/AQuery(6854): get:http://my.api.server/search?q=abc
08-09 20:59:10.551: W/AQuery(6854): creating http client
08-09 20:59:10.561: W/AQuery(6854): java.io.IOException: Aborted
08-09 20:59:10.561: W/AQuery(6854): at com.androidquery.callback.AbstractAjaxCallback.httpDo(AbstractAjaxCallback.java:1569)
...
so, i can't perform even single query in this case.
There is no way of making android-query cancel an AJAX request once it has been started.
You'll have to use another library, sorry.
What you can do is to check if the request has become obsolete when it finishes.
You could do that by checking if the URL matches the latest URL you requested for
if (searchResult != null && url.equals(latestRequestUrl)) {
(note, you'd have to let getSearchResult return the URL)
You can use the droidQuery library instead. Using droidQuery, you can cancel all Ajax tasks using the call:
$.ajaxKillAll();
You can also perform your request with this:
$.ajax(new AjaxOptions().url(url).header("content-type", "application/json").type("json").dataType("GET").dataType("json").success(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
JSONObject json = (JSONObject) params[0];
//TODO handle json
}
}).error(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
AjaxError error = (AjaxError) params[0];
Log.e("Ajax", "Error " + error.status + ": " + error.reason);
}
}));
You can abort any aquery processing using this.
private AjaxCallback<String> ajaxCallback = new AjaxCallback<String>(){
#Override
public void callback(String url, String object, AjaxStatus status) {
//do your processing with server response
processInformation(result);
};
};
//on our previous code
query.ajax(remoteUrl,String.class,ajaxCallback);
public void cancelAquery(){
//this statement does cancel the request i.e. we won't receive any information on callback method
//ajaxCallback.async(null);
ajaxCallback.abort();
}
For more info, you can see this link https://laaptu.wordpress.com/tag/android-cancelling-aquery/
just call aq.ajaxCancel() for more details refer to the API docs
http://android-query.googlecode.com/svn/trunk/javadoc/com/androidquery/AbstractAQuery.html#ajaxCancel()
Related
I am using mvvm architecture I would like to notify view when volley post request is successful, what i could do is to instantiate ViewModel in appRepository class and then post values to a liveData, but i guess that's not a good approach as I haven't seen a similar practice. Can anyone suggest me a good approach to return my response to ui, or at least notify that post request has been successful.
From fragment/View I trigger this method
// save data to api
checkInViewModel.updateEventPersonEntity(eventPersonsEntity);
ViewModel forwards it to apprespository
public void updateEventPersonEntity(EventPersonsEntity eventPersonsEntity) {
mRepository.updateEventPersonEntity(eventPersonsEntity);
}
AppRepository.Java class
public void updateEventPersonEntity(EventPersonsEntity eventPersonsEntity) {
executor.execute(() -> {
// mDb.eventPersonsDao().update(eventPersonsEntity);
if (isNetworkAvailable(context)) {
post_updateEventPersonEntity(eventPersonsEntity);
}
});
}
private void post_updateEventPersonEntity(EventPersonsEntity eventPersonsEntity) {
Map<String, Object> params = new HashMap<>();
params.put("EventPersonId", eventPersonsEntity.getEventPersonId());
params.put("EventId", eventPersonsEntity.getEventId());
params.put("PersonId", eventPersonsEntity.getPersonId());
params.put("CashStart", parseDoubleToGerman(eventPersonsEntity.getCashStart()));
params.put("CashEnd", parseDoubleToGerman(eventPersonsEntity.getCashEnd()));
params.put("StartingTime", String.valueOf(eventPersonsEntity.getStartingTime()));
params.put("EndingTime", String.valueOf(eventPersonsEntity.getEndingTime()));
params.put("isChekcedIn", eventPersonsEntity.getIsCheckedIn());
params.put("isChekcedOut", eventPersonsEntity.getIsCheckedOut());
JSONObject objRegData = new JSONObject(params);
String eventPersonApi = APP_URL.EVENT_PERSONS_API + eventPersonsEntity.getEventPersonId();
RequestQueueSingleton.getInstance(context).objectRequest(eventPersonApi, Request.Method.PUT, this::onSuccess_updateEventPersonEntity, this::onError, objRegData);
}
private void onError(VolleyError error) {
Log.d(APP_REPOSITORY_TAG, "requestError: " + error);
}
private void onSuccess_updateEventPersonEntity(JSONObject jsonObject) {
// notify ui
}
You can do this same as you did for your success response logic in repository. Simply create new callback interface:
interface OnEventUpdatedListener{
void eventUpdated();
}
Then, update your method to look like this, passing the listener to the actual method that does the work:
public void updateEventPersonEntity(EventPersonsEntity eventPersonsEntity, OnEventUpdatedListener listener) {
mRepository.updateEventPersonEntity(eventPersonsEntity, listener);
}
Pass this inside your:
if (isNetworkAvailable(context)) {
post_updateEventPersonEntity(eventPersonsEntity, listener);
}
After that, in your onSuccess() method simply call:
private void onSuccess_updateEventPersonEntity(JSONObject jsonObject) {
listener.eventUpdated();
}
Finally, you will have the info when the update happens, in the calling site, if you call your repository like this:
updateEventPersonEntity(null, new OnEventUpdatedListener() {
#Override
public void EventUpdated() {
// Do your logic here
}
});
In my Android app, I'd like to implement success and error callbacks for when I get reading passages from my backend. In iOS, it would look like this:
In my Passage.h:
-(void)getPassagesWithSuccessCallback:(void (^)(NSArray<Passage *> *))success errorCallback:(void (^)(NSString *))errorString;
In my Passage.m:
-(void)getPassagesWithSuccessCallback:(void (^)(NSArray<Passage *> *))success errorCallback:(void (^)(NSString *))errorString {
MyApiInterface* api = [MyApiInterface sharedInstance];
[api sendGetRequestTo:#"passages" successCallback:[Passage modelListCallback:success] errorCallback:error];
}
In my Android app, I'm using Volley to handle my API requests, but I want to further encapsulate this API interfacing by having a Passage.java class with a public static void method that gets the passages. Something like this:
public static void getPassagesForFirebaseUser(FirebaseUser user, Context context) {
final String url = URL_BASE + "/passages.json" + "?auth=" + user.getToken(false);
final JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
// convert JSON into ArrayList<Passage> object
// pass on this array of Passages in the success completion listener of the method that called this
// just like iOS does success(passages)
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// convert error to string
// pass on this errorString in the error completion listener of the method that called this
// just like iOS does error(errorString)
}
});
Volley.newRequestQueue(context).add(request);
}
Is there any way to get this kind of implementation flow?
You can use an Interface
public interface ICallbacks {
public void onResponse(JSONObject response);
public void onError(VolleyError error);
}
Then in your routine code just put a new instance of Callbacks (depending on ide that you work could autogenerate the methods)
public static void getPassagesForFirebaseUser(FirebaseUser user,
Context context, ICallbacks events) {
//here code and call ICallbacks methods
if(result){ events.onResponse(response); }
if(error){ events.onError(err); }
}
ultimately you can call the method with :
getPassagesForFirebaseUser(user, context, new ICallbacks(){
#Override
public void onResponse(JSONObject response){
//Success !!!
}
#Override
public void onError(VolleyError response){
//Error !!!
}
});
Sorry for my English, hope this help !
I am using Volley to make Http requests to my Web Api.
However I am having trouble getting the values from my api calls due to the asynchronous nature of Volley.
I have read that using a callback function could help with this issue, however I do not know how to implement such a solution.
How would I go about implementing a callback function in the following scenario?
public class Main
{
String name;
WebServiceConnections wsc = new WebServiceConnections();
name = wsc.getNameFromWeb();
System.out.println("Name: " + name);
}
public class WebServiceConnections
{
public String getNameFromWeb()
{
String url = "http://nameservice.net/GetName";
JsonArrayRequest req = new JsonArrayRequest(url,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
try {
return response.getString("Name");
}
catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d(TAG, "Error: " + error.getMessage());
}
});
}
}
The problem with this code is that the variable "name" in Main will be null when it is called by the print statement as the asynchronous method in the WebServiceConnections class will not be finished by time the print statement is called.
Is a callback a good way to solve this problem?
Your code doesn't compile - you can't return a value in a method with void return type.
Your onResponse method is the callback. Perform the print within the method itself.
This question already has answers here:
How to use Jsoup with Volley?
(3 answers)
Closed 6 years ago.
I'm trying to parse data from my server in Java with jsoup. I wrote a new function and it should return data in string format, but it returns blank string. Here is my code:
public String doc;
public String pare(final String url){
Thread downloadThread = new Thread() {
public void run() {
try {
doc = Jsoup.connect(url).get().toString();
}
catch (IOException e) {
e.printStackTrace();
}
}
};
downloadThread.start();
return doc;
}
You're returning the doc object immediately, before the thread has had a chance to add any data to it, so it should be no surprise that this returns empty. You can't return threaded information in this way, and instead will need to use some type of call-back mechanism, one that notifies you when the thread is done and when data is ready to be consumed.
On android platform, you shouldn't ask Jsoup to download anything for you. Under the hood, Jsoup make use of HttpUrlConnection. This class is notoriously slow and has some known issues.
Use a faster alternative instead: Volley.
Here is the function in your post taking advantage of Volley. In the following sample code, I'm using a CountDownLatch to wait for the data.
private static RequestQueue myRequestQueue = null;
public String pare(final String url) throws Exception {
final String[] doc = new String[1];
final CountDownLatch cdl = new CountDownLatch(1);
StringRequest documentRequest = new StringRequest( //
Request.Method.GET, //
url, //
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
doc[0] = Jsoup.parse(response).html();
cdl.coutDown();
}
}, //
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("MyActivity", "Error while fetching " + url, error);
}
} //
);
if (myRequestQueue == null) {
myRequestQueue = Volley.newRequestQueue(this);
}
// Add the request to the queue...
myRequestQueue.add(documentRequest);
// ... and wait for the document.
// NOTA: User experience can be a concern here. We shouldn't freeze the app...
cdl.await();
return doc[0];
}
I totally agree with the above answer. You can follow any of the below tutorials for fetching data from server
http://www.androidhive.info/2014/05/android-working-with-volley-library-1/
http://www.vogella.com/tutorials/Retrofit/article.html
These two are the best libraries for Network calls in android
Before the return statement add a downloadThread.join(). This will wait until the thread has finished and put the response into doc. But: Doing so you will loose all benefit from the asynchronous execution, it's behaving the same as you just would code:
public String pare(final String url){
return Jsoup.connect(url).get().toString();
}
I'm using Phonegap to develop an app. I've downloaded a camera plugin, however, I'd like to make a Javascript call from within the plugin.
In the Java file for the camera plugin I have done the following;
private class sendJS extends CordovaActivity {
public void sendcommand() {
this.sendJavascript("alert('1337')");
}
}
#Override
public void onClick(View v) {
sendJS test = new sendJS();
test.sendcommand();
}
However, when the onclick is triggered nothing happens...
I've also tried super.sendJavascript() and super.loadUrl() but it didn't work.
Thanks.
You have two ways to comunicate to your javascript code. The first one is injecting code to webview via .loadUrl(...) method. The second one is via a callback in response to a javascript->native-plugin(java) call.
You can see callback response in execYourJavaMethod() and injecting in sendcommand()
private class sendJS extends CordovaActivity {
#Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
// Implement here your calls from javascript
boolean result = false;
if ("yourJavaMethod".equals(action)) {
JSONObject options = args.optJSONObject(0);
result = execYourJavaMethod(options, callbackContext);
}
return result;
}
public boolean execYourJavaMethod(JSONObject options, CallbackContext callbackContext) {
// This will inject an event to your javascript code
this.sendcommand();
boolean iWantToCallSuccessCallbackWithData = false;
if (iWantToCallSuccessCallbackWithData) {
// This will call your success callback with some data
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, "Your data back to javascript"));
} else {
// This will call the success callback with no data
callbackContext.success();
}
return true;
}
public void sendcommand() {
String event = String.format("javascript:cordova.fireDocumentEvent('yourEventHere', { 'param1': '%s' });", "some string for param1");
this.webView.loadUrl(event);
}
}
From javascript side you should register the listener for your event:
document.addEventListener('yourEventHere', function(e) {
alert(JSON.stringify(e));
});
To comunicate to your Java plugin:
myPlugin.doSomethingInJava = function (successCallback, failureCallback) {
cordova.exec(successCallback, failureCallback, 'sendJS', 'yourJavaMethod', []);
};