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.
Related
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 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
In android I want to make a basic login and registration application. I am following this tutorial. The application works properly and runs. I am just trying to understand the code now and after many google searches I can not understand some of the code and was wondering if somebody could help me understand it.
Below I have posted the method I do not understand and in comments highlighted what I do not understand - any clarification is much appreciated, I have also commented the code to what I believe the code does, if any of it is incorrect please tell me, you can also view all of the code on the tutorial website.
I am mainly confused about how the sharedpreferences works I have followed his tutorial on sharedpreferences too I understand that but do not understand this. Thank you and sorry if the problem is very basic
private void checkLogin(final String email, final String password) {
// Tag used to cancel the request
String tag_string_req = "req_login";
// Dialog stating trying to login
pDialog.setMessage("Logging in ...");
showDialog();
// Send the request over to the database to check details
StringRequest strReq = new StringRequest(Method.POST,
AppConfig.URL_LOGIN, new Response.Listener<String>() {
// Do this once you get a response
#Override
public void onResponse(String response) {
Log.d(loginName, "Login Response: " + response.toString());
hideDialog();
// Break the response up into individual things and store in variables
try {
JSONObject jObj = new JSONObject(response);
boolean error = jObj.getBoolean("error");
// Check for error node in json
if (!error) {
// I DO NOT UNDERSTAND THIS!!! how does this bit work?
// it sets the shared preferences login to true correct?
// but how does it set it true to only this particular user?
// Because it doesnt store the email and password along with it
// and sets its tag "isLoggedIn" and then saves it to the shared
// preferences
session.setLogin(true);
// Now store the user in SQLite
String uid = jObj.getString("uid");
JSONObject user = jObj.getJSONObject("user");
String name = user.getString("name");
String email = user.getString("email");
String created_at = user
.getString("created_at");
//I DO NOT UNDERSTAND THIS!!! Why do you need to do this & does this
//affect the MySQL DB at all?
db.addUser(name, email, uid, created_at);
// I DO NOT UNDERSTAND THIS!!! Why do you need to write LoginActivity.this
// do you not just write MainActivity?
Intent intent = new Intent(LoginActivity.this,
MainActivity.class);
startActivity(intent);
finish();
} else {
// Error in login. Get the error message
String errorMsg = jObj.getString("error_msg");
Toast.makeText(getApplicationContext(),
errorMsg, Toast.LENGTH_LONG).show();
}
} catch (JSONException e) {
// JSON error
e.printStackTrace();
Toast.makeText(getApplicationContext(), "Json error: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e(loginName, "Login Error: " + error.getMessage());
Toast.makeText(getApplicationContext(),
error.getMessage(), Toast.LENGTH_LONG).show();
hideDialog();
}
}) {
/***************************************************************/
//I DO NOT UNDERSTAND THIS WHOLE METHOD WHY DO YOU DO THIS?!!!
/***************************************************************/
#Override
protected Map<String, String> getParams() {
// Posting parameters to login url
Map<String, String> params = new HashMap<String, String>();
params.put("email", email);
params.put("password", password);
return params;
}
};
// FINALLY I ALSO DO NOT UNDERSTAND WHY YOU DO THIS! AND WHAT DOES IT DO
AppController.getInstance().addToRequestQueue(strReq, tag_string_req);
}
This adds a user to an SQL database:
db.addUser(name, email, uid, created_at);
There should be a class somewhere that defines the actual function, which then creates the query that actually interacts with the database.
The intent changes the activity (what is rendered on the screen and what logic is handled):
LoginActivity.this: the context in the current class - this can be simplified to just this, but it's a bit of syntactic sugar in Java that attempts to clarify which this is being referred to.
MainActivity.class: the target activity
Intent intent = new Intent(LoginActivity.this,
MainActivity.class);
The difference between two activities can be explained with the content of a game. The menu is "LoginActivity.this" and "MainActivity.class" is the actual game content
As for shared preferences, the usage is pretty straight-forward:
To obtain shared preferences, use the following method In your
activity:
SharedPreferences prefs = this.getSharedPreferences(
"com.example.app", Context.MODE_PRIVATE);
To read preferences:
String dateTimeKey = "com.example.app.datetime";
// use a default value using new Date()
long l = prefs.getLong(dateTimeKey, new Date().getTime());
To edit and save preferences
Date dt = getSomeDate();
prefs.edit().putLong(dateTimeKey, dt.getTime()).apply();
(Source, posted by naikus)
The internal mechanics aren't something you need to worry about - the thing you really need to know is that it's able to save your data in a way you can use that doesn't involve directly accessing files (which has become a maze since Android 10).
EDIT:
Based on what I saw at the tutorial, the entire thing is to check if the login information entered exists in the database. The getParams() method defines what goes into the form data
I am using Volley to make Http requests to my Web Api.
However I am having trouble getting the values from my api calls due to the asynchronous nature of Volley.
I have read that using a callback function could help with this issue, however I do not know how to implement such a solution.
How would I go about implementing a callback function in the following scenario?
public class Main
{
String name;
WebServiceConnections wsc = new WebServiceConnections();
name = wsc.getNameFromWeb();
System.out.println("Name: " + name);
}
public class WebServiceConnections
{
public String getNameFromWeb()
{
String url = "http://nameservice.net/GetName";
JsonArrayRequest req = new JsonArrayRequest(url,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
try {
return response.getString("Name");
}
catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d(TAG, "Error: " + error.getMessage());
}
});
}
}
The problem with this code is that the variable "name" in Main will be null when it is called by the print statement as the asynchronous method in the WebServiceConnections class will not be finished by time the print statement is called.
Is a callback a good way to solve this problem?
Your code doesn't compile - you can't return a value in a method with void return type.
Your onResponse method is the callback. Perform the print within the method itself.
My app crashes because the images ArrayList is empty when I set the adapter, I figured that out by putting a toast message right after I parse my JSON request, and a Toast message after I initialize my adapter, "second" gets printed first on screen and the app crashes right after, does it have to do with my internet? Or am I missing something, here's my code, thanks!
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_page);
mViewPager = (ViewPager) findViewById(R.id.view_pager);
mVolleySingleton = VolleySingleton.getInstance();
mRequestQueue = mVolleySingleton.getRequestQueue();
//First Toast message inside this method
sendAPIRequest();
//after you get the images
mCustomSwipeAdapter = new CustomSwipeAdapter(this, images);
//SECOND TOAST
Toast.makeText(getApplicationContext(), "Second", Toast.LENGTH_LONG).show();
mViewPager.setAdapter(mCustomSwipeAdapter);
mCustomSwipeAdapter.notifyDataSetChanged();
}
public void sendAPIRequest(){
String requestURL = "";
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, requestURL, (String) null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
parseJSONResponse(response);
//FIRST TOAST : SHOULD BE CALLED FIRST
Toast.makeText(getApplicationContext(), "First", Toast.LENGTH_LONG).show();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
mRequestQueue.add(jsonObjectRequest);
}
public void parseJSONResponse(JSONObject response) {
if (response != null || response.length() != 0) {
try {
JSONObject GObject = response.getJSONObject("game");
String name = "N/A";
if (GObject.has("name") && !GObject.isNull("name")) { name = GObject.getString("name"); }
if (GObject.has("screenshots") && !GObject.isNull("screenshots")) {
JSONArray screenShotsArray = GObject.getJSONArray("screenshots");
for (int i = 0; i < screenShotsArray.length(); i++){
JSONObject screenshot = screenShotsArray.getJSONObject(i);
String screenshotURL = screenshot.getString("url");
images.add(screenshotURL);
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
Does it have to do with my internet? Or am I missing something ...
Both. It happens because you have a race condition.
From what I can make out, your images list is being populated asynchronously by the onResponse callback. Basically, that happens when your app gets the responses to the API requests that it is making. That is going to take at least milliseconds, and possibly seconds (or longer).
But your app is (so you say) crashing soon after the swipe adapter is registered, and the evidence is that the images list has not been populated.
There are three possibilities:
There is something wrong with the requests you are sending which is causing the API requests to not give you any response. (Hypothetically, you could have authentication wrong or something.)
The API requests are taking a long time because of internet connection speed, congestion, or the remote server being slow.
The API requests are taking a short time ... but the adapter registration is even quicker.
If (hypothetically) there is a problem with your requests you will need to fix that. But both of the other scenarios have to be fixed by:
modifying the code that uses the images to work properly if there are no images (yet), or
modifying the code to wait until the image loading has completed before registering the adapter.
Please use this code in your onResponse callback :
//after you get the images
mCustomSwipeAdapter = new CustomSwipeAdapter(this, images);
//SECOND TOAST
Toast.makeText(getApplicationContext(), "Second", Toast.LENGTH_LONG).show();
mViewPager.setAdapter(mCustomSwipeAdapter);
mCustomSwipeAdapter.notifyDataSetChanged();
Volley adds your requests in queue , so better do all the dependent tasks in Response or Error callback only.