java wait observer to complete - java

i am quite new to java async function and observable all these. Is there anyway to make sure the async function return before proceeding?
I am currently using Thread.sleep(100); so that i can get the data i want, but i do not think it is appropriate .
my code:
private void getGroupAllInfo(CallbackContext callbackContext){
Log.d("executing: ", "getGroupAllInfo1");
Observable observable = GsscFactory.executeGetZwaveAllInfo(this.sock, MationPlugin.gatewayId, MationPlugin.account, MationPlugin.password);
observable.subscribe(t -> {
System.out.print(t);
MationPlugin.allInfo.put("data",t.toString());
// callbackContext.success(t.toString());
});
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
this.getGroupAllInfo(callbackContext);
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
callbackContext.success(this.allInfo.getString("data"));
}
I realized that i cannot assign variable inside the observable.subscribe(t -> {}), so i decide to put the data i want into a json object. If there is some alternatives, please tell me. ><

First create a MutableLiveData variable like:
MutableLiveData<String> response = new MutableLiveData<String>();
Then create an observer that observes the response value changes like:
// Create the observer which updates the response when a new value arrives.
final Observer<String> nameObserver = new Observer<String>() {
#Override
public void onChanged(#Nullable final String response) {
// Update the UI like a TextView text or do whatever you want with your [response] value
responseTextView.setText(response);
}
};
// Observe the MutableLiveData, passing the observer.
response.observe(this, nameObserver);
Then post your new value to the response variable in your background task like:
response.postValue("response value");

Related

The right way to deal with a few network requests

I'm preety new in java/android.
I'm writing android app, which takes data from online api.
The problem is, I'm not sure if my concept is correct.
So my app send first request, but I need some of respond data to start next request.
Here is some example more or less how it looks right now:
public class MainActivity extends AppCompatActivity{
private int key = 0;
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new DownloadTask().execute("url of 1st request");
}
private class DownloadTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
try {
return downloadContent(params[0]);
} catch (IOException e) {
return "Unable to retrieve data. URL may be invalid.";
}
}
#Override
protected void onPostExecute(String result) {
if(key == 0){
// I know it's 1st request because my key == 0
JSONObject json = new JSONObject(result);
String id = json.getString("id");
key++;
new DownloadTask().execute( "url of 2nd request/" + id );
}else{
// I know it's 2st request because my key != 0
//here I'm getting data i need
// and I'm going to rest of my app
end(result);
}
}
}
private void end(String result){
//rest of my app
}
}
Code is working fine, but I wanted to know if it's proper way to do it.
Maybe you know another way to do that, I'm not asking for completly new code, but maybe some topic I should find and read.
If you want to do it asynchronously, i will recommend you using CompletableFuture's. It is very simple and look like that:
CompletableFuture
.supplyAsync(() -> yourFirstrequest())
.thenApplyAsync(yourInstance::getResponse)
.thenAcceptAsync(nextRequestClass::sendNextrequest);
or you can separate them by Future's like:
CompletableFuture firstRequest = CompletableFuture.supplyAsync(() -> yourFirstrequest());
CompletableFuture getResponse = firstRequest.thenApply(response -> someActionWithResponse(response));
it is very powerful and convenient framework.
Take a look at the java docs or android specific docs

java can't find local variable in reterofit 2 api call

I have done an API call to retrieve a list of messages, then i want to check if each message has a flag of 2 and then if it does do another API call to receive information on whether the message has been "paid" and if it has alter the object message to paid = true;
Here is my failed attempt.
for (int i = 0; i < chatHistory.getData().size(); i++) {
final ChatMessage chatMessage = chatHistory.getData().get(i).getBody();
if (chatMessage.flag.equals("2")) {
RestClient.getInstance().getApiService().getPaymentRequest(chatMessage.payment_request_id, new Callback<SinglePaymentRequest>() {
#Override
public void success(SinglePaymentRequest singlePaymentRequest, Response response) {
Payment payment = singlePaymentRequest.getPayment();
if(payment.getStatus().equals("paid")) {
chatMessage.isPaid=true;
}
}
#Override
public void failure(RetrofitError error) {
System.out.println("fail");
}
});
}
chatMessages.add(chatMessage);
Log.e("chat history", chatMessage.from);
}
addData(chatMessages);
The problem I am facing is that the api call cannot find local variable chatmessage, any ideas as to why this is?
Thanks
Notice the bit of code new Callback<SinglePaymentRequest>() that creates your new Callback object? It does not have access to the variables outside it, for good reason too.
What you should be doing, is calling a setter method that's part of your container class (the one that is the parent of the Callback) that will, in turn manipulate the values that you want to change.

Android Volley Request - within request populating custom class not working

I've got an Activity consisting of a buch of TextViews (fourteen) and two Buttons.
I have created a custom class named Lesson, wich basically has a constructor and getter methods for its variables.
Now, inside my onCreate() in my Activity I am calling two functions: 1.) populateLessonDetails(myURL) and 2.) populateLessonTextViews().
I have created a private Lesson mLesson; variable inside my Activity, above all the #Overrides, because I'm trying to use this variable to populate it later on.
So, populateLessonDetails(myURL) is basically making a JsonArrayRequest, getting all the data from the JSON inside the onResponse(), saving it to String variables still inside the onResponse() and then, still inside the onResponse() I am trying to populate the mLesson variable, by calling
mLesson = new Lesson(mName, mRoom, mExtra, mAddress, mPC, mCity, mStart, mDate, mRID, mMaxAtt, mCurrentAtt); - the variables used within the constructor are the String variables containing the JSON data.
I Log.i() the JSON data as well as the mLesson variables via its getter methods, and the data is there. Everything is fine.
Now, my populateLessonDetails() ends.
It returns to the onCreate() and continues with the next line of code, wich would be calling populateLessonTextViews().
This is where things went south...
As soon as the function is called I try to get the information stored inside mLesson via its getter methods to set it to the TextViews like so:
//Lesson Name Big
TextView lessonNameTextBig = (TextView) findViewById(R.id.text_activelesson_name_big);
lessonNameTextBig.setText(mLesson.getLessonName());
This is the proper way to do it, I've done it a bunch of times already, but my App crashes at the second line.
I have debugged it and I have noticed that mLesson is empty. My guess would be that me populating it inside the onResponse() of the JsonArrayRequest, which is inside the populateLessonDetails() is only valid for this particular function, the scope of the variable mLesson ends when the function returns to the onCreate() and the mLesson variable is empty again since it died with the function.
Now how can I fix this? Do I have to set mLesson as a parameter for the populateLessonDetails() and then also return it (currently the populate functions are void) ? Then save the return value into another variable of type Lesson and set this new variable as a parameter for the populateLessonTextViews() ?? I've tried a couple of those things but they didn't work, but maybe its just me not doing it right.
This is what my code looks like (the important part):
public class ActiveLesson extends AppCompatActivity {
// there are also some other variables up here
private Lesson mLesson;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_active_lesson);
requestQ = Volley.newRequestQueue(this);
Intent intent = getIntent();
Bundle extras = intent.getExtras();
mDatum = extras.getString("datum");
mRID = extras.getString("rid");
mVon = extras.getString("von");
myActiveLessonURLFiltered += "datum="+mDatum+"&rid="+mRID+"&von="+mVon;
populateLessonDetails(myActiveLessonURLFiltered);
populateLessonTextViews();
}
private void populateLessonDetails(String myActiveLessonURLFiltered) {
JsonArrayRequest lessonJAR = new JsonArrayRequest(myActiveLessonURLFiltered,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response){
try{
for (int i=0; i < response.length(); i++)
{
JSONObject jsonObject = response.getJSONObject(i);
String mName = jsonObject.getString("Name");
String mRoom = jsonObject.getString("Raum");
String mExtra = jsonObject.getString("Zusatz");
String mAdresse = jsonObject.getString("Address");
String mPC = jsonObject.getString("PLZ");
String mCity = jsonObject.getString("City");
String mMaxAtt = jsonObject.getString("maxAnz");
String mCurrentAtt = jsonObject.getString("belegtAnz");
if(mExtra.length()==0 || mExtra == "null")
mExtra="";
if(mRoom.length()==0 || mRoom == "null")
mRoom="";
else
mRoom="Room: "+mRoom;
if(mName.length()==0 || mName == "null")
mName="";
mLesson = new Lesson(mName, mRoom, mExtra, mAdresse,
mPC, mCity, mVon, mDatum, mRID, mMaxAtt, mCurrentAtt);
Log.i("mmLesson"," Lesson with new = "+ mLesson.getLessonName()
+" "+mLesson.getLessonCity());
}
}catch (JSONException e){
e.printStackTrace();
}
}
},
new Response.ErrorListener(){
#Override
public void onErrorResponse(VolleyError error){
error.printStackTrace();
Toast.makeText(ActiveLesson.this, "No Lessons Available",
Toast.LENGTH_LONG).show();
}
}){
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("Accept", "application/json");
return headers;
}
};
requestQ.add(lessonJAR);
}
private void populateLessonTextViews(Lesson mLesson) {
//Lesson Name Big
TextView lessonNameTextBig = (TextView) findViewById(R.id.text_activelesson_name_big);
lessonNameTextBig.setText(mLesson.getLessonName());
// there are others lines of code like these two,
// but I've left them out, since they are all the same
}
If some could help me out I would appreciate it. Thank you!
The onResponse() method is a callback that is called later when the network request returned a value. The server does not respond with any delay. This means the populateLessonDetails(..) method get called from onCreate triggers an network request and return immedietly to the onCreate() call of this function and steps forward.
You have to take this in consideration. The best way to do this, call inside the onResponse the populateLessonTextViews() method. Then you can be sure that the content has been loaded.

How to resolve a promise inside another promise?

I have an action which requires to get a list of emails from a remote server. Then I want to use the emails to get a list of emailDomainInformation from another remote server (note that this second piece of info depends on the first). After all this, I want to output data from both servers onto a map and render it onto the page with dust.
I managed to get this to work without the second piece of data by doing it like this:
public static Result index()
{
F.Promise<Email> emailPromise = getEmailPromise(...);
F.Promise<Result> results = emailPromise.map( new F.Function<Email, Result>()
{
public Result apply(Email email)
{
Map<String, Object> data = new HashMap<String, Object>();
data.put("email", email.getAddress());
data.put("domain", email.getDomain());
dustRenderer.render(data);
}
}
async(results);
}
Now, since I want to make an async call to getEmailDomainData(email.getDomain()); inside the emailPromise.map() method. What do I do with the Promise<EmailDomain> object I get back? How do I put that into the data map to pass to the dustRenderer?
Here is an example that essentially does what you need:
public static Result social() {
final F.Promise<WS.Response> twitterPromise = WS.url("http://search.twitter.com/search.json").setQueryParameter("q", "playframework").get();
final F.Promise<WS.Response> githubPromise = WS.url("https://api.github.com/legacy/repos/search/playframework").get();
return async(
twitterPromise.flatMap(
new F.Function<WS.Response, F.Promise<Result>>() {
public F.Promise<Result> apply(final WS.Response twitterResponse) {
return githubPromise.map(
new F.Function<WS.Response, Result>() {
public Result apply(final WS.Response githubResponse) {
return ok(views.html.social.render(twitterResponse.asJson().findValuesAsText("text"), githubResponse.asJson().findValuesAsText("name")));
}
}
);
}
}
)
);
}
In this case the two run in parallel but you could move the second Promise creation into the handler for the first Promise.

Binding data to the UI in GWT

In Silverlight, a frequently used pattern is:
Request data
Get back an empty container for the data
Asynchronously fire off a query to fill the container
When the query returns, fire an event on the container
Update the UI according to the container's contents
Can this be done in GWT?
The reason I ask is that I'm trying to make a SuggestBox that contains a list of group names and icons. First, I query Facebook to get a list of groups IDs that are close to the current String in the SuggestBox. Then, I fire off queries to get icons for each group id. The problem is that I have to return the suggestions before those queries are done. I'm not sure how to go back and insert the data after I have it. I don't want to block until the calls are complete, and there's no real way to know in advance what data to load.
I could return a widget for the suggestion that loads an image, but the suggestion must be a plain String.
What is the right approach here?
Let's assume you're using GWT RPC. You'll have some service interface that lets you fetch the groupIds for a suggestion and the icon for a specific group id.
public interface FacebookService extends RemoteService {
List<String> getFacebookGroupIds(String suggestion);
Icon getIconForGroup(String groupId);
}
You should build your own implementation of Suggestion that can display itself with either just a groupId or a groupId and an Icon.
public class FacebookGroupSuggestion implements Suggestion {
private String groupId;
private Icon icon;
public FacebookGroupSuggestion(String groupId) {
this.groupId = groupId;
}
public String getDisplayString() {
StringBuilder builder = new StringBuilder();
builder.append("<b>");
builder.append(this.groupId);
builder.append("</b>");
if (this.icon != null) {
builder.append(this.icon.toSafeHtml());
}
return builder.toString();
}
}
I'm using Icon as your own implementation of an icon, it's not a standard class.
Then, you can make your implementation of SuggestOracle to fetch the groupIds and icons asynchronously. The SuggestOracle uses a callback to inform the suggestBox that some response to a request is available. So fetch your results, and call the callback when you get them. It'll look something like this.
public class FacebookSuggestOracle extends SuggestOracle {
private FacebookServiceAsync service = GWT.create(FacebookService.class);
private Request currentRequest;
private Callback currentCallback;
#Override
public void requestSuggestions(Request request, Callback callback) {
// Save request & callback for future use.
this.currentRequest = request;
this.currentCallback = callback;
// Fetch the groupIds
service.getFacebookGroupIds(request.getQuery(), new AsyncCallback<List<String>>() {
public void onSuccess(List<String> result) {
createSuggestionsForGroupIds(result);
}
});
}
private void createSuggestionsForGroupIds(List<String> groupIds) {
List<FacebookGroupSuggestion> suggestions = new ArrayList<FacebookGroupSuggestion>();
for (String groupId : groupIds) {
suggestions.add(new FacebookGroupSuggestion(groupId));
}
Response response = new Response(suggestions);
// Tell the suggestBox to display some new suggestions
currentCallback.onSuggestionsReady(currentRequest, response);
// Fetch the icons
for (String groupId : groupIds) {
service.getIconForGroup(groupId, new AsyncCallback<Icon>() {
public void onSuccess(Icon result) {
// match the icon to the groupId in the suggestion list
// use the callback again to tell the display to update itself
}
});
}
}
}

Categories