I am new to android and OOP. I know one of Fowler's bad smells is code duplication. I have about 25 volley requests in my whole projects in different activities, and in all of them, there is a 90% same code.
Look at this pseudo code:
on Error:
if no connection:
Toast "no connection"
else if no response:
Toast "server error"
else
show popup with a retry button which will call the function which this request is inside it again.
My first question is how to write these in one place and use the whole request object in different places.
My second question is there are some activity-related codes in some special activities. For example, on my login page I have no unauthorized error but in other pages I have. Or as you see, In my last else in the above code, I have said recall the function and if every request it different. How can I handle that?
How should I do that?
Is there any special reference?
tnx
You can create an util class to create the request.
For example this is a class able to create a GET request using JSONObject that display Toast messages on error, and execute the function that you pass as paramter on success:
public class VolleyUtils{
public static JsonObjectRequest createGetRequest(String url, Consumer<? super JSONObject> successCallback){
return new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
successCallback.consume(response)
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
if (volleyError instanceof TimeoutError || volleyError instanceof NoConnectionError) {
VolleyUtils.showToast("No Internet connection");
} else if (volleyError instanceof AuthFailureError) {
VolleyUtils.showToast("Bad authentication");
} else if (volleyError instanceof ServerError) {
VolleyUtils.showToast("Error with server");
} else if (volleyError instanceof NetworkError) {
VolleyUtils.showToast("Network error");
} else if (volleyError instanceof ParseError) {
VolleyUtils.showToast("Parsing Error");
}
}
});
}
private static void showToast(String errorText){
Toast.makeText(getApplicationContext(), errorText, Toast.LENGTH_SHORT).show();
}
}
So, for create a new request, you just need this:
//supposing you get the RequestQueue with Singleton Pattern
MySingleton.getInstance().addToRequestQueue(VolleyUtils.createGetRequest("www.google.com", (JSONObject response) -> {
//stuff to do when request has success, for example :
myTextView.setText(response.toString())
} ));
Instead of doing duplication of code, Put your code in a function (with or without argument, its depend on your task requirement) and call it where you want in your program.
If you have no idea how to make a function in Android or java Please follow this link
http://www.learnjavaonline.org/en/Functions
Related
I'm using this library to validate URL within my app but I wanna run the same method for 4 or more URLs instead of one but can't seem to figure out how to.
My code:
validateUrl(this, "https://www.dummy.dummy/");
SharedPreferences sharedPreferences = getSharedPreferences("url", MODE_PRIVATE);
if (sharedPreferences.getBoolean("validated", true)) {
Log.e("WEB", "RUNNING");
doSomething():
} else {
Log.e("WEB", "DOWN");
}
private void validateUrl (Context context, String URL) {
new FarlaGetRequest(context)
.setURL(URL)
.setListener(new FarlaGetRequest.onGetRequestListener() {
#Override
public void onSuccess(String response) {
Log.e("WEB", "Done");
SharedPreferences.Editor editor = getSharedPreferences("url", MODE_PRIVATE).edit();
editor.putBoolean("validated",true);
editor.apply();
}
#Override
public void onFailure(int error) {
Log.e("WEB", "Failed");
}
}).execute();
}
The goal is, if the url is able to connect (server sent response 200) then do something, else don't.
So what I'm stuck at is, how do I do this for multiple URLs?
Example:
Check 1st (log if it's running or down)
Check 2nd (log if its running or down)
Same for the 3rd and 4th as well.
At the end, it should say which are active and which ones aren't so can someone help me please? ease?
you can use a List of url's and execute that validator function for the number of times that the size of list is and if the url connects (got response 200) then just move to the next url otherwise remove that url from list. So , in this way you will end up with all the working URL's.
Solution Code:
List<String> urlList = new ArrayList<>();
// add all url's in the list; for example : urlList.add("https://www.dummy.dummy/");
Now we will use a loop to execute the checker function for all the url's
for(int i =0; i<urlList.size();i++)
{
validateUrl (context, urlList.get(i))();
}
and in the YOUR_URL_CHECKER_FUNCTION just put a condition that if the server sents response as 200 then do nothing otherwise urlList .remove("https://www.dummy.dummy/")
Sample code:
private void validateUrl (Context context, String URL) {
new FarlaGetRequest(context)
.setURL(URL)
.setListener(new FarlaGetRequest.onGetRequestListener() {
#Override
public void onSuccess(String response) {
// HERE YOU CAN CHECK THAT WHAT'S THE RESPONSE**
if(!response.equals("200"))
{
`urlList .remove("https://www.dummy.dummy/")`// link to remove
}
}
#Override
public void onFailure(int error) {
Log.e("WEB", "Failed");
}
}).execute();
}
Hope you get that. Feel free to ask if something is unclear. And kindly mark it as the correct answer if it helps you so that this answer can help any other needy.😀
I am working on an Android app. Therefore I'm creating a Login/Register Aktivity.I'm using JsonRequest (Volley) to get the Information stored in an SQL Data-base.Furthermore I wrote some PHP files (for Testing Purpose) to create a JSONObject as the Server Response.
Everything seems to work but only for some webhosts. What I mean by that is that the same Android Code and the same PHP Code once work fine and in the other case Output Errors.
My Test PHP Code uploaded via FileManagers
<?php
$myObj=new stdClass();
$myObj->name = "Test";
$myJSON = json_encode($myObj);
echo $myJSON;
?>
My Java Code
RequestQueue requestQueue= Volley.newRequestQueue(this);
JsonObjectRequest jsonObjectRequest=new JsonObjectRequest(Request.Method.GET,url,null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
String success=response.getString("name");
Toast.makeText(getApplicationContext(),success, Toast.LENGTH_SHORT).show();
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), "ErrorCatch"+e.toString(), Toast.LENGTH_LONG).show();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(getApplicationContext(), "ErrorResponse"+error.toString(), Toast.LENGTH_LONG).show();
}
});
requestQueue.add(jsonObjectRequest);
Using one Webhost I get the Correct Toast including the stored Information from the Server. Using another Webhost I get an Error:"Java.lang.String cannot be converted to JSONObject" which I find Odd since I'm doing the same Stuff.
I'm trying to find a way to handle asynchronous network requests in Swift. My plan is to isolate these calls in their own class, so I need a way to handle callbacks. In Android, I'm doing something like the following using an Interface:
apiService.fetch(data, new Callback() {
#Override
public void onResponse(Response r) {
// success
}
#Override
public void onFailure(Error e) {
// error
}
});
Can someone point me in the direction on how I can handle this similarly in Swift 3? Thanks!
It's possible to have multiple completion handlers. Here is a short example:
func request(url:String, success:#escaping(Any) -> Void, failure:#escaping(Any?) -> Void)
{
let task = URLSession.shared.dataTask(with: URL.init(string: url)!) { (data, response, error) in
if let responseError = error
{
failure(responseError)
}
else if let responseData = data //Make additional checks if there is an error
{
success(responseData) //Call when you are sure that there is no error
}
else
{
failure(nil)
}
}
task.resume()
}
Example of usage:
self.request(url: "https://jsonplaceholder.typicode.com/posts", success: { (data) in
//Do if success
}) { (error) in
//Do if error
}
I am trying to make this application in Android, I am getting data from foursquare's API in JSON format and I need to Parse it to present it in another intent.
I am using Android's volley library to get the JSON but the problem is the onResponse() function of JsonObjectRequest has no return parameter.so I cannot get the JSON object gotten from url outside of the the onResponse.
I haven't worked with volley before and hence don't know much about it, any help is appreciated. Here is the code that I am trying to make it work.
Edit: The main problem I'm facing is that I cannot assign a value to global variable in this case myPlaces inside the JsonObjectRequest's onResponse method. Or to be exact, the variable assigned inside means nothing outside, thus in the last line
Toast.makeText(MainActivity.this, myPlaces[2].getName(), Toast.LENGTH_LONG).show();
when I try to access the myPlaces[2] it gives me an null pointer exception.
Thanks.
public void onClick(View v) {
RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this);
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(urlString, null, new com.android.volley.Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
JSONObject meta = response.getJSONObject("meta");
String status = meta.getString("code");
Toast.makeText(MainActivity.this, status, Toast.LENGTH_SHORT).show();
if(status.equals("200"))
{
JSONObject responseJson = response.getJSONObject("response");
JSONArray venues = responseJson.getJSONArray("venues");
Places[] tempPlaces = new Places[venues.length()];
for (int i = 0 ; i < venues.length(); i++)
{
Places place = new Places();
JSONObject venueObject = venues.getJSONObject(i);
place.setName(venueObject.getString("name"));
JSONObject locatVenue = venueObject.getJSONObject("location");
place.setLat(locatVenue.getDouble("lat"));
place.setLon(locatVenue.getDouble("lng"));
tempPlaces[i] = place;
}
Toast.makeText(MainActivity.this, tempPlaces[2].getName(), Toast.LENGTH_LONG).show();
myPlaces = tempPlaces;
}
else
{
Toast.makeText(MainActivity.this, "No response from API", Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(MainActivity.this, "There is some error here", Toast.LENGTH_LONG).show();
}
}
}, new com.android.volley.Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(MainActivity.this, "There has been some error", Toast.LENGTH_LONG).show();
}
});
requestQueue.add(jsonObjectRequest);
Toast.makeText(MainActivity.this, myPlaces[2].getName(), Toast.LENGTH_LONG).show();
Volley itself isn't an inner class; the response is an anonymous class.
You don't need a return in Volley, you just use the variables already defined in your class.
I'm assuming myPlaces is a field in your class? Otherwise, I'm not sure where it is declared outside the onClick..
This line assigns myPlaces and looks like it would work fine
myPlaces = tempPlaces;
You could define a method in your class to parse the whole JSONObject instead of needing to return from Volley. This just passes the logic to another method, so you don't need to think about "returning" inside Volley.
public void parseJSON(JsonObject object)
And pass the response from volley into that and do your normal parsing and variable assignment and you can Toast myPlaces inside that method.
Also, note that Volley is asynchronous, meaning you aren't guaranteed an immediate result, so
Toast.makeText(MainActivity.this, myPlaces[2].getName(), Toast.LENGTH_LONG).show();
Would likely have thrown either a NullPointerException or IndexOutOfBoundsException because myPlaces was either undeclared or empty before the Volley request. I say that because it does not appear to be assigned before the Volley request.
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.