I'm rewriting some of my code after years of stepping away from it, and wanted to ask the community for a push in the right direction from a more modern architecture perspective (if one is needed).
--
One of the things my app does is send JSON data (maybe about 15-20 key-value pairs in total) upon clicking a button in the app.
I wrote the code to use Volley inside an AsyncTask to interact with my API.
I created a progress bar that would basically block the UI with a loading spinner until the data sent out and then got a confirm back from the API.
When that is done, it would dismiss and the user could carry on, but in a few rare cases some of my users complain the data is slow and the progress bar takes a while to go away (a full 5-10 minutes, which is interesting) or just never goes away at all.
I believe I erred here in creating the spinner, so I will do away with that and have it run in the background.
I found myself more confused than ever at the seemingly various approaches and frameworks out there for interacting with APIs that weren't as available years ago, so wanted to ask here in hopes of getting a more unified / community-based response.
My Question
Is it still a modern/ideal/commonly found approach to
Create a class that extends AsyncTask<type here>
Add your your onPreExecute, doInBackground(String... params), onPostExecute, declarations and code
Use code like this to interact with your API using Volley:
My old code is below, and unfortunately I've wrote many tasks like this (practically every case of where I need to make an API call has this large bloated file that follows the format below).
#Override
protected String doInBackground(String... params)
{
RequestQueue mmRequestQueue = Volley.newRequestQueue(context);
StringRequest mmyReq = new StringRequest(Method.POST, "ENDPOINT HERE", new Response.Listener<String>()
{
#Override
public void onResponse(String rawResponse) {
JSONObject response = null;
try {
response = new JSONObject(rawResponse);
//do stuff with response here..
}
catch(Exception ex)
{
//logging code here
}
}
},
new Response.ErrorListener()
{
#Override
public void onErrorResponse(VolleyError error) {
//handle my stuff here
}
}
)
{
#Override
protected Map<String, String> getParams()
{
Map<String, String> params = new HashMap<String, String>();
//put my params here, for example:
params.put("helloworld", val1);
params.put("helloworld2", val2);
return params;
}
};
mmRequestQueue.add(mmyReq);
try {
Thread.sleep(1000); //not sure why this is even here anymore, I can't recall if this was intentional or not, maybe it was to show the progress bar which I won't be using anymore anyway?
} catch (InterruptedException e) {
}
return "";
}
Related
I want to send a String message to database when user presses a specific button in the LibGDX game I am designing for android. How do I go about doing that? Following is the code I tried. But it does not work.
Net.HttpRequest httpRequest = new Net.HttpRequest();
httpRequest.setMethod("POST");
httpRequest.setUrl("URL is here");
httpRequest.setContent("INSERT INTO `game_table` (`Button`) VALUES ('Button 1 Pressed')");
Net.HttpResponseListener httpResponseListener = new Net.HttpResponseListener() {
#Override
public void handleHttpResponse(Net.HttpResponse httpResponse) {
Gdx.app.log("Log httpResponse", httpResponse.getResultAsString());
}
#Override
public void failed(Throwable t) {
}
#Override
public void cancelled() {
}
};
Gdx.net.sendHttpRequest(httpRequest,httpResponseListener);
Log does not provide anything in android monitor. I also tried using AsyncTask and without AsyncTask to implement this code. But neither works.
Am I missing something? If so could you give me small code snippet that will work?
You don't need to use an AsyncTask, libGDX' HTTPRequest is async out of the box.
You did not log anything if the request fails or is cancelled so probably that's the case.
I'm developing an Android Mobile Application and one of the most important functionality of the app itself is being able to talk with a third-party API Service.
The third party service, offering these API, wants a "beacon" to be included into every API request i made.
The "beacon" is a "long integer" and it must be unique and incremental for every request.
The problem is:
I'm firing a couple of these request and i do not know which of these requests will complete first so i'm running into a race condition: where the second request ends quickly before the first request invalidating the first request!
When a button is clicked the following action will be executed:
public void fireRequests(View view)
{
long first_beacon = System.nanoTime();
fireFirstRequest(view, first_beacon);
long second_beacon = System.nanoTime();
fireSecondRequest(view, second_beacon);
}
I'm using Volley in a proper way, setting up callback etc.. example here:
fireFirstRequest method:
public void fireFirstRequest(View view, long beacon)
{
final ThirdPartyLib api_lib = new ThirdPartyLib(getActivity());
api_lib.doOperationA(beacon, new ThirdPartyLib.MyOwnCallback()
{
#Override
public void update(JSONObject jsonObject)
{
try
{
JSONObject result = jsonObject.getJSONObject("response");
/* my code */
Log.d("doOperationA", result)
}
catch (JSONException e)
{
e.printStackTrace();
}
}
});
}
fireSecondRequest method:
public void fireSecondRequest(View view, long beacon)
{
final ThirdPartyLib api_lib = new ThirdPartyLib(getActivity());
api_lib.doOperationB(beacon, new ThirdPartyLib.MyOwnCallback()
{
#Override
public void update(JSONObject jsonObject)
{
try
{
JSONObject result = jsonObject.getJSONObject("response");
/* my code */
Log.d("doOperationB", result)
}
catch (JSONException e)
{
e.printStackTrace();
}
}
});
}
Here is the execution log:
03-12 14:26:56.252 18769-18769/it.example.app D/Volley: queued doOperationA
03-12 14:26:58.124 18769-18769/it.example.app D/Volley: queued doOperationB
03-12 14:26:59.433 18769-18769/it.example.app D/App: doOperationB: {
"error": false,
"payload": {
"foo": "bar"
}
}
03-12 14:27:04.181 18769-18769/it.example.app D/App: doOperationA: {
"error": true,
"errorMessage": "invalid beacon"
"payload": {}
}
The question is: what's the best way to keep track of beacon before firing an API request or to maintain a "execution order" separation even if we are talking of ASync request?
My rough solution is to call the fireSecondRequest() inside the callback of the fireFirstRequest() when i'm completely sure that first request is done.
I know, this is the best way to kill the awesome world of async requests.
modified action:
public void fireRequests(View view)
{
long first_beacon = System.nanoTime();
fireFirstRequest(view, first_beacon);
}
fireFirstRequest modified method with final View parameter:
public void fireFirstRequest(final View view, long beacon)
{
final ThirdPartyLib api_lib = new ThirdPartyLib(getActivity());
api_lib.doOperationA(beacon, new ThirdPartyLib.MyOwnCallback()
{
#Override
public void update(JSONObject jsonObject)
{
try
{
JSONObject result = jsonObject.getJSONObject("response");
/* my code */
Log.d("doOperationA", result)
/* fire second request */
// EDIT
fireSecondRequest(view, System.nanoTime());
}
catch (JSONException e)
{
e.printStackTrace();
}
}
});
}
You didn't add the part of your code which initiates the Volley RequestQueue, but I'm assuming you're creating the default way using:
RequestQueue requestQueue = Volley.newRequestQueue(context, stack);
When you do this, you get a request queue which allows for 4 concurrent requests by default. You can see this by looking at the constructor this method uses to create a request queue:
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
...
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
You can overcome this issue if instead of using the default method for creating a RequestQueue, you create your own RequestQueue with a thread pool size of 1. This way, there can be no 2 concurrent requests, and requests will be sent in the order they are dispatched.
The downside with this, of course, is that this can dramatically slow down your app. If all requests must wait until the previous request is finished, this creates a serious bottleneck in your app.
Perhaps consider using more than 1 request queue, and only use this special request queue for requests that rely on this special constraint.
Hope this helps.
I'm trying to use retrofit with rxjava. I have a problem chaining retrofit observables with one another or with observables created by me. Some example:
Observable<List<Friend>> friendsListObservable = friendsService.getFriends();
Observable<Void> updateReqestObservable = friendsListObservable.switchMap(friends -> {
Log.d(TAG, "Hello");
return userAPI.updateFriends(session.getUserId(), friends);
}).subscribe();
Everything gets called until it gets to switchMap. So hello is never displayed, but if I return for instance Observable.just(null) instead of the retrofit observable it works fine. Also if I user the retrofit observable without chaining, it works.
Edit1:
It's an android app. Seems like the map operator is not called at all. Sometimes it happens that with retrofit observables also. I still think that it has something to do with threading. From what I understand an operator is called when an item is emitted, but calling onNext doesn't trigger the map operator. Below is my whole code:
public Observable<List<FacebookFriend>> getFriends() {
PublishSubject<List<FacebookFriend>> friendsPublishSubject = PublishSubject.create();
Observable<List<FacebookFriend>> returnObservable = friendsPublishSubject.doOnSubscribe(() -> {
Log.d(TAG, "OnSubscribe called");
Session session = Session.getActiveSession();
if (session != null && session.isOpened()) {
new Request(session, "/me/friends", null, HttpMethod.GET,
new Request.Callback() {
public void onCompleted(Response response) {
JSONObject graphResponse = response.getGraphObject()
.getInnerJSONObject();
try {
JSONArray friends = graphResponse.getJSONArray("data");
Gson gson = new Gson();
Type listType = new TypeToken<ArrayList<FacebookFriend>>() {
}.getType();
List<FacebookFriend> friendsList = gson.fromJson(friends.toString(), listType);
friendsPublishSubject.onNext(friendsList);
friendsPublishSubject.onCompleted();
} catch (JSONException e) {
e.printStackTrace();
friendsPublishSubject.onError(e);
}
}
}).executeAsync();
} else {
InvalidSessionException exception = new InvalidSessionException("Your facebook session expired");
friendsPublishSubject.onError(exception);
}
});
return returnObservable.subscribeOn(AndroidSchedulers.mainThread()).observeOn(AndroidSchedulers.mainThread());
}
public Observable<Void> updateFriendsList() {
Observable<List<FacebookFriend>> facebookFriendsListObservable = facebookService.getFriends();
Observable<Void> updateReqestObservable = facebookFriendsListObservable.map(friends -> {
Log.d(TAG, "This is never called");
});
}
One way you could get around blocking at the actual calling point would be to subscribe it to a subject and then block on the subject at the end of whatever part of your code requires the requests to have been executed.
For example:
final ReplaySubject<Void> subject = ReplaySubject.create();
friendsService.getFriends()
.switchMap(friends -> userApi.updateFriends(session.getUserId(), friends))
.subscribeOn(Schedulers.io())
.subscribe(subject);
// Do other things that don't require this to be done yet...
// Wherever you need to wait for the request to happen:
subject.toBlocking().singleOrDefault(null);
Since a subject is also an Observable, you could even return the subject from a method, and block on it later.
All that being said, it does seem a bit odd to be using the update call as a side effect there. Is updateFriends also a Retrofit service? If so, you may want to consider making the update call a synchronous service that returns void instead of an Observable<Void> that you will call from within an onNext call. If it still needs to block, you can use forEach like this:
friendsService.getFriends()
.forEach(friends -> { userApi.updateFriends(session.getUserId(), friends) });
forEach is very similar to subscribe, except that it explicitly blocks. You could even use it with your original code, but added an empty onNext action wouldn't be terribly clean.
If you can also provide more details about your app structure (is this all in a main method? is it an Android app? etc.) I could give some more pointers on avoiding blocking as much as possible.
In my android app, after sometime (hour or so.. not something determined) the connection and response to Google-AppEngine takes very long, something like 10 seconds or more.
After the first connection all other enpoint requests are done pretty quickly and this is why I believe this is SW issue and not internet connection issue.
Should I establish a 'dummy' connection as the app is loaded ?
Here is a sample code of an AsyncTask which tried to get User entity from AppEngine endpoint :
private class getUser extends AsyncTask<Void, Void, Boolean> {
long mTaskUserId = Constants.USER_ID_NO_ID_INFDICATOR;
String mIdInPlatform = Constants.USER_ID_NO_ID_INFDICATOR.toString();
Long mServerScore;
Context mContext;
String mUserName;
getUser(String idInPlatform, String userName, Context c) {
mIdInPlatform = idInPlatform;
mUserName = userName;
mContext = c;
}
#Override
protected Boolean doInBackground(Void... params) {
Userendpoint.Builder builder = new Userendpoint.Builder(
AndroidHttp.newCompatibleTransport(), new JacksonFactory(), null);
builder = CloudEndpointUtils.updateBuilder(builder);
Userendpoint endpoint = builder.build();
try {
User user = endpoint.getUser().execute();
} catch (IOException e) {
Log.e(TAG, "Error getting user details from server ", e);
return false;
}
this.mUserName = user.getUserName();
this.mServerScore = user.getScore();
this.mTaskUserId = user.getId();
return true;
}
#Override
protected void onPostExecute(Boolean result) {
if (result) {
setUserFacebookIdInPreferences(mIdInPlatform, mContext);
setUserIdInPreferences(this.mTaskUserId, mContext);
setScoreInPreferences(this.mServerScore, mContext);
setUserNameInPreferences(this.mUserName, mContext);
} else {
Toast.makeText(mContext, R.string.string_login_failed, Toast.LENGTH_SHORT).show();
}
// Restart login activity.
moveToLoginActivity(result);
super.onPostExecute(result);
}
}
Your application in Google App Engine uses two types of server instances: Dynamic instances and Resident instances. The difference is that dynamic instances are created in demand to serve traffic requests. Resident instances are always on.
When traffic stops, all your dynamic instances will shut down to save resources (and help you save money). The first time a request hits the server, a new dynamic instance will spin off to serve that request. The process of starting a new instance might take some time.
This is very likely what you are seeing in your application. To avoid that initial latency you can do two different things:
1) Optimize the time it takes for your code to load up.
2) Set up a Resident instance.
You can find more information on the Google documentation here:
https://developers.google.com/appengine/docs/adminconsole/instances#Introduction_to_Instances
You can warm-up your instances so that they're live before any query hits them - saving you this 10s delay. See documentation at:
https://developers.google.com/appengine/docs/adminconsole/instances#Warmup_Requests
I've made an app that sends a request to a webserver in a specified interval and gets XML data. It then parses the XML data, gets information from the phone (text messages, contacts or something similar) and shoots it back to the server with a http post request.
The problem is that it usually takes a few seconds for the app to get the info, which often leaves the app crashing. A dialog comes up saying the app has become unresponsive and asks if i want to close the app or wait, if i press wait it eventually starts working again.
Is AsyncTask the right solution to this problem?
Another thing i don't really understand is how AsyncTask actually works. Let's say i have two methods that do a lot of work and crashes the app, can i put both of them in one AsyncTask and just call them from doInBackground()?
I have also implemented something similar you are trying. That is sending request to server, receive XML response, parse XML, show result. View This. I have used AsyncTask for this.
Here is how i have implemented it using AsynTask
private class AsyncClass extends AsyncTask<Void, Void, Bundle>{
#Override
protected Bundle doInBackground(Void... arg0) {
Bundle b=startProcess();
// startBundle() method do all the processing and return result in a bundle. You can as many methods from within startBundle() method
return b;
}
#Override
protected void onPostExecute(Bundle result) {
Log.d(TAG , "In onPostExecute");
dialog.dismiss();
if(result==null)
Toast.makeText(cont, "Can't process query.\nTry again later.", Toast.LENGTH_LONG).show();
else{
Intent in = new Intent(cont, QueryResultDisplay.class);
Log.d(TAG , "Displaying");
in.putExtras(result);
cont.startActivity(in);
}
}
I give you brief description about your problem.
There are many possibility that you don't get data from server
if your network speed is very slow and you try to get all the
information from server and XML data then in this case if network crash then it show you error
if you're making request to that page which is not in server
Now, if you are facing the problem in code, then I give you the complete code of AsyncTask class which I had implemented in my project and it work fine.
private class GetLoginResponse extends AsyncTask<Void, Void, Boolean> {
private ProgressDialog progressDialog;
private String email;
private String password;
public GetLoginResponse(String emailId, String paswd) {
this.email = emailId;
this.password = paswd;
}
#Override
protected void onPreExecute() {
progressDialog = ProgressDialog.show(LoginActivity.this, "",
"Loading....", true, false);
}
#Override
protected Boolean doInBackground(Void... params) {
try {
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
HttpResponse response = httpclient.execute(httpGet);
//here u can check the reponse is ok and 200
} catch (NetworkException e) {
isNetworkError = true;
}
return false;
}
#Override
protected void onPostExecute(Boolean data) {
progressDialog.dismiss();
System.out.println("lOGIN RESPONSE for email = " + email + data);
}
}// end AsyncTask
This would solve your problem.
Yes, you can use AsyncTask.
The code called in doInBackground() must not touch the UI.
You can touch the UI thread with publishProgress().