I implemented a paging library and it does not work quite as it should. I request data from the github and paginate the list of repositories. The code works well, but after several changes to the search query, it stops loading data. In the debug, the data always loads well. I guess the problem is asynchrony, but I can’t figure out where to look.
My code:
RepoDataSource
public class RepoDataSource extends PageKeyedDataSource<Integer, Repo> {
#Override
public void loadInitial(#NonNull LoadInitialParams<Integer> params, #NonNull LoadInitialCallback<Integer, Repo> callback) {
Timber.d("Initial RepoDataSource");
try {
Response<RepoSearchResponse> response = githubService.searchRepos(query, firstNumberPage).execute();
RepoSearchResponse repoSearchResponse = response.body();
if (repoSearchResponse != null) {
List<Repo> items = repoSearchResponse.getItems();
callback.onResult(items, 1, 2);
}
} catch (IOException e) {
Timber.i(e);
}
}
#Override
public void loadBefore(#NonNull LoadParams<Integer> params, #NonNull LoadCallback<Integer, Repo> callback) {
}
#Override
public void loadAfter(#NonNull LoadParams<Integer> params, #NonNull LoadCallback<Integer, Repo> callback) {
Timber.d("Fetching next page: %s", params.key);
try {
Response<RepoSearchResponse> response = githubService.searchRepos(query, params.key).execute();
if (response.isSuccessful()) {
RepoSearchResponse repoSearchResponse = response.body();
if (repoSearchResponse != null) {
List<Repo> items = repoSearchResponse.getItems();
callback.onResult(items, params.key + 1);
}
}
} catch (IOException e) {
Timber.i(e);
}
}
}
GithubApiCall
#GET("search/repositories")
Call<RepoSearchResponse> searchRepos(#Query("q") String query, #Query("page") Integer page);
RepoDataSourceFactory
public class RepoDataSourceFactory extends DataSource.Factory<Integer, Repo> {
private GithubService githubService;
private String query;
public RepoDataSourceFactory(GithubService githubService, String query) {
this.githubService = githubService;
this.query = query;
}
#NonNull
#Override
public DataSource<Integer, Repo> create() {
return new RepoDataSource(githubService, query);
}
}
Repository method
public class RepoRepository {
...
...
public RepoDataSourceFactory getRepoPagedFactory(String query) {
return new RepoDataSourceFactory(githubService, query);
}
}
ViewModel
public final class MyViewModel {
...
public MutableLiveData<String> searchQuery = new MutableLiveData<>();
...
public LiveData<PagedList<Repo>> getRepos() {
return Transformations.switchMap(searchQuery, query -> {
RepoDataSourceFactory factory = repository.getRepoPagedFactory(query);
return new LivePagedListBuilder<>(factory, pagedListConfig).build();
});
}
...
public SearchView.OnQueryTextListener listener = new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
if (query != null && !query.trim().equals("")) {
searchQuery.postValue(query);
}
return true;
}
#Override
public boolean onQueryTextChange(String newText) {
return true;
}
};
...
}
And in my activity
viewModel.getRepos().observe(this, adapter::submitList);
There is nothing wrong with your code. I was on a GitHub project, and was stuck in the same problem until I realize GitHub has a Rate Limit of 10 requests per minute for unauthenticated requests. But if it is an authenticated one, you can make up to 30 requests per minute.
I assume you also send request for every changes in the search query, just like I did, where typing/changing 5 characters equals 5 requests. So the real cause is the very limited request rate from GitHub, not your code.
Check this out: https://developer.github.com/v3/search/#rate-limit
Related
I am new in RXjava. I have impliment it in my project but it is not getting the data and didnot display it. what is the problem here?
My viewModel
public LiveData<Resource<List<Item>>> makeApiCallTopArticles() {
final MutableLiveData<Resource<List<Item>>> mediumObjectsList = new MutableLiveData<>();
mediumObjectsList.setValue(Resource.loading());
APIService apiService = RetroInstant.getRetroMediumClient().create(APIService.class);
Observable<CnnResponse> observable = apiService.getNewsObjectsList("http://rss.cnn.com/rss/cnn_topstories.rss",
"", "25");
Observer<CnnResponse> observer = new Observer<CnnResponse>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(CnnResponse value) {
List<Item> articles = new ArrayList<>();
assert value != null;
List<Item> responce = value.getItems();
for (int i = 0; i < Objects.requireNonNull(responce).size(); i ++) {
if (!Objects.equals(Objects.requireNonNull(responce.get(i).getEnclosure()).getLink(), null) && !Objects.equals(responce.get(i).getTitle(), "")) {
articles.add(responce.get(i));
}
}
mediumObjectsList.postValue(Resource.success(articles));
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
};
observable.subscribe(observer);
return mediumObjectsList;
}
ViewModel before I added RXjava
public LiveData<Resource<List<Item>>> makeApiCallTopArticles() {
final MutableLiveData<Resource<List<Item>>> mediumObjectsList = new MutableLiveData<>();
mediumObjectsList.setValue(Resource.loading());
APIService apiService = RetroInstant.getRetroMediumClient().create(APIService.class);
Call<CnnResponse> call = apiService.getNewsObjectsList("http://rss.cnn.com/rss/cnn_topstories.rss",
"", "25");
call.enqueue(new Callback<CnnResponse>() {
#Override
public void onResponse(#NotNull Call<CnnResponse> call, #NotNull Response<CnnResponse> response) {
List<Item> articles = new ArrayList<>();
assert response.body() != null;
List<Item> responce = response.body().getItems();
for (int i = 0; i < Objects.requireNonNull(responce).size(); i ++) {
if (!Objects.equals(Objects.requireNonNull(responce.get(i).getEnclosure()).getLink(), null) && !Objects.equals(responce.get(i).getTitle(), "")) {
articles.add(responce.get(i));
}
}
mediumObjectsList.postValue(Resource.success(articles));
}
#Override
public void onFailure(#NotNull Call<CnnResponse> call, #NotNull Throwable t) {
mediumObjectsList.setValue(Resource.error(t.getMessage() != null ? t.getMessage() : "Unknown Error"));
}
});
return mediumObjectsList;
}
.......................................................................................................................................................................................
Try to add logs to: onNext and onError method. Just to understand that you really receive a response or maybe you have some errors during the request. If you receive an error that can be a reason.
When you're using Rx you should use schedulers to avoid perform long term operation on the main thread. replace you subscription with:
observable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(observer);
Try this,
observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
I have a function searchForTrips() which sends an API request and fetch some response in following way.
private void searchForTrips(){
int departurePortId = PORT_ID_LIST.get(departurePort);
int returnPortId = PORT_ID_LIST.get(returnPort);
int pax= Integer.parseInt(noOfPassengers);
String departureDatePARSED = DEPARTURE_DATE_VALUES.get(departureDate);
String returnDatePARSED = RETURN_DATE_VALUES.get(departureDate);
Call<TripSearchResponse> call = apiService.searchAvailableTrips(TripType,departurePortId,returnPortId,departureDatePARSED,returnDatePARSED,pax);
call.enqueue(new Callback<TripSearchResponse>() {
#Override
public void onResponse(Call<TripSearchResponse> call, Response<TripSearchResponse> response) {
int statusCode = response.code();
switch(statusCode){
case 200:
default:
Snackbar.make(findViewById(android.R.id.content),"Error loading data. Network Error.", Snackbar.LENGTH_LONG).show();
break;
}
}
#Override
public void onFailure(Call<TripSearchResponse> call, Throwable t) {
Log.i(TAG, t.getMessage());
Snackbar.make(findViewById(android.R.id.content),"Error loading data. Network Error.", Snackbar.LENGTH_LONG).show();
}
});
}
The purpose is to make this callback function reusable so I can call it from several activities and get requested data as I need. What is the best way to implement this?
try this way, its dynamic way and easy to use:
Create Retforit Interface:
public interface ApiEndpointInterface {
#Headers("Content-Type: application/json")
#POST(Constants.SERVICE_SEARCH_TRIP)
Call<JsonObject> searchForTrip(#Body TripRequest objTripRequest);
}
Create Retrofit Class:
public class AppEndPoint {
private static Retrofit objRetrofit;
public static ApiEndpointInterface getClient() {
if (objRetrofit == null){
objRetrofit = new Retrofit.Builder()
.baseUrl(Constants.SERVER_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return objRetrofit.create(ApiEndpointInterface.class);
}
}
Create this helper Classes/Interfaces to hold web service callback:
public enum ResponseState {
SUCCESS,
FAILURE,
NO_CONNECTION
}
public enum RequestType {
SEARCH_FOR_TRIP // add name for each web service
}
public class Response {
public ResponseState state;
public boolean hasError;
public RequestType requestType;
public JsonObject result;
}
public interface RestRequestInterface {
void Response(Response response);
Context getContext();
}
public class ResponseHolder { used to hold the Json response could be changed as your response
#SerializedName("is_successful")
#Expose
private boolean isSuccessful;
#SerializedName("error_message")
#Expose
private String errorMessage;
public boolean isSuccessful() {
return isSuccessful;
}
public void setSuccessful(boolean successful) {
isSuccessful = successful;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}
public class AppClient {
private static ApiEndpointInterface objApiEndpointInterface;
private static Response objResponse;
private static Call<JsonObject> objCall;
// implement new method like below for each new web service
public static void searchForTrip(TripRequest objTripRequest, RestRequestInterface objRestRequestInterface) {
objResponse = new Response();
objResponse.state = ResponseState.FAILURE;
objResponse.hasError = true;
objResponse.requestType = RequestType.SEARCH_FOR_TRIP; // set type of the service from helper interface
objApiEndpointInterface = AppEndPoint.getClient();
objCall = objApiEndpointInterface.searchForTrip(objTripRequest);
handleCallBack(objRestRequestInterface);
}
private static void handleCallBack(final RestRequestInterface objRestRequestInterface) {
objCall.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, retrofit2.Response<JsonObject> response) {
try {
ResponseHolder objResponseHolder = new Gson().fromJson(response.body(), ResponseHolder.class);
if (objResponseHolder.isSuccessful()) {
objResponse.state = ResponseState.SUCCESS;
objResponse.hasError = false;
objResponse.result = response.body();
} else {
objResponse.errorMessage = objResponseHolder.getErrorMessage();
}
objRestRequestInterface.Response(objResponse);
} catch (Exception objException) {
objResponse.errorMessage = objRestRequestInterface.getContext().getString(R.string.server_error);
objRestRequestInterface.Response(objResponse);
}
}
#Override
public void onFailure(Call<JsonObject> call, Throwable objThrowable) {
String errorMessage = "";
if (objThrowable instanceof IOException) {
errorMessage = objRestRequestInterface.getContext().getString(R.string.no_connection_error);
} else {
errorMessage = objRestRequestInterface.getContext().getString(R.string.server_error);
}
objResponse.errorMessage = errorMessage;
objRestRequestInterface.Response(objResponse);
}
});
}
}
then go to your activity of fragment and make the call like this:
public class MainActivity extends AppCompatActivity implements RestRequestInterface {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// initialize ids
// prepare to call web service
// 1.Initialize your object to be sent over web service
TripRequest objTripRequest = new TripRequest();
objTripRequest.id = 1;
// 2.Show loader
// 3.Make the call
AppClient.searchForTrip(objTripRequest, this);
}
#Override
public void Response(Response response) {
// hide loader
try {
if (response.state == ResponseState.SUCCESS && !response.hasError) {
// check the type of web service
if (response.requestType == RequestType.SEARCH_FOR_TRIP) {
// acces the return here from response.result
}
} else {
String errorMsg = response.hasError ? response.errorMessage : getString(R.string.no_connection_error);
// show the error to the user
}
} catch (Exception objException) {
// show the error to the user
}
}
#Override
public Context getContext() {
// do not forgit set the context here
// if fragment replace with getAcitvity();
return this;
}
}
I am trying to properly handle Volley responses in my Android application, which loads some items from a database. Volley functions are encapsulated in the WebRequester class:
public class WebRequester extends Application {
public static final String TAG = WebRequester.class.getSimpleName();
private RequestQueue mRequestQueue;
private static WebRequester mInstance;
public WebRequester() {
mInstance = this;
}
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
}
return mRequestQueue;
}
public static synchronized WebRequester getInstance() {
return mInstance;
}
public <T> void addToRequestQueue(Request<T> req) {
req.setTag(TAG);
getRequestQueue().add(req);
}
/* ... */
}
Another class, ItemsController, centralizes the requests to be created. In order to get the response code, I created a nested class, VolleyCallback, and set its attribute responseCode inside an overriden parseNetworkResponse() call:
public class FeedItemsController extends Application {
private String URL_GET_FEED_ITEMS = /* My URL */;
private static final String TAG = FeedItemsController.class.getSimpleName();
private ArrayList<FeedItem> feedItems;
public class VolleyRequestCallback {
public int responseCode;
public int getResponseCode() {
return responseCode;
}
public void setResponseCode(int responseCode) {
this.responseCode = responseCode;
}
}
public void loadItems() {
final VolleyRequestCallback callback = new VolleyRequestCallback();
if (feedItems == null) {
feedItems = new ArrayList<>();
Cache cache = WebRequester.getInstance().getRequestQueue().getCache();
Cache.Entry entry = cache.get(URL_GET_FEED_ITEMS);
if (entry != null) {
try {
String data = new String(entry.data, "UTF-8");
parseJsonFeed(new JSONObject(data));
} catch (JSONException | IOException e) {
e.printStackTrace();
}
}
else {
JsonObjectRequest jsonReq = new JsonObjectRequest(Request.Method.GET, URL_GET_FEED_ITEMS, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
VolleyLog.d(TAG, "Response: " + response.toString());
parseJsonFeed(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d(TAG, "Error: " + error.getMessage());
}
}
) {
#Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
callback.setResponseCode(response.statusCode);
System.out.println("Code 1 = " + response.statusCode);
return super.parseNetworkResponse(response);
}
};
WebRequester.getInstance().addToRequestQueue(jsonReq);
}
}
System.out.println("Code 2 = " + callback.getResponseCode());
}
/* ... */
}
Then method loadItems() is called from another class. The issue is - when it enters the parseNetworkResponse() method, the resultCode is correctly set to, let's say, 200. However, when I try to reuse it outside the request overriding, it's 0 again:
Code 1 = 200
Code 2 = 0
It might be a bad implementation of a response handling, but my main question is why is the object attribute changed?
Thanks in advance
It turned out to be a not exciting bug. The call to parseNetworkResponse is asynchronous, meaning that when the first print is performed, the server had not responded yet.
Since I got some bad reviews I am rewriting this question...
I have an HTTP REST server and a client (Android app). I have programmed several APIs that work just fine, however there is one that is giving me a 400 error, and if I put a breakpoint in the server, it does not even triggers it. So, I would like to understand why it fails :( ...
It is very simple, I have a value object called Alarm with a few attributes, that I want to POST to the server for registration of object in the database.
This is the output:
Callback failure for call to http://10.0.0.3:8080/...
java.io.IOException: Unexpected code Response{protocol=http/1.1, code=400, message=, url=http://10.0.0.3:8080/BiTrack_API/api/assets/registerAlarm}
at it.bitrack.fabio.bitrack.AlarmView$2$1.onResponse(AlarmView.java:438)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:135)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
This is my client side Android button listener:
View.OnClickListener addAlarmAction = new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
alarm.setThreshold(Float.parseFloat(thresholdEditText.getText().toString()));
String alarmJson = j.makeJsonBodyForAlarmRegistration(alarm);
tagLinearLayout.setVisibility(view.GONE);
operatorLinearLayout.setVisibility(view.GONE);
thresholdLinearLayout.setVisibility(view.GONE);
assetSpinner.setSelection(0);
r.attemptAddNewAlarm(alarmJson,
new Callback() {
#Override public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override public void onResponse(Call call, Response response) throws IOException {
try (final ResponseBody responseBody = response.body()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
Headers responseHeaders = response.headers();
for (int i = 0, size = responseHeaders.size(); i < size; i++) {
System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
}
final String responseBodyString = responseBody.string();
final int resultCode = response.code();
try {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
Log.i("BiTrack", "attemptAddNewAlarm RESULT: " + resultCode);
executeAlarmRegistration(resultCode);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
};
This is the code where I generate the Json body for the POST in the client:
public String makeJsonBodyForAlarmRegistration (Alarm alarm) {
Gson gson = new Gson();
String jsonAlarm = gson.toJson(alarm);
return jsonAlarm;
}
This is the actual POST code in the client (Android) side:
public void attemptAddNewAlarm(String json, Callback callback) throws Exception {
final OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder()
.url(WEB_SERVER + "BiTrack_API/api/assets/registerAlarm")
.post(body)
.build();
client.newCall(request).enqueue(callback);
}
This is my server side code:
#POST
#Path("/registerAlarm")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response registerAlarm(Alarm alarm) {
System.out.println("Received API Call: registerAlarm for alarm tagId: " + alarm.getTagId() + " operatorId: " + alarm.getOperatorId() + " treshold: " + alarm.getThreshold());
DataProcessor dp = new DataProcessor();
AssetUpdateDAO aDAO = new AssetUpdateDAO();
ArrayList<Alarm> customerAlarms = aDAO.getUserAlarmsForAsset(alarm.getUserId(), alarm.getAssetId());
if (dp.isNewAlarmDuplicate(customerAlarms, alarm)) {
return Response.status(480).build(); // duplicated error
} else {
int response = aDAO.insertAssetUserAlarm(alarm.getUserId(), alarm.getAssetId(), alarm.getTagId(), alarm.getOperatorId(), alarm.getThreshold());
if (response == -5) {
return Response.status(484).build(); // something went wrong while inserting alarm into db
} else {
return Response.status(200).build();
}
}
}
This is my Alarm value object (identical class in client and server):
public class Alarm {
public Alarm() {
}
protected int id;
protected int userId;
protected int assetId;
protected int tagId;
protected int operatorId;
protected float threshold;
protected String networkAssetCode;
public String getNetworkAssetCode() {
return networkAssetCode;
}
public void setNetworkAssetCode(String networkAssetCode) {
this.networkAssetCode = networkAssetCode;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getAssetId() {
return assetId;
}
public void setAssetId(int assetId) {
this.assetId = assetId;
}
public int getTagId() {
return tagId;
}
public void setTagId(int tagId) {
this.tagId = tagId;
}
public int getOperatorId() {
return operatorId;
}
public void setOperatorId(int operatorId) {
this.operatorId = operatorId;
}
public float getThreshold() {
return threshold;
}
public void setThreshold(float threshold) {
this.threshold = threshold;
}
}
I really appreciate any help...
In order to help you, the endpoint code is required. Now it is even unclear what technology stack is used for your API.
But from the information that is present... The endpoint considers your json as invalid.
400 Bad Request
The request could not be understood by the server due to malformed
syntax. The client SHOULD NOT repeat the request without
modifications.
In jax-rs the payload is first deserialized before it reaches the method that is bound to the url en http method.
Possibly the deserializing is failing and it never reaches the breakpoint you set.
What would be interesting is the following:
logs or exception from the server. The client exception is not that helpful, since the server returns this response.
the actual (json) payload that is send over the wire.
what deserialization mechanism is used at the server end? Reflection or did you make your own deserializer?
I found the issue! After 48 hours looking for the impossible, discovered that I had done a small update at the object attribute at the server side that had not been replicated in the client side...
I aim to call Volley from another class in, a very succinct, modular way ie:
VolleyListener newListener = new VolleyListener();
VolleySingleton.getsInstance().somePostRequestReturningString(getApplicationContext(), newListener);
JSONObject data = newListener.getResponse();
But am having allot of trouble getting the listener portion to work so as to be able to access the resulting data from a method such as
newListener.getResponse();
There are a few questions on this site that generally outline how to set up a volley call from another class, such as: Android Volley - How to isolate requests in another class. I have had success getting the method call to work, but to now get that data into the present class for usage has caused trouble.
I have the action within my VolleySingleton class as:
public void somePostRequestReturningString(final Context context,final VolleyListener<String> listener) {
final String URL = "http://httpbin.org/ip";
JsonObjectRequest set = new JsonObjectRequest(Request.Method.GET, URL, ((String) null),
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
listener.outPut = response.toString();
//Toast.makeText(context, response.toString(), Toast.LENGTH_LONG).show();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d("Error.Response", error.toString());
}
}
);
mRequestQueue.add(set);
}
and within the listener class:
public class VolleyListener {
public static String outPut;
private static Response.Listener<String> createSuccessListener() {
return new Response.Listener<String>() {
#Override
public void onResponse(String response) {
outPut = response;
}
};
}
}
How can I configure this to work and allow Volley calls and data retrieval from another class, particularly how to build callbacks correctly?
For your requirement, I suggest you refer to my following solution, hope it's clear and helpful:
First is the interface:
public interface VolleyResponseListener {
void onError(String message);
void onResponse(Object response);
}
Then inside your helper class (I name it 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, inside your Activity classes, you can call like the following:
VolleyUtils.makeJsonObjectRequest(mContext, url, new VolleyResponseListener() {
#Override
public void onError(String message) {
}
#Override
public void onResponse(Object response) {
}
});
You can refer to the following questions for more information (as I told you yesterday):
Android: How to return async JSONObject from method using Volley?
POST Request Json file passing String and wait for the response Volley
Android/Java: how to delay return in a method
Volley excels at RPC-type operations used to populate a UI, such as
fetching a page of search results as structured data. It integrates
easily with any protocol and comes out of the box with support for raw
strings, images, and JSON. By providing built-in support for the
features you need, Volley frees you from writing boilerplate code and
allows you to concentrate on the logic that is specific to your app.
How to create Common GET/POST Method Using Volley .
Create a Application Class
The Application class in Android is the base class within an Android
app that contains all other components such as activities and services
public class MyApplication extends Application {
public static final String TAG = MyApplication.class
.getSimpleName();
private RequestQueue mRequestQueue;
private static MyApplication mInstance;
#Override
public void onCreate() {
super.onCreate();
mInstance = this;
}
public static synchronized MyApplication getInstance() {
return mInstance;
}
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
}
return mRequestQueue;
}
public <T> void addToRequestQueue(Request<T> req, String tag) {
// set the default tag if tag is empty
req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
getRequestQueue().add(req);
}
public <T> void addToRequestQueue(Request<T> req) {
req.setTag(TAG);
getRequestQueue().add(req);
}
public void cancelPendingRequests(Object tag) {
if (mRequestQueue != null) {
mRequestQueue.cancelAll(tag);
}
}
}
Make Sure you add this Manifest Section .
<application
.....
android:name=".MyApplication"
>
Now, You need to create Singleton Class .
Singleton Pattern says that just define a class that has only one
instance and provides a global point of access to it .
public class MySingleton
{
private static MySingleton mInstance;
private RequestQueue mRequestQueue;
private static Context mCtx;
private MySingleton(Context context)
{
mCtx = context;
mRequestQueue = getRequestQueue();
}
public static synchronized MySingleton getInstance(Context context)
{
if (mInstance == null)
{
mInstance = new MySingleton(context);
}
return mInstance;
}
public RequestQueue getRequestQueue()
{
if (mRequestQueue == null)
{
mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
}
return mRequestQueue;
}
public <T> void addToRequestQueue(Request<T> req)
{
getRequestQueue().add(req);
}
}
Now Common Class
public class VolleyUtils {
public static void GET_METHOD(Context context, String url, final VolleyResponseListener listener)
{
// Initialize a new StringRequest
StringRequest stringRequest = new StringRequest(
Request.Method.GET,
url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
listener.onResponse(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
listener.onError(error.toString());
}
})
{
};
// Access the RequestQueue through singleton class.
MySingleton.getInstance(context).addToRequestQueue(stringRequest);
}
public static void POST_METHOD(Context context, String url,final Map<String,String> getParams, final VolleyResponseListener listener)
{
// Initialize a new StringRequest
StringRequest stringRequest = new StringRequest(
Request.Method.POST,
url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
listener.onResponse(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
listener.onError(error.toString());
}
})
{
/**
* Passing some request headers
* */
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
getParams.put("Content-Type", "application/json; charset=utf-8");
return headers;
}
};
// Access the RequestQueue through singleton class.
MySingleton.getInstance(context).addToRequestQueue(stringRequest);
}
}
Now You should create Interface .
A class implements an interface, thereby inheriting the abstract
methods of the interface .
/**
* Created by Intellij Amiyo on 10-06-2017.
* Please follow standard Java coding conventions.
* http://source.android.com/source/code-style.html
*/
public interface VolleyResponseListener {
void onError(String message);
void onResponse(Object response);
}
How To Call
public void _loadAPI()
{
//GET
String URL_GET = "";
VolleyUtils.GET_METHOD(MainActivity.this, URL_GET, new VolleyResponseListener() {
#Override
public void onError(String message) {
System.out.println("Error" + message);
}
#Override
public void onResponse(Object response) {
System.out.println("SUCCESS" + response);
}
});
//POST
String URL_POST=" ";
VolleyUtils.POST_METHOD(MainActivity.this, URL_POST,getParams(), new VolleyResponseListener() {
#Override
public void onError(String message) {
System.out.println("Error" + message);
}
#Override
public void onResponse(Object response) {
System.out.println("SUCCESS" + response);
}
});
}
public Map<String,String> getParams()
{
Map<String, String> params = new HashMap<String, String>();
params.put("YOUR_KEY", "VALUE");
return params;
}
For demo you should Download Volley-Common-Method
If you followed the general example from Android Volley - How to isolate requests in another class, (including the stuff regarding the singleton stuff) and looking for the parsing part (or, how to actually use the objects you receive), then this is the (again very general) addition
say you have a Json object coming in, that looks somewhat like this :
{"users":
[{"username":"Jon Doe","userid":83},
{"username":"Jane Doe",userid":84}]}
and our User object would look something like this:
public class User
{
String username;
int userid;
public String getName()
{
return username;
}
public int getId()
{
return userid;
}
}
Important: When working with Gson (you will see later), the object
fields should be named according to params you get in the Json, this
sort of reflection is how the parsing works.
then, the request itself would look something like this
(note the listener callback returning a
List<User>
object back to the caller, you'll see later):
public class NetworkManager
{
//... other stuff
public void getUsers(final SomeCustomListener<List<User>> listener)
{
final String URL = "http://httpbin.org/ip";
StringRequest request = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>()
{
#Override
public void onResponse(String response)
{
Log.d(TAG + ": ", "getUsers Response: " + response);
List<User> users = MyJsonParser.getListObjects(response, "$.users[*]", User.class);
if(null != users)
listener.getResult(users);
}
},
new Response.ErrorListener()
{
#Override
public void onErrorResponse(VolleyError error)
{
if (null != error.networkResponse)
{
Log.d(TAG + ": ", "Error Response code: " + error.networkResponse.statusCode);
listener.getResult(null);
}
}
});
requestQueue.add(request);
// ... other stuff
}
what you would need now is that class to parse the Json string, namely the object list, in this example I use Gson (again - this is a general example, change and reorder stuff according to your needs, you could probably also optimize this some more - it's just for the explanation):
public class MyJsonParser
{
//... other stuff
public static <T> List<T> getListObjects(String json_text, String json_path, Class<T> c)
{
Gson gson = new Gson();
try
{
List<T> parsed_list = new ArrayList<>();
List<Object> nodes = JsonPath.read(json_text, json_path);
for (Object node : nodes)
{
parsed_list.add(gson.fromJson(node.toString(), c));
}
return (parsed_list);
}
catch (Exception e)
{
return (new ArrayList<>());
}
}
//... other stuff
}
So, after we have all this (and the following stuff from the pre-mentioned SO question), what you said you were looking for is the callback in your working code, well that can be achieved in a couple of ways:
A straight forward way:
just call the method and override it's callback right there, e.g:
public class SomeClass
{
private List<User> mUsers;
private void someMethod()
{
// ... method does some stuff
NetworkManager.getInstance().getUsers(new SomeCustomListener<List<User>>()
{
#Override
public void getResult(List<User> all_users)
{
if (null != allUsers)
{
mUsers = allUsers;
// ... do other stuff with our info
}
}
});
// ... method does some more stuff
}
}
Or, in an indirect way (considering the time, memory consumption, etc. ), you can save the info you got in the same Singelton (or another container), and create a get method for it, and just get the object later (looks more slick)
remember: fire the request before (considering the latency for the response), as the nature of these callbacks is to be dependent on the response which might be delayed.
It would then look like this:
private List<User> mUsers;
private void someMethod()
{
// ... method does some stuff
mUsers = NetworkManager.getInstance().getUsersObject();
// ... method does some more stuff
}
A different option entirely would be to consider using Retrofit, that does the parsing for you, uses annotations, and is supposedly a lot faster , that might be what you're looking for (for the streamlined look) - I would read up on benchmarks, especially since the new 2.0 version came out.
Hope this Helps (although somewhat late)! :)