Android volley POST request parameters error on using parsed variable - android

//Code
private void searchQuery(String strQuery, String link){
Toast.makeText(getContext(), "You searched for "+strQuery, Toast.LENGTH_SHORT).show();
JsonArrayRequest request = new JsonArrayRequest(Request.Method.POST, link, new Response.Listener<JSONArray>(){
#Override
public void onResponse(JSONArray jresponse) {
//displayResultInList(jresponse);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}) {
#Override
protected Map<String, String> getParams() throws com.android.volley.AuthFailureError {
Map<String, String> parameters = new HashMap<String, String>();
parameters.put("query", strQuery);
return parameters;
}
};
RequestQueue rQueue = Volley.newRequestQueue(getActivity());
rQueue.add(request);
}
This particular line: parameters.put("query", strQuery); is giving an error and the error message says:
Variable 'strQuery' is accessed from within inner class, needs to be declared final.
I did some research and read some tutorials on POST requests using Volley and the large chunk of them are not using variables but strings in that function.The problem with setting the variable as final is that the user is typing it in therefore that variable will only have a value after the query is submitted.

It's because you're using the parameter variable in a block of code inside the method.
The JAVA compiler demands that the variables from outside the scope of the Response.Listener block are declared as final to guarantee that the variable value keeps the same (when the block is declared) when running the code.
It assures that you cannot modify the value of the variable after declaring the block.
So, you DO NEED to put final before String strQuery in the function parameter, since you're using it inside the block.

Related

How to pass Parameter In retrofit

Hear is my API:
http://v2sgroups.in/Erp_V2s_Groups/AndroidPanel/OTPVerification/099567.
I try to call Api through retrofit framework , how can I pass parameter in retrofit like above link. 099657 is the passing parameter.
#GET("/AndroidPanel/OTPVerification/")
void otp(#Field("otp") String otp,
Callback<OTP> callback);
how to pass 099567 in using interface?
Its a path, you can do:
#GET("/AndroidPanel/OTPVerification/{otp}")
void otp(#Path("otp") String otp,
Callback<OTP> callback);
You can pass parameter by #QueryMap
Retrofit uses annotations to translate defined keys and values into appropriate format. Using the #Query("key") String value annotation will add a query parameter with name key and the respective string value to the request url .
public interface API{
#POST("media-details")
retrofit2.Call<MediaDetails>getMediaList(#QueryMap Map<String, String> param);
}
private void getData() {
Map<String, String> data = new HashMap<>();
data.put("id", "12345");
data.put("end_cursor", "00000");
Call<MediaDetails> mediaDetails = ServiceAPI.getService().getMediaList(data);
mediaDetails.enqueue(new Callback<MediaDetails>() {
#Override
public void onResponse(Call<MediaDetails> call, Response<MediaDetails> response) {
}
#Override
public void onFailure(Call<MediaDetails> call, Throwable t) {
System.out.println("Failed");
}
});
}

Why does ArrayAdapter fail to update listview after activity restart?

I have an activity that in onCreate() does the following:
Creates an empty ArrayList
Creates a new ArrayAdapter associated with the above ArrayList
Sets ListView to use the above ArrayAdapter
Uses Volley to send a GET request to my API to fetch some JSON data to load into the ListView
Once the data is fetched I add it to my ArrayList and the ListView is populated as expected
My problem is that when the activity is restarted (i.e. the screen is rotated via the emulator or the activity is restarted through Android Studio) the ListView no longer populates.
I am not saving any state. I expect the activity to return to its initial default state so I don't think onSaveInstanceState() is the answer.
I've verified that the data is returned successfully from the API and that the adapter's hashcode is the same before and after the volley request and that it equals the ListView's set adapter. I've also verified that onDestroy() and then onCreate() are called when the activity is restarted so I know it is going through a full life cycle.
If I rotate the screen programmatically with setRequestedOrientation() I don't experience this issue. If I add items to my ArrayList outside of the GET request callback, I don't experience this issue.
Here is my activity onCreate()
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
//The data to be displayed
descriptions = new ArrayList<>();
listView = (ListView)this.findViewById(R.id.myListView);
//Link 'descriptions' to the adapter
adapter = new ArrayAdapter<>(this, R.layout.list_json_text_view, descriptions);
listView.setAdapter(adapter);
this.addTextFilter();
this.addListViewClickListener();
//See my ApiGetRequest class below
request = new ApiGetRequest();
request.send(this.getContext(), getDataUrl(), this, "", REQUEST_TYPES.TEXT);
}
And my activity GET request callback
public void onSuccess(DescriptiveJSONArray items, REQUEST_TYPES type) {
descriptions.clear();
try {
for (int i = 0; i < items.length(); ++i) {
JSONObject obj = items.getJSONObject(i);
String desc = obj.optString("name", "") + " " + obj.optString("description", "");
//TODO: Remove debug code
System.out.println("Adding: "+desc);
descriptions.add(desc);
}
}
catch(JSONException e) {
e.printStackTrace();
//getJSONObject failed
}
}
And my ApiGetRequest methods
//My activity implements ApiGetCallback
public void send(Context context, String url, ApiGetCallback callback, String tag, REQUEST_TYPES type) {
StringRequest stringRequest = getStringRequest(url, callback, tag, type);
//Singleton wrapper for RequestQueue
AppRequestQueue queue = AppRequestQueue.getInstance(context);
queue.add(stringRequest);
}
//Inner class inside ApiGetCallback
class SuccessListener implements Response.Listener<String> {
ApiGetCallback callback;
REQUEST_TYPES type;
public SuccessListener(ApiGetCallback callback, REQUEST_TYPES type) {
this.callback = callback;
this.type = type;
}
#Override
public void onResponse(String response) {
try {
DescriptiveJSONArray jsonResp = new DescriptiveJSONArray(response);
callback.onSuccess(jsonResp, type);
}
catch(JSONException e) {
callback.onJsonException(e);
}
}
}
Any ideas what is happening?. I'm testing on Marshmallow and Nougat
You are missing a call to notifyDataSetChanged, after the onSuccess function is done.
you may need to override onStart and do update anything in it
adapter = new ArrayAdapter<>(this, R.layout.list_json_text_view, descriptions);
listView.setAdapter(adapter);
//See my ApiGetRequest class below
request = new ApiGetRequest();
request.send(this.getContext(), getDataUrl(), this, "", REQUEST_TYPES.TEXT);
use this part of code in onResume method.

Avoid getting Race condition in Android Volley in Android app

I'm new to Android Development and I'm trying to develop my first Android app which gets data from some public APIs using Android Volley.
I'm using singleton Volley Request Queue which is initialized in the launcher activity. I am successfully able to parse the JSON contents and display them on a Fragment layout/view (uses RecyclerView & CardView) when I set my RecyclerView adapters INSIDE the Volley's JsonObjectRequest.
The following code does display data, but suffers from time race condition.
Note: RvJoiner is a library which merges multiple adapters and makes a single adapter ordered by FIRST COME FIRST SERVE basis.
My Fragment class is as follows:
public class Fragment1 extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.recylcer_main, container, false);
ParseJSON parseJSON = new ParseJSON(v);
parseJSON.makeRequest1();
parseJSON.makeRequest2();
return v;
}
}
My ParseJSON class is as follows
public class ParseJSON {
private static final String URL1 = "some url";
private static final String URL2 = "some other url";
private static final String TAG = "ParseJSON";
private RequestQueue requestQueue;
private boolean FLAG_REQUEST1_FETCHED;
private boolean FLAG_REQUEST2_FETCHED;
private ArrayList<status1> status1ArrayList;
private ArrayList<status2> status2ArrayList;
private Context context;
private RvJoiner rvJoiner;
private View view;
ProgressDialog pd;
ParseJSON (View v){
this.view= v;
this.context=v.getContext();
pd = ProgressDialog.show(v.getContext(), "Please Wait", "Getting Data from APIs", true);
requestQueue = AppController.getInstance(v.getContext()).getRequestQueue();
rvJoiner = new RvJoiner();
}
public void makeRequest1() {
JsonObjectRequest request1 = new JsonObjectRequest(Request.Method.GET, URL1,
null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
/* Parsing Stuff and storing it in status1ArrayList */
FLAG_REQUEST1_FETCHED=true;
Status1Adapter status1Adapter = new Status1Adapter(status1ArrayList);
RecyclerView recList = (RecyclerView) view.findViewById(R.id.cardList);
recList.setLayoutManager(new LinearLayoutManager(context));
rvJoiner.add(new JoinableAdapter(status1Adapter));
recList.setAdapter(rvJoiner.getAdapter());
pd.dismiss();
}
} catch (JSONException e) {}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {}
});
AppController.getInstance(context).addToRequestQueue(request1);
}
public void makeRequest2() {
JsonObjectRequest request2 = new JsonObjectRequest(Request.Method.GET, URL2,
null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
/* Parsing stuff and storing it inside ArrayList status2ArrayList */
FLAG_REQUEST2_FETCHED=true;
Status2Adapter status2Adapter = new Staus2Adapter(status2ArrayList);
RecyclerView recList = (RecyclerView) view.findViewById(R.id.cardList);
recList.setLayoutManager(new LinearLayoutManager(context));
rvJoiner.add(new JoinableAdapter(status2Adapter));
}
} catch (JSONException e) {}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {}
});
AppController.getInstance(context).addToRequestQueue(request2);
}
public boolean isStatusFetched(){
return FLAG_REQUEST1_FETCHED && FLAG_REQUEST2_FETCHED;
}
public ArrayList<status1> getstatus1ArrayList() {
return status1ArrayList;
}
public ArrayList<status2> getstatus2ArrayList() {
return status2ArrayList;
}
}
In the above code, I'm having a race condition. Since Volley network calls are asynchronous in nature, I have no control on which request will get completed and displayed on my Fragment CardView first. (i.e any of rvJoiner.add() requests can be executed first)
I would like to make my UI consistent i.e I want Request1 adapter to be added to RvJoiner first and then the Request2.
If possible, I would like to move all my code that sets adapters and joins them from JsonObjectRequest to my Fragment's onCreateView method. So, in this way, I have a control on the order of adapters. However, then I need a method which checks the value of FLAG_REQUEST1_FETCHED and FLAG_REQUEST2_FETCHED via isStatusFetched method continuously.
Code for the Fragment class will be
public class Fragment1 extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.recylcer_main, container, false);
ParseJSON parseJSON = new ParseJSON(v);
parseJSON.makeRequest1();
parseJSON.makeRequest2();
while(!parseJSON.isDataFetched()){
/* I want to wait here till both status1ArrayList and status2ArrayList gets populated with data in ParseJSON. In this way I can control the order in which adapters are added inside RvJoiner. If I don't wait here I will get NullPointerException on runtime since Volley calls are asynchronous and getStatus1ArrayList/getStatus2ArrayList will most probably return null. But how to wait here without consuming too much CPU power? */
}
ArrayList<status1> status1ArrayList = parseJSON.getstatus1ArrayList();
ArrayList<status2> status2ArrayList = parseJSON.getstatus2ArrayList();
Status1Adapter status1Adapter = new Status1Adapter(status1ArrayList);
Status2Adapter status2Adapter = new Status2Adapter(status2ArrayList);
RecyclerView recList = (RecyclerView) v.findViewById(R.id.cardList);
recList.setLayoutManager(new LinearLayoutManager(v.getContext()));
RvJoiner rvJoiner = new RvJoiner();
/* Problem solved as I'm adding adapters in the order I want */
rvJoiner.add(new JoinableAdapter(status1Adapter));
rvJoiner.add(new JoinableAdapter(status2Adapter));
recList.setAdapter(rvJoiner.getAdapter());
return v;
}
}
One solution can be using callbacks. I read somewhere about them, but I'm not sure if it solves my problem of 'multiple request at the same time while maintaining order'.
Another solution would be to restrict my Volley Queue to handle one request at one time only but that would increase the time taken to fetch and serve data. This is my last choice.
I am virtually out of ideas and would like someone to help me so that I can control the order of setting my adapters and maintain a consistent UI. If you need any other information, please tell me.
Thanks.
This is how avoiding race conditions for two requests work in general. You should work with callbacks. The implementation of your onResponse methods are callbacks because those methods are called after one request is done. Response handling works on the UI thread right ? So the responses can just be handled one by the other.
This means you just have to maintain order there. Extract the work you would like to do after getting one response. You need some boolean flags indicating whether your requests are done. Pseudocode would look like this:
request1Done = false;
request2Done = false;
doRequest1();
doRequest2();
onResponse1() {
doWorkForRequest1(); // always start handling the response
request1Done = true;
if (request2Done) { // if this is true, request2 was faster than request1
doWorkForRequest2();
}
};
onResponse2() {
request2Done = true;
if (request1Done) { // request1 did its work, no its request2's turn
doWorkForRequest2();
}
};
So basically you should fix your onReponse methods. Hope this will help you. :)

Android Volley request - send object to Spring boot java web service

I have some custom POJO:
class CustomClass {
int x;
String str;
SecondCustomClass obj; //indicate it's not class of simple types
//etc...
}
I want to send instance of him from Android (Volley library) client to web service running on Spring-boot java application. Currently I know how to send data with URL params and return to client custom object. But I want also to send custom object.
Code in Android (I know that I need to use 3'rd parameter which is now null but I'm struggling get it work):
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET,
"BASE_URL?param1=param",
null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
CustomClass result = new Gson().fromJson(response.toString(), CustomClass.class);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}
);
volleyQueue.add(request);
Code in server:
EDIT: The solution to receive pojo is using #RequestBody
#RequestMapping("/webmethod")
public CustomClass webmethod(#RequestParam(value="obj_param") #RequestBody CustomClass obj) {
//work with obj
}
What do I need to put in Android side to get it work?
You have to use the JSON object inside the JSON object.Below is the sample. When you are request the Parameter with only one request.
This the Request JSON
{
"x":10,
"str":"MyName",
"SecondCustomClass":{
"id":10,
"title":"make it eassy"
}
}
This is the post parameter request from Android. Try this way.
For more details please use this link

RushORM store RequestParams

I'm using rushorm for sqlite object serializing and storage. It work pretty cool so far. The problem is that I wan't to store following Request object:
public class Request extends RushObject {
private String url;
private RequestParamsStorable params;
public Request() {}
public Request(String aUrl, RequestParamsStorable aParams)
{
this.url = aUrl;
this.params = aParams;
}
public String getUrl()
{
return this.url;
}
public RequestParams getParams()
{
return this.params;
}
}
As you can see I need to store RequestParams object. In order to store it, as I obviously cannot make it to extend RushObject I made a subclass and made it to implement Rush as per docs instructions:
public class RequestParamsStorable extends RequestParams implements Rush {
public RequestParamsStorable() {}
#Override
public void save() { RushCore.getInstance().save(this); }
#Override
public void save(RushCallback callback) { RushCore.getInstance().save(this, callback); }
#Override
public void delete() { RushCore.getInstance().delete(this); }
#Override
public void delete(RushCallback callback) { RushCore.getInstance().delete(this, callback); }
#Override
public String getId() { return RushCore.getInstance().getId(this); }
}
It didn't throw any errors and calling save() on Request object went smoothly. When I ask for stored objects like that:
List<Request> remainingsInDB = new RushSearch().find(Request.class);
I indeed receive stored Request objects, with proper url, however RequestParamsStorable is empty(""). I checked and when I save them, they definetely had values, and are not empty.
So the question is where I'm wrong?
Regards,
If your parent class RequestParams contains fields declared as final, they will not be restored.
As reference you can see the RushORM source code
ReflectionClassLoader.java
for (Field field : fields) {
field.setAccessible(true);
if (!annotationCache.get(clazz).getFieldToIgnore().contains(field.getName())) {
if (!loadJoinField(object, rushColumns, annotationCache, field, joins, joinTables)) {
if(rushColumns.supportsField(field)) {
String value = values.get(counter);
if(value != null && !value.equals("null")) {
rushColumns.setField(object, field, value);
}
counter++;
}
}
}
}
You should either remove final modifier of the fields or create wrapper object which will be stored in the db and then restore RequestParams from it
Okay the problem indeed is fields declared as final in RequestParams. #Stuart Campbell properly noted RequestParams reference. Now what I'm trying as a workaround is to store all properties(except Wrappers) as JSONObject, store this JSONObject as String and then restore state from it. I'm facing some issues with JsonWritter and didn't solved my issue yet. Not sure if it is a good idea to post relevant code here, or to post new question though?

Categories

Resources