Every time my mobile app needs to access the DB, I have to write this piece of code.
//NetworkManager.getInstance().start();
ConnectionRequest request = new ConnectionRequest();
request.setUrl(WebConstants.HOST+"URL");
request.setPost(false);
request.addArgument("x",y);
request.addResponseListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
NetworkEvent event = (NetworkEvent)evt;
Exception error = event.getError();
if(error != null) {
return;
}
ConnectionRequest request = event.getConnectionRequest();
byte[] result = request.getResponseData();
String response = new String(result);
/** Process the string response **/
}
});
NetworkManager.getInstance().addToQueue(request);
This becomes really tedious when the web app constantly accesses the DB and for every call I need to copy, paste and modify same code. I tried to create a method and return the string response, but since the web service is done asynchronously, the method that calls the web service has to finish before web service method process the response. Therefore, I can't simplified this part of code. Any ideas on how to proceed?
Encapsulate the code in
/** Process the string response **/
into a Callable, then you can create a generic method that calls
callable.call()
You can use addToQueueAndWait(request) which blocks until the network operation completes and is completely safe to use on the EDT since it uses invoke and block.
You can also use something like the webservice wizard which encapsulates web calls in method calls.
Related
Let's say I have an android app with a single activity that contains a button. When I click the button I'd like to make several requests to a rest API that return JSON response. Then I parse the response to a java object and persist it with Room. For the http requests I implemented a Volley request queue as singleton.
The requests are asynchronous and deliver their responses back to the UI thread. There I let Room persist the objects.
I send my http request like this:
RestService.requestSomeData(context, objectId, new ResponseListener() {
#Override
public void onRestSuccess(String response) {
// parse response JSON
// call the insert method
}
#Override
public void onRestError(int code, String errorMessage) {
// handle error
}
}
Since Room forces you to dispatch the queries to worker threads, I'm using RxJava to handle the task. So, for example my Insert method returns an ArrayList of the IDs of the inserted objects wrapped in a Single<ArrayList<Integer>>. Then I call the Insert method and subscribe to the result like this:
myDisposable = MyDatabase.getInstance().myDao()
.insert(myObject)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(idList -> {
Log.d(TAG, "IDs inserted: " + idList.toString());
}, Throwable::printStackTrace);
However, I want to chain multiple requests to the server and then get notified when all are complete and the DB insertions are ready in order to update the UI (e.g. display confirm message, disable the save button). I read numerous articles but nowhere I could find how to perform this apparently easy task. Basically what I want to achieve is:
// a some sort of container for all the observables I get from the database insertions
private Object aPoolOfObservables;
RestService.requestSomeData(context, objectId, new ResponseListener() {
#Override
public void onRestSuccess(String response) {
// parse response JSON
aPoolOfObservables.add(MyDatabase.getInstance().myDao()
.insert(myObject)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()));
}
}
// repeat this n-times more
...
aPoolOfObservables.subscribe(new Listener() {
#Override
public void onComplete() {
// update UI
}
});
Then perform this request multiple times and add the responses to the collection of Single<> (or Maybe<> or Flowable<>) responses and subscribe not to every single stream but to the collection, because I only care that all the operations are complete. Chaining them by firing a request in the onRestSuccess of the previous one seems like a pretty awful solution.
Do you know if there is a RxJava mechanism that allows this?
Is there any general approach/design pattern to handle this situation? I can think of numerous cases when you'd like to e.g. enable a button only after multiple requests have been performed and delivered results. How do you create and subscribe to such event in the context of RxJava? I haven't worked a lot with reactive data so any knowledge will be appreciated.
You can wrap each request in a Single<Pair<ArrayList<Integer>, String>> to store each JSON responses per request. Then, execute them all together with Single.zip(...)
private CompositeDisposable disposables;
private ArrayList<Single<Pair<ArrayList<Integer>, String>>> singles;
RestService.requestSomeData(context, objectId, new ResponseListener() {
#Override
public void onRestSuccess(String response) {
// parse response JSON
// kotlin syntax
singles.add(
MyDatabase.getInstance().myDao().insert(myObject)
.flatMap { ids: ArrayList<String> ->
// transform single to include JSON response
return#flatMap Single.just(Pair(ids, response))
}
);
}
}
// kotlin syntax
disposables.add(
// execute all singles
Single.zip(singles) {}.subscribe()
);
I have phone contact numbers list stored in an array and called contactsString[]
and in an online database registered users numbers
I want to count how many registered users are there
and there is my code
for (i=0;i<contactsString.length-1;i++){
Phone phone=new Phone();
phone.phone=contactsString[i]
WebService.getInstance().getApi().checkNumber(phone).enqueue(new Callback<MainResponse>() {
#Override
public void onResponse(Call<MainResponse> call, Response<MainResponse> response) {
if (response.body().status==1){
availableUsers++;
}
}
#Override
public void onFailure(Call<MainResponse> call, Throwable t) {
}
});
}
my problem is the web service response is delayed so it don't count and availableUsers is printed it's initial value which is 0
I would try better sending an array of Phone objects. In this way you would get the correct answer in 1 call.
I would never do this in the way you implemented: imagine you have 500 contacts: you will be doing 500 calls to your server. Now imagine you have 100000 users with 500 contacts each
Try to customize your api call in this format. Which uses async task class.
private void phoneContact() {
new AsyncTask<String,Void,String>() {
#Override
protected String doInBackground(String ... params) {
try {
Platform http = Url_Contacts;
JSONObject resp = http.search(what,where);
Log.d(TAG, "Response: " + resp.toString());
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
return "";
}
}.execute();
}
Make sure that your service works well and the format of json with value status in there.
In onResponse, run on UIThread to update your View with the availableUsers.
The enqueue method is asynchronous. So your code should respect the multithreaded nature of it.
There are many approaches you can take:
Replace enqueue() method with execute(). But that makes all the calls synchronous. If you call it in UI Thread then whole app can stutter. Probably you will get NetworkOnMainThreadException. Not a good approach anyway.
Use RxAndroid or RxJava with Observer pattern.
Simple solution. Create a variable int callsFinished = 0;. In onResponse increment that variable. Then if that callsFinished == contactsString.length that means all calls have been done.
In your activity add a listener
void onAllCallsFinished(int availableUsers) {
//do what you want with availableUsers information
}
Call onAllCallsFinished(availableUsers) when callsFinished == contactsString.length.
There you can do what you want with that data. Update a view, call another service.
In a Struts application some controllers write Javascript code to the HTTP response (this JS code will be executed when the submit ends). Do you think it's a bad approach or is acceptable ? Would it be better to set an attribute and move the JS code to the view ?
#UserInterfaceEvent
public void aceptar_onClick(FormActionContext ctx) {
IGenerateDocListOutputBF bproxy = ProxyFactory.newBusinessProxy(getBusinessComponent(), IGenerateDocListOutputBF.class);
GenerateDocListOutputForm form = (GenerateDocListOutputForm) ctx.form();
String mode = form.getDetailMode();
removeErrors(ctx);
GemaBrowseControl browse = (GemaBrowseControl) ctx.session().getAttribute("outputs");
SelectedData data = browse.getAllSelectedData(ctx, true);
try {
bproxy.generateOutputs(data.getSelectedDboids(), form.getDboid(), false);
if (GemaAppErrorMgr.currentScopeHasErrors()) {
ctx.request().setAttribute(DetailRequest.MODE, mode);
showErrorPopUp(ctx);
} else {
String javascript = "<script>window.opener.refreshDivDocsRelated(); window.close();</script>";
ctx.response().getOutputStream().print(javascript);
ctx.forwardToResponse();
}
} catch (Exception e) {
handleException(e, ctx.request());
}
}
On my opinion it is extremely bad practice. Mixing the business logic and representation layers will not only make it harder to understand (what if the back-end dev does not understand JS at all?), but event more importantly it will make it harder to debug, especially in big projects where the people are constantly changing and this knowledge is lost in process.
I would implement an AJAX call and execute that JS upon request completion (on the view side ofc).
I'm trying to connect to .NET 4.0 webservice I created for receiving SOAP-calls from Android-devices, now hosted on local IIS for testing purposes.
I found out that ksoap2 would be an excellent class library for doing what i want to do. Downloaded the .jar package from https://code.google.com/p/ksoap2-android/ and started pounding the keyboard in ecstacy... with my fingers.
The amount of information being sent is from few kilobytes to few megabytes.
What is working
HttpTransportSE.call(String, SoapSerializationEnvelope)-method works perfectly while still in Eclipse's Android emulator, sending the call to webservice hosted in local IIS. Even tested that the webservice receives empty calls from trying to open the service address from a web browser in the same local area network.
What doesn't work
When I copy the .apk-file to an Android device, install it, start it and trying to make the call, the whole program freezes without making the call.
As you can see from a code block presented some lines after that possible errors are being taken into account: In emulated environment a successful call returns a SoapPrimitive-object or flows into the correct catch block generating an error message for the user according to the current situation.
Then on live Android device, program loses it's responsivity forever and has to be terminated from application menu.
What have i tried
I removed the call from the asynchronous method, and tried calling it straight from an anonymous inner function assigned for a button click-event.
Tried not trying to get a response, just making the call.
Tried getting a logcat-program for the device to see what's happening behind the UI, found two, they needed root access, which i don't have in the device. This is why i don't have any logcats to show you, and showing the emulator logcat would probably(?) be useless because it works fine there.
Not trying to connect to localhost.
Tried installing the program on older Lenovo-tablet running Android 4.2.2 and on brand new Samsung Galaxy Tab, both would have the same problem while otherwise working well.
The code
Here's the asynchronous method for making the call in device/emulator, where variables str_URL and soapRequest are a correct service address (checked) and a well formed SoapObject respectively:
#Override
protected WebServiceResult doInBackground(Void... v) {
WebServiceResult _ret;
SoapSerializationEnvelope soapEnvelope= new SoapSerializationEnvelope(SoapEnvelope.VER11);
soapEnvelope.dotNet=true;
soapEnvelope.setAddAdornments(false);
soapEnvelope.setOutputSoapObject(soapRequest);
HttpTransportSE conn = new HttpTransportSE(str_URL);
conn.setXmlVersionTag("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
conn.debug = true;
try {
conn.call(str_ACTION, soapEnvelope);
SoapObject o = (SoapObject)soapEnvelope.getResponse();
_ret = new WebServiceResult(o, WebServiceResultEnum.ok);
} catch (NetworkOnMainThreadException e) {
_ret = new WebServiceResult(null, WebServiceResultEnum.keskeytys);
} catch (HttpResponseException e) {
_ret = new WebServiceResult(null, WebServiceResultEnum.httpVirhe);
} catch (XmlPullParserException e) {
_ret = new WebServiceResult(null, WebServiceResultEnum.vaara_muoto);
} catch (SocketTimeoutException e) {
_ret = new WebServiceResult(null, WebServiceResultEnum.aikakatkaisu);
} catch (Exception e) {
_ret = new WebServiceResult(null, WebServiceResultEnum.keskeytys);
}
return _ret;
}
Thank you in advance!
Is it possible you are doing something like this:
YourAsyncTask task = new YourAsyncTask();
WebServiceResult result = task.doInBackground();
Because that would be wrong, completely wrong. If you call doInBackground() directly it will run in the same Thread and not in a new one. You need to start the AsyncTask with execute() like this:
YourAsyncTask task = new YourAsyncTask();
task.execute();
You need to implement the AsyncTask like this:
public class ExampleTask extends AsyncTask<Void, Void, WebServiceResult> {
public interface FinishedListener {
public void onFinished(WebServiceResult result);
}
private final FinishedListener finishedListener;
public ExampleTask(FinishedListener listener) {
this.finishedListener = listener;
}
#Override
protected WebServiceResult doInBackground(Void... params) {
WebServiceResult result = ...;
return result;
}
#Override
protected void onPostExecute(WebServiceResult result) {
super.onPostExecute(result);
if(this.finishedListener != null) {
this.finishedListener.onFinished(result);
}
}
}
And if you implemented it that way you can use it like this:
ExampleTask task = new ExampleTask(new ExampleTask.FinishedListener() {
#Override
public void onFinished(WebServiceResult result) {
// This will be called if the task has finished
}
});
task.execute();
It seems that I had declared the minimum SDK as 14 and target SDK as 17 in AndroidManifest.xml. I didn't use any fancy things in newer sdk's so i lowered the target SDK to the same level as minimum SDK, 14. I also had an Avast! Antivirus service running on the tablet which i removed.
This solved my problem. It could be that probably the Avast! antivirus-program wanted to block all communications from applications not downloaded from Play-store. I don't know if changing the target SDK had much effect really.
Well, I had the same question as you. When it goes to the method transport.call, it pauses, and for a while, it throws a timeout problem. At first, I thought maybe the network was poor, but the server logcat shows it is not the problem. The request was fine and the response was good. My business process is like below:
First, I get a list from the server through ksoap inner a child thread, then cycle the list, send a ksoap request based on every item of the list. It means it will send another list.size() request. When debugging in a real device the above problems occured. I solved it by starting a new child thread after getting the list and making all the list.size requests in the new child thread. So, ksoap use in android may cause thread block which leads to ioexception. So when you put it in a new thread, it escapes from the parent catch exception and works fine.
I have a complex application containing a server and a frontend part.
I am trying in the frontend part to update a progress monitor according to an action that takes place in the server part. The action from the server part is called remotely from the frontend. But I am having trouble getting the notifications real time to update the monitor.
My code structure looks somewhat like this:
class frontend_class1{
public void method {
List<String> strings = initializeStrings();
progressMonitor.begingTask("", noOfSteps);
reponse = frontend_class2.method2(strings);
progressMonitor.worked(1);
}
class frontend_class2{
public responseType method2(List<String> strings){
ServerClassRemote remote = new ServerClassRemote();
response = remote.serverMethod(strings);
return response;
}
class server_class{
public serverMethod(List<String> strings){
otherMethod(strings);
}
public otherMethod(List<String> strings){
someOtherMethod(strings);
}
public someOtherMethod(List<String> strings){
for (String s:Strings){
doSomethingWithString(s);
setStatus(true);
}
}
public setStatus(boolean status){
myVar = status;
}
public boolean getStatus(){
return status;
}
My intention is to send a notification to the frontend from the server side, so that the progress monitor is updated with one each time a string is done with.
That is why I included the status methods: to ask from the frontend what the status is, in a separate thread running, theoretically, simultaneously with the other method (serverMethod) and then resetting the status. Something like this:
new Thread(new Runnable() {
public void run() {
if (remote.getChanged()) {
remote.setChanged(false);
}
}
}).start();
But they don't run concurrently. How could I get the status each time a string from the list is finished so that I can update my progress monitor?
Instead of just setting the status is someOtherMethod you could send the update report with a new thread to the front end.
If you do not have a way of sending this update yet you will probably need to have the fornt_end run some kind of server to receive at least these messages. Maybe some RMI, should be easy enough to implement.