lately I'm using Volley library in android.. It works fine but I was wondering about the most efficient way of updating the UI.. I have a Utils class that has all the Volley methods.. right now I pass all the Views that will be updated as parameters, but I've read that I could implement the listeners in my activity then pass them as parameters in the Utils class..
So my question is:
Which is more efficient and why updating the UI like this:
public void getSettings(final TextView exampleView) {
JsonObjectRequest jsonRequest = new JsonObjectRequest(Method.GET,
url, (String) null, new Response.Listener<JSONObject>() {
public void onResponse(JSONObject response) {
try {
final String setting = getSettingFromJSON(response);
exampleView.setText(setting);
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
public void onErrorResponse(VolleyError arg0) {
}
});
app.add(jsonRequest);
}
Or I declare the listeners in my activity like this :
Response.Listener<JSONObject> listener = new Response.Listener<JSONObject>() {
public void onResponse(JSONObject response) {
String setting = Utils.getSettingFromJSON(response);
exampleView.setText(setting);
}
};
then I pass them into my utils function as parameters so the call would be like this :
utils.getSettings(listener);
instead of :
utils.getSettings(exampleView);
Thanks In Advance :)
Why not use something like EventBus to manage passing back info from the request to your activity? This way you can separate your networking code from your activities/views. The basic idea using EventBus and Volley is the following:
Register a class to receive some type of event, in this case probably
the activity
Create a method in this class to handle this event,
exampleView.setText(setting).
Post the event to the bus, post the String or volley error in the
Volley listeners.
For a more detailed example checkout this blogpost or Eventbus' github page.
As in my question Android/Java: how to delay return in a method
I also use interface listener, however, I update views in main thread, not in Utils class
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
}
});
I am trying to build a Rest client with Retrofit 2. This is the first time using this library.
Most of the examples that I have seen normally use a callback function in Android activity or view.
http://www.vogella.com/tutorials/Retrofit/article.html
https://github.com/MeetMe/TwitchTvClient/tree/master/src/com/wdonahue/twitchtvclient
I was wondering if, in the code below, one could return an object of type Appver instead of void?
Thank you!
public void GetAppver()
{
Call<Appver> call = endPoint.GetAppver();
call.enqueue(new Callback<Appver>()
{
#Override
public void onResponse(Call<Appver> call, Response<Appver> response)
{
if (response.isSuccessful())
{
Appver appver = response.body();
}
}
#Override
public void onFailure(Call<Appver> call, Throwable t) { }
});
}
I guess you want to have Appver because you need to use this object to possibly poulate the UI for instance inside a RecyclerView, in this case I guess you cannot avoid to not use void, because you are extending the library. But for sure you can use Dagger2 or a Singleton although there are some contraindications to maintain an instance of Appver so that you can use it in an Adapter for instance
Also in real life is rarely used Retrofit2 in this way, usually you implement it with RXJava2 or with Google Architecture components as ViewModel,LiveData and LifeCycle
You can call Retrofit service methods synchronously.
public AppVer GetAppVer() {
Call<AppVer> call = endPoint.GetAppver();
Response<AppVer> response;
try {
response = call.execute();
} catch (IOException e) {
// Handle network communication errors here
}
if (!response.isSuccessful()) {
// Handle REST service errors here
}
return response.body();
}
However, as you know, you can't do network communication on the main thread in Android. You have to wrap the call in e.g. a Thread, AsyncTask or Service.
Have a look at the following links how to structure your app.
Android Architecture Blueprints: https://github.com/googlesamples/android-architecture
Android Architecture Components: https://developer.android.com/topic/libraries/architecture
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 a junior android developer and I almost finished the alpha version of my first big project. I think that I have good knowledge of java but I am not sure if I organized my app right.
Short description: I use in my app the volley library to send and receive data from server. Because of that I created a class to manage server methods. In that class I created a lot of static methods for every connection to server I need(like this example):
public static void sendDataToServer(final Context context, final String data) {
StringRequest mStringRequest = new StringRequest(Request.Method.POST, URL_VERIFY, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
// get response
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// get error response
}
}) {
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
// the POST parameters:
params.put(API_KEY, API_KEY_VALUE);
params.put(API_KEY_DATA, data);
return params;
}
};
Volley.newRequestQueue(context).add(mStringRequest);
}
So in my activities I call this like MyServerClass.sendDataToServer(...)
My question is: Is it ok to call my server methods like that? Or should I make them instance methods and instantiate MyServerClass when activity is started? I must mention that I have about 5 methods in that class.
I have another class like that with methods to check data accuracy. Should I also make them instance methods and instantiate it in the activities I need?
Any reference or advice is welcome. Thanks in advance!
No, in your case, both ways will have the same result...
The only thing to mention is, that if you need to receive the response to your request too (may be in the future), you will need to add a Delegate / Callback / Interface to your class, to get the result right back to your calling activity instance... In that case it would be better to create a "non-static instance method" way... But you can add a non-static Method to your Class too so I don't see anything against it.
UPDATE TO COMMENT
Well for example, if you want to provide a ListView with Images... In most cases you first request an JSONArray with your ListView entries, which contains the links to Bitmaps located on the remote Server...
If you download Images Async and put them into the ImageViews in the rows of a ListView (while the user scrolls), it could be possible that images are loaded longer and the ListView will show images in wrong places... For something like that you will need a Singleton Pattern, which will manage the downloads for you... This will not be possible with your class/static Method
Although this question has already had an accepted answer, however, I'd like to share my code that looks like your issue. Hope this helps!
I also use Interface like #Neo answer, as the following:
public interface VolleyResponseListener {
void onError(String message);
void onResponse(Object response);
}
Then in my VolleyUtils class:
public static void makeJsonObjectRequest(Context context, String url, final VolleyResponseListener listener) {
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest
(url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
listener.onResponse(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
listener.onError(error.toString());
}
}) {
#Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
};
// Access the RequestQueue through singleton class.
VolleySingleton.getInstance(context).addToRequestQueue(jsonObjectRequest);
}
Then in Activity:
VolleyUtils.makeJsonObjectRequest(mContext, url, new VolleyResponseListener() {
#Override
public void onError(String message) {
}
#Override
public void onResponse(Object response) {
}
});
P/S: my project uses Google's official Volley library, instead of using compile 'com.mcxiaoke.volley:library:1.0.17' in build.gradle. As a result, JsonObjectRequest(...) will have a difference at its definition.
I'm using a bit of code to help me with pulling data from the web called WebRequest (https://github.com/delight-im/Android-WebRequest). It provides a callback for retrieving asynchronous data. However I can't update my ArrayAdapter because I get an error "Non-Static method 'notifyDataSetChanged()' cannot be referenced from a static context"
Now, I've seen a number of similar questions here. Some suggest putting the notifyDataSetChanged command in the Adapter class itself. Some suggest using a Handler and some suggest using a Loader. However I have had no luck actually implementing these solutions. Here's my code:
new WebRequest().get().to(stringSQLUrl).executeAsync(new WebRequest.Callback() {
public void onSuccess(int responseCode, String responseText) {
try {
DataHistory = CsvUtil.fromCsvTable(responseText);
DataHistory.remove(0); //Removes header row
} catch (Exception e) {
Log.e("Main pullWebData","Error converting from CsvTable: " + e.getMessage());
}
DataAdapter.notifyDataSetChanged(); // <-- ERROR HERE
runOnUiThread(new Runnable() {
#Override
public void run() {
DataAdapter.notifyDataSetChanged(); // <-- ALSO ERROR HERE
}
});
}
public void onError() {
Log.e("Main pullWebData","Error pulling data from web");
}
});
I also defined this Handler in my activity thinking I could call it and it would update the ArrayAdapter, but I got the same error here:
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
WeightAdapter.notifyDataSetChanged();
}
};
Lastly I created a method inside the Adapter definition to notify itself, but calling that gave me the same static/non-staic error:
public void updateMe() {
this.notifyDataSetChanged();
}
Long story short - there are a ton of questions seemingly about this same topic and lots of advice, but I have been unsuccessful in implementation. Can anyone show me exactly how I'd implement this?
Thank you!
One other thing: I was considering switching from Web data to an Azure SQL DB, but that would also use a callback and I presume have the same issue?
You can only call static methods using ClassName.methodName(); However, notifyDataSetChanged() is not a static method. i.e. notifyDataSetChanged() works depending on the instance of your adapter.
To make sure that this works, you should use notifyDataSetChanged() on the object of the custom adapter.
If you have something like :
DataAdapter customAdapter = new DataAdapter(//params);
listView.setAdapter(customAdapter);
You should call :
customAdapter.notifyDataSetChanged();