Android Volley Why does JsonObjectRequest does not call onResponse - java

I am trying to get my volley JsonObjectRequest working. When calling the getById() meth, onResponse() does not get called. I already have a working post request. So the connection parameters are correct. I do not get any error response or helpfull responses in LogCat.
I created a test class in order to isolate the getById() method.
public class Test {
Customer customer;
public Customer getById(int id) {
Log.i("CustomerDAO getById", "getById called");
String urlJsonObj = "http://192.168.0.39:3000/Customers/" + id;
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, urlJsonObj, null, new Response.Listener < JSONObject > () {
#Override
public void onResponse(JSONObject response) {
try {
// Parsing json object response
// response will be a json object
customer = new Customer(response.getInt("customernumber"), response.getString("name"),
response.getString("lastname"), response.getString("phonenumber"),
response.getInt("addressid"), response.getString("password"));
} catch (JSONException e) {
Log.e("JSON Exception:", e.toString());
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("GetByIdErrorResponse", error.toString());
}
});
AppController.getInstance().addToRequestQueue(request);
return customer;
}
}
This is the singleton RequestQueue class.
public class AppController extends Application {
public static final String TAG = AppController.class
.getSimpleName();
private RequestQueue mRequestQueue;
private static AppController mInstance;
#Override
public void onCreate() {
super.onCreate();
mInstance = this;
}
public static synchronized AppController getInstance() {
return mInstance;
}
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
Log.i("RequestQueue= ", mRequestQueue.toString());
}
return mRequestQueue;
}
}
This is how I call the getById() method
Test test = new Test();
Entities.Customer check = test.getById(1);

There are a few mistakes in your code. Since volley is an asynchronous networking library, you can't return the result from the network as you have done. You are initializing your Customer model class inside OnResponse and returning it from outside of it.
So what will be happening when you perform this operation is
Creates Request -> Adds to Request Queue -> getById() method returns null
The return statement, at last, won't wait till the response arrives. You'll be getting null every time. So what you have to do is to implement a custom callback when the API returns a result or error.
Create an interface for inside Test class for handling API response.
interface APICallback {
void onSuccess(Customer customer);
void onError(String error);
}
Pass these interface implementation to the getById() method along with id
public void getById(int id,APICallback callback) {
....
}
call methods on result
#Override
public void onResponse(JSONObject response) {
try {
// Parsing json object response
// response will be a json object
Customer customer = new Customer(response.getInt("customernumber"), response.getString("name"),
response.getString("lastname"), response.getString("phonenumber"),
response.getInt("addressid"), response.getString("password"));
callback.onSuccess(customer);
} catch (JSONException e) {
Log.e("JSON Exception:", e.toString());
}
}
#Override
public void onErrorResponse(VolleyError error) {
Log.e("GetByIdErrorResponse", error.toString());
callback.onError(error.toString());
}
and now you can call getById() as follows
Test test = new Test();
test.getById(1, new Test.APICallback() {
#Override
public void onSuccess(Customer customer) {
}
#Override
public void onError(String error) {
}
});

Related

Access Activity Method From AsyncTask Callback Interface

I have several activities and one asyncTask that uses the same interface for callback methods as below
The interface used by all
public interface AsyncTaskCallback {
void onCookie(CookieManager cookieManager);
void onResponse(String response);
void onProgress(String... values);
void onError(Exception e);
}
AsyncTask1 is called from all activities as follows
public void exec_taskt() {
alertDialog.SetText("Sending Request...");
AsyncTask1 task1 = new AsyncTask1("https://stackoverflow.com");
task1.setCookieManager(cookiejar);
task1.setCallback(this);
task1.execute();
}
Each Activity also implements the interface
#Override
public void onCookie(CookieManager cookieManager) {
cookiejar = cookieManager;
}
#Override
public void onResponse(String response) {
try {
PostProc(response);
}catch (Exception e){ // ERROR HERE
e.printStackTrace();
}
}
#Override
public void onProgress(String... values) {
alertDialog.SetText(values[0]);
}
#Override
public void onError(Exception e) {
///SAME ERROR HERE TOO
//Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_LONG).show();
swipeRefreshLayout.setRefreshing(false);
}
private void PostProc(String response) {
//the response string is parsed and displayed in a recyclerview in this method
//this method is slightly different for each activity
}
AsyncTask1
public class AsyncTask1 extends AsyncTask<String, String, String> {
private String address = "";
private CookieManager mCookieManager;
private Exception mException;
private AsyncTaskCallback mCallback;
public AsyncTask1 (String page) {
this.address = page;
}
public void setCookieManager(CookieManager cm) {
this.mCookieManager = cm;
}
public void setCallback(AsyncTaskCallback cb) {
this.mCallback = (AsyncTaskCallback) cb;
}
#Override
protected String doInBackground(String... strings) {
try{
//all code here is executed without error
//code skipped for simplicity
// basically just loads the given url and then...
publishProgress("Page Loaded");
mCallback.onCookie(mCookieManager);
mCallback.onResponse(response);
return response;
} catch (Exception e) {
publishProgress("Error");
e.printStackTrace();
mCallback.onError(e);
return "";
}
#Override
protected void onProgressUpdate(String... values) {
Log.d(tag, TextUtils.join(",", values));
mCallback.onProgress(values);
super.onProgressUpdate(values);
}
}
The errors I get are marked
ERROR HERE
in the code above. and the message is as follows
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
My main suspect is the PostProc() method in the activity that does play with the UI component. But doesnt implementing the interface in the activity mean that part of the code runs in the main UI thread? I know you can pass the activity instance to the asyncTask and call the PostProc() method from postexecute of the asyncTask but I would like to explore an alternative way to it, since the same asynctask is accessed by other activities as well. the returns are the same for all activities. Only the PostProc Method is different.
Thanks in advance
Instead of using callback interface you can use a android.os.Handler() which is associated with the Looper.getMainLooper() to update the views like:-
AsyncTask1 task1 = new AsyncTask1(android.os.Handler(Looper.getMainLooper(), msg -> {
// handle message sent from asyncTask
return false;
}), url);
Then in task1 send the message using using the handler
public AsyncTask1 (Handler handler, String page) {
this.address = page;
this.handler = handler;
}
then ,
Message message = new Message();
message.obj = uiData;
handler.sendMessage(message);
The handler onHandleMessage will always execute on the main looper !

Null textview when it's call on a handler

I'm trying to do a setText() on a Textview (already instantiate in the onCreate()) called by a Handler and using the ruiOnUiTread() but I have a nullPointerException on the Textview.
Where can the problem come from?
I saw in the debug that the instance of the activity was not the same between the instantiation and the setText() while I do not change activity but impossible to instantiate it in the same place as the setText().
private TextView ambianceTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ambianceTextView = findViewById(R.id.valeur_ambiance);
StarterKillerPidroid.getInstance().startApp();
}
private final Runnable retrieveData = new Runnable() {
public void run() {
try {
setText();
} catch (Exception e) {
e.printStackTrace();
}
handlerRecup.postDelayed(retrieveData, 1000);
}
};
public void setText(){
runOnUiThread(new Runnable() {
#Override
public void run() {
ambianceTextView.setText("test");
}
});
}
public void doAfterLogin() {
handlerRecup.postDelayed(retrieveData, 10000);
}
the runnable is started by a function called by a callback of an http request with Volley
public class StarterKillerPidroid {
void startApp() {
//Sending a request
PostmanPidroid.getInstance().login();
}
public void ackLogin(Boolean isValid) {
if (isValid) {
ActivityMain.getInstance().doAfterLogin();
} else {
PostmanPidroid.getInstance().login();
}
}
}
The class Postman :
public class Postman {
public void login(){
// Parameters
String email = "test#tes";
String password = "test";
// Encoding the request with parameters
JsonObjectRequest request = EncoderDecoderPidroid.getInstance()
.encodeRequestLogin(email, password);
// Sending the request
sendRequest(request);
}
void sendRequest(StringRequest message){
// Creating the queu if it's not create
if (queue == null) {
queue = Volley.newRequestQueue(context);
}
// Adding the request to the queue
queue.add(message);
}
}
When a success response is received, this callback is called :
private Response.Listener<JSONObject> callbackLogin =
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
...
StarterKillerPidroid.getInstance().ackLogin(true);
}
};
Basically, this kind of problem is due to the instance. It may be possible that your textview instance is not initialized. One more thing using handler directly for updating UI thread is not a good idea. Instead of directly updating Ui with handler you should use FunctionalInterface for doing this.
FunctionalInterface is a good approach for such cases.
A functional interface is an interface that contains only one abstract method. They can have only one functionality to exhibit. From Java 8 onwards, lambda expressions can be used to represent the instance of a functional interface. ... Runnable, ActionListener, Comparable are some of the examples of functional interfaces.
Java has a predefined FunctionalInterface Callable. It goes something like this
public static void doDid(final Callable<Void> callable) {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
try {
callable.call();
handler.postDelayed(this, every * repeattime);
} catch (Exception e) {
e.printStackTrace();
}
}
}, every * tempvalue);
}
and use this for updating UI in this way
doDid(new Callable<Void>() {
#Override
public Void call() {
textView.setText("Your text");
return null;
}
});
There is one open-source library available for Android which works like a charm is such cases called Predictor. You can download it from here and import in your project. You can also contribute in this project for saving many developers life.
Do you wanna see how predictor can do this?
Predictor.every(3).second().doDid(new Job<Void>() {
#Override
public Void run() {
textView.setText("Your text");
return null;
}
});
What can you do with predictor?
Predictor gives you several ways of handling multithreading some of them are as follows:
Predictor.every(3).second().doDid(something());
Predictor.every(5).minutes().doDid(something());
Predictor.every().hour().doDid(something());
Predictor.every().week().doDid(something());
Predictor.every().month().doDid(something());
and many more...
Try this way:
private final Runnable retrieveData = new Runnable() {
public void run() {
try {
ambianceTextView = (TextView) findViewById(R.id.valeur_ambiance);
setText();
} catch (Exception e) {
e.printStackTrace();
}
handlerRecup.postDelayed(retrieveData, 1000);
}
};

How to return string in java methods?

I have a problem where i am unable to return a string from this method. I was unsuccessful when I tried creating a new variable outside the Response.Listener. This is probably very simple but how do I go about returning a string from this method. The string I want to return is the 'featured_img_url' string.
public String secondServiceCall(String featuredmedia){
JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.GET,
"http://www.gadgetsinnepal.com/wp-json/wp/v2/media/"+featuredmedia, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject nested_response) {
try {
JSONObject guilld = nested_response.getJSONObject("guid");
String featured_img_url = guilld.getString("rendered");
Toast.makeText(getApplicationContext(),"IMAGE :" + featured_img_url,Toast.LENGTH_LONG).show();
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(),
"Error: " + e.getMessage(),
Toast.LENGTH_LONG).show();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(getApplicationContext(),
"ERROR "+error.getMessage(), Toast.LENGTH_LONG).show();
}
});
MySingleton.getInstance(getApplicationContext()).addToRequestQueue(jsonObjReq);
return featured_img_url;
}
update your code to:
String featured_img_url = null;
public String secondServiceCall(String featuredmedia){
JsonObjectRequest jsonObjReq = new JsonObjectRequest(Request.Method.GET,
"http://www.gadgetsinnepal.com/wp-json/wp/v2/media/"+featuredmedia, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject nested_response) {
try {
JSONObject guilld = nested_response.getJSONObject("guid");
featured_img_url = guilld.getString("rendered");
Toast.makeText(getApplicationContext(),"IMAGE :" + featured_img_url,Toast.LENGTH_LONG).show();
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(),
"Error: " + e.getMessage(),
Toast.LENGTH_LONG).show();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(getApplicationContext(),
"ERROR "+error.getMessage(), Toast.LENGTH_LONG).show();
}
});
MySingleton.getInstance(getApplicationContext()).addToRequestQueue(jsonObjReq);
return featured_img_url;
}
Here, you should simply pass the instance that call this methods to execute the methods from the response.
So change the methods to :
public void secondServiceCall(String featuredmedia, final MyClass caller){
Note that this will return nothing. And the caller instance need to be final to be used in the inner class JsonObjectRequest.
and in the response, you need to pass the value to the instance of MyClass. So add a method in MyClass
public void setFeatureImgUrl(String featuredImgUrl){ ... }
and you just need to call this in the response.
public void onResponse(JSONObject nested_response) {
...
caller.setFeatureImgUrl(feature_img_url);
...
}
Note : This could be done with an Observer pattern but I know that some people doesn't like it. I could add an example of it if needed.

Volley:Cannot refer to the non-final local variable in android

I have a following method, which submits http request using Volley:
private boolean SaveInformationToServer(Information information)
{
RequestQueue queue = Volley.newRequestQueue(this);
StringRequest stringRequest = new StringRequest(Request.Method.POST, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try{
return true;
}
catch (Exception ex){
return false;
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
return false;
}
});
queue.add(stringRequest);
}
I require this method to return either true or false but currently it's giving error void method cannot return a value. I tried a variable, then I got cannot refer to the non-final local variable inside an inner class.
How can I make this method return a boolean value to its caller?
The return type of onResponse is void plus it's a function of an anonymous class so you can't return value from anonymous class to your caller of volley request.
You can create your function can call them on volley response and put your code to execute the result instead of keeping a flag, this will keep your code more structural and readable(you can also set you flags in your onSuccess).
void onSuccess(String response){
// do what you want to do
}
void onError(VolleyError error){
// do what you want to do
}
private void SaveInformationToServer(Information information)
{
RequestQueue queue = Volley.newRequestQueue(this);
StringRequest stringRequest = new StringRequest(Request.Method.POST, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
onSuccess(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
onError(error);
}
});
queue.add(stringRequest);
}

Sending POST Request and Receive Json Responds using Retrofit Unable to use Responds data I need to pass it to another activity

I am using Retrofit for sending POST request.Server return's a JSON Response and i am able to parse the response in the callback method. I need to pass the data from server to another activity. But i can't use the response data outside.
api.LoginUser(
Email.getText().toString(), // passing value to interface of retrofit
Password.getText().toString(),
new Callback<Response>() {
#Override
public void success(Response result, Response response) {
BufferedReader reader = null;
String output = "";
try {
reader = new BufferedReader(new InputStreamReader(result.getBody().in()));
output = reader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
//Json PArsing
try {
JSONObject mainObject = new JSONObject(output);
JSONObject dataObj = mainObject.getJSONObject("data");
String id = dataObj.getString("id");
String name = dataObj.getString("name");
n=name;
Log.d("jsontext", n); //This works
}
catch(JSONException e)
{
e.printStackTrace();
}
Toast.makeText(MainActivity.this, output, Toast.LENGTH_LONG).show();
}
#Override
public void failure(RetrofitError error) {
//If any error occured displaying the error as toast
Toast.makeText(MainActivity.this, error.toString(), Toast.LENGTH_LONG).show();
}
}
);
I can't use this when it executes App crashes.its ok now there is no value in the variable.how to get values out of the callback Responds OnSuccess method???
Log.d("outer",n);
Intent dash = new Intent(this,Dashboard.class);
dash.putExtra("Value",fi);
startActivity(dash);
}
You can create an object and implement Serializable:
class User implements Serializable {
...
}
Then put object User to bundle, add to intent:
Bundle bundle = new Bundle();
bundle.putSerializable("data", user);
Intent intent = new Intent(this, YourClass.class);
intent.putExtras(bundle);
startActivity(intent);
Hope it help you.
Hold all data in a string and using intent app another activity and parse it;
You can do it as follows
public void onSuccess(int statusCode, Header[] headers,
byte[] response) {
try {
BufferedReader br = new BufferedReader(
new InputStreamReader(new ByteArrayInputStream(
response)));
String st = "";
String st1 = "";
while ((st = br.readLine()) != null) {
st1 = st1 + st;
}
showStoreData(st1);
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onFailure(int statusCode, Header[] headers,
byte[] errorResponse, Throwable e) {
// called when response HTTP status is "4XX" (eg. 401, 403, 404)
Log.e("FAIL", "FAIl" + statusCode);
}
#Override
public void onRetry(int retryNo) {
// called when request is retried
}
});
after that
public void showStoreData(String st) {
Intent intent = new Intent(this, YourClass.class);
intent.putExtras(st);
startActivity(intent);
}
You should use an interface that you initialize from the calling method and pass as a parameter into you request class, that way you can call the requests from anywhere and get the callback response back to where you called it from, an example would be:
A general interface, separated in another file:
public interface SomeCustomListener<T>
{
public void getResult(T object);
}
In the class holding your call (complete the stuff you need):
public void someRequestReturningString(Object param1, final SomeCustomListener<String> listener)
{
//here you initialize what you need... it's your stuff
response.enqueue(new Callback<ResponseBody>()
{
#Override
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> rawResponse)
{
try
{
String response = rawResponse.body().string();
// do what you want with it and based on that...
//return it to who called this method
listener.getResult("someResultString");
}
catch (Exception e)
{
e.printStackTrace();
listener.getResult("Error1...");
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable throwable)
{
try
{
// do something else in case of an error
listener.getResult("Error2...");
}
catch (Exception e)
{
throwable.printStackTrace();
listener.getResult("Error3...");
}
}
});
}
Then from where you're calling the request (could be anywhere, Fragments, onClicks, etc):
public class BlaBla
{
//.....
public void someMethod()
{
NetworkManager.getInstance().someRequestReturningString(someObject, new SomeCustomListener<String>()
{
#Override
public void getResult(String result)
{
if (!result.isEmpty())
{
//do what you need with the result...
}
}
});
}
}
If you need more context, you can refer to this SO thread.
Hope this helps!

Categories

Resources