NullPointerException using Retrofit library - Android - java

(Scroll at the end of question to see the final solution)
Playing around with the Retrofit Android library. I am trying to push a POST request into a web server that is supposed to return 3 fields after a successful call to a "/login" method through POST. Information:
End point: http://example.us.es:3456
Server-side method to perform the login: /login
Required parameters to the server-side method: "user" and "password"
HTTP method allowed by server administrator: POST
No matter what values for "user" and "password" the app client enters, the web server should send a single JSONObject containing three fields:
ok: string with values "true" or "false" (false would mean that the credentials were not valid).
msg: would carry a string message in case there was an error (for example, invalid credentials or database error)
data: another JSONObject that in this method contains one single name/value pair, the id_session (string containing the session identifier). Other methods contain several name/value pairs; this is not the case for the login method.
Basing myself in this information, the first thing I did is create a POJO Java method that looks like this:
POJO Method (LoginInfo_POJO.java)
package com.example.joselopez.prueba1;
import java.util.List;
public class LoginInfo_POJO {
public String _ok;
public String _msg;
public Dataset _dataset;
class Dataset {
String _idsession;
}
}
The next thing I did is create an interface containing the login method (I'd add other methods here after I can successfully log in):
API METHODS IN INTERFACE (IApiMethods.java)
package com.example.joselopez.prueba1;
import retrofit.http.POST;
import retrofit.http.Query;
public interface IApiMethods {
// Log-in method
#FormUrlEncoded
#POST("/login")
LoginInfo_POJO logIn(#Query("user") String user,
#Query("password") String password);
}
Almost there. Now I create a class that extends from AsyncTask that will perform the networking operations in a separate thread. This class is inside the "MainActivity.java" file.
Main Activity (MainActivity.java)
...
...
private class BackgroundTask_LogIn extends AsyncTask<Void, Void, LoginInfo_POJO> {
RestAdapter restAdapter;
#Override
protected LoginInfo_POJO doInBackground(Void... params) {
IApiMethods methods = restAdapter.create(IApiMethods.class);
LoginInfo_POJO loginInfo = methods.logIn(mUser, mPassword);
return loginInfo;
}
#Override
protected void onPreExecute() {
restAdapter = new RestAdapter.Builder()
.setEndpoint("http://example.us.es:3456")
.build();
}
#Override
protected void onPostExecute(LoginInfo_POJO loginInfo_pojo) {
tv.setText("onPostExecute()");
tv.setText(loginInfo_pojo._ok + "\n\n");
tv.setText(tv.getText() + loginInfo_pojo._msg + "\n\n");
tv.setText(tv.getText() + loginInfo_pojo.data.id_sesion);
}
}
}
The MainActivity layout contains a single TextView (inside a RelativeLayout) whose id is "textView", and is instantiated in code as "tv" as you will see next. The complete code for MainActivity is:
Main Activity (MainActivity.java)
package com.example.joselopez.prueba1;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;
import retrofit.RestAdapter;
public class MainActivity extends AppCompatActivity {
TextView tv;
String mUser, mPassword;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.textView);
mUser = "test_user";
mPassword = "test_password";
BackgroundTask_LogIn tryLogin = new BackgroundTask_LogIn();
tryLogin.execute();
}
private class BackgroundTask_LogIn extends AsyncTask<Void, Void, LoginInfo_POJO> { ... }
Everything should be working. But it is not, and after a bit of debugging I found that the onPostExecute() method inside class BackgroundTask_LogIn stops in line:
for (LoginInfo_POJO.Dataset dataset : loginInfo_pojo._dataset) {
The error thrown is:
com.example.joselopez.prueba1 E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.NullPointerException
So I set a breakpoint at this line and guess what? My LoginInfo_POJO instance is holding these values for its internal variables:
_ok = null
_msg = null
_dataset = null
This means my variables aren't being populated from the server response, BUT the connection seems to be successful as the doInBackground method runs entirely and onPostExecute is being called.
So what do you think? Maybe I am not carrying out the POST request the right way?
UPDATE
As #Gaëtan said, I made a huge error in my POJO class; local variable names there MUST be EQUAL to those in the resulting JSON. I said that I was expecting fields "ok", "msg", "data" and "id_session" from the JSON, but the local variables inside my LoginInfo_POJO have names "_ok", "_msg", "_dataset", "_idsession" (notice the leading underscores). This is a huge error from a Retrofit perspective, so rewriting the POJO method accounting for this will eventually solve the problem.

A couple of information about how to use Retrofit:
The name of the fields in your POJO must match the first in the JSON response. Here, your fields are named _ok, _msg and _dataset while the JSON response contains ok, msg and data. You have two options here: either rename the fields to match the JSON response, or use the #SerializedName annotation on each field to give the name of the JSON field.
public class LoginInfo_POJO {
// If you rename the fields
public String ok;
public String msg;
public List<Dataset> data;
// If you use annotation
#SerializedName("ok")
public String _ok;
#SerializedName("msg")
public String _msg;
#SerializedName("data")
public String _dataset;
}
Retrofit provide a way to not use AsyncTask. Instead of using a return type for your API method (here LoginInfo_POJO logIn(String, String), use a last parameter of type Callback<LoginInfo_POJO>. The request will be executed on a background thread by retrofit, and the callback will be called on the main thread when the request is complete (or failed if something went wrong).
See the documentation for more information, in the SYNCHRONOUS VS. ASYNCHRONOUS VS. OBSERVABLE section.

You could try to set the logLevel option to RestAdapter.LogLevel.FULL when constructing your adapter inside your onPreExecute to get perhaps more information on what's actually going on.
From Square Retrofit API Declaration:
If you need to take a closer look at the requests and responses you can easily add logging levels to the RestAdapter with the LogLevel property.
Example with logLevel set to FULL:
RestAdapter restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint("https://api.github.com")
.build();
That said, it's true that you don't need AsyncTask with Retrofit.

Related

How to get POJO from a volley callback, and use/return it in the calling function?

I'm implementing a simple mobile app with user accounts. Additionally, it must be structured in a layered architecture that cleanly separates presentation, logic and access to the database.
I'm currently able to send and get data from a server, using the volley library. However, this data is only available inside the onResponse method of the Response.Listener<String> passed as a parameter in the constructor of stringRequest object, later used to perform the request. I want to use the data that I get in the response to construct a User object that I could use all over my app, and keep the layered architecture as much as possible.
This is an example of the kind of method I've been aiming for:
public ResponseType insertUser (final Context context, final String id, final String name, final String password) {
//using a wrapper object because have to declare object as final to use
//inside inner class, so use field to assign value
final ResponseWrapper wrapper = new ResponseWrapper();
StringRequest stringRequest = new StringRequest(Request.Method.POST, BuildConfig.ip,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
wrapper.response = response.equals("") ?
ResponseWrapper.SUCCESS :
ResponseWrapper.DB_ERROR;
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
wrapper.response = ResponseWrapper.CONNECTION_ERROR;
}
}){
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String,String> params = new HashMap<String,String>();
params.put("id",id);
params.put("name",name);
params.put("password",password);
return params;
}
};
RequestQueue requestQueue = Volley.newRequestQueue(context);
requestQueue.add(stringRequest);
//waiting for callback to modify field
while(wrapper.response == null);
return wrapper.response;
}
I've tried setting a field of an external object inside onResponse, waiting for the field to be changed to continue execution, to no avail. The code compiles and performs the request, but the field is kept unchanged. My research has suggested to me that this is something to be expected when dealing with asynchronous code.
Most examples I've read limit their scope to using Toast to show the response in the screen. A couple change activities inside the method, but this goes against the layer separation that I'm trying to achieve by performing presentation actions inside the database access layer (and potentially performing business logic too, as my app becomes more complex).
So, how can I get an object from inside the callback? (For example the String containing the response, or an enum indicating the result of an operation).
If this isn't possible or advisable, how could I structure the code to keep the separation of concerns?
My thanks in advance for any suggestion that could steer me in the right direction.

How to save the value of a variable inside a Retrofit call?

I am trying to retrieve the value of a certain field from the header using a Retrofit call to be used to be sent back to server. I am successful in getting the value inside the try block and send it back immediately in the try block too. But when I try the same outside the call instance, the value of abc (which is where I assigned the value of the response header) is lost. I have already declared the String abc as a global variable. How do I save the value of the string?
public class MainActivity extends AppCompatActivity {
private static final String LOG_TAG = "MainActivityClass";
String abc;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
Call<List<TrendingModel>> call = apiService.getAllTodos();
call.enqueue(new Callback<List<TrendingModel>>() {
#Override
public void onResponse(Call<List<TrendingModel>> call, Response<List<TrendingModel>> response) {
try {
List<TrendingModel> todoModels = response.body(); // WHERE WE GET THE RESPONSE BODY
abc = response.headers().get("Tanand"); // WHERE WE GET THE RESPONSE HEADER AND ASSIGN IT TO abc, WHICH WE DECLARED GLOBALLY
ApiClient.getClient(abc).create(ApiInterface.class); // PASSING THE abc VARIABLE TO THE GETCLIENT(TOKEN) METHOD WITHIN
// THE SAME TRY BLOCK WHICH WORKDS
} catch (Exception e) {
Log.d("onResponse", "There is an error");
e.printStackTrace();
}
}
#Override
public void onFailure(Call<List<TrendingModel>> call, Throwable t) {
Log.d("onFailure", t.toString());
}
});
ApiClient.getClient(abc).create(ApiInterface.class); // VALUE OF abc IS NOT PERSISTED HERE (abc IS NULL) ALTHOUGH WE DECLARED IT GLOBALLY
}
}
Try to call method inside OnResponse method .
because onResponse() method runs in background until the data is fetched.
If you want to access the data of response then call your method inside it .
And outside all statements are called before the response data finished which is why it doesn't give you actual data .
As a Good Practice, use the below method.
Just create a method inside the class and call your all statements inside it.
Now call your method inside onResponse method .
You can use a setter method within the onResponse method of Retrofit.
This answer explains how you can go about it https://stackoverflow.com/a/63060520/10123715

Return Multiple Data from AsyncTask

I'm trying to organize my app code separating AsyncTask classes from Activity or Fragments.
Surfing this forum I've learned about the "interface and listener" solution:
Interface.java
public interface TaskCompleteListener<T> {
public void onTaskComplete(T result);
}
AsyncTask.java
[...]
#Override
protected void onPostExecute(String JSONResponse) {
// using Gson library I convert JSONResponse string to POJO objects...
listener.onTaskComplete(result);
}
Activity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// start AsyncTask...
}
// Inner class implementing interface
public class LoadTaskCompleteListener implements TaskCompleteListener<Object> {
#Override
public void onTaskComplete(Object result) {
updateUI(result);
}
}
public void updateUI(Object result) {
// here you can manage UI updating, using result object
}
}
UPDATE
DataHelper.java
public class DataHelper {
private AsyncTaskCompleteListener<Object> listener;
public DataHelper(AsyncTaskCompleteListener<Obejct> listener) {
this.listener = listener;
}
// Multiple AsyncTask are defined here...
}
Well, I like this pattern so much, but: what if inside activity (or fragment)
I have multiple requests, with different type of results (single object, array, list)
and different business logic for managing them?
I can't handle that using a single callback. Any ideas? Should I add multiple
callbacks inside the interface? I'm really stuck with this.
I've a single file called DataHelper with multiple AsyncTask inside (something like web getters).
Naturally I can give up with this approach if you suggest any other alternative.
Why not return an array of Objects as a result? Then if you have more than one object inside that array you'll know the order in which you put them so you can use more than one set of results at a time.
public interface TaskCompleteListener<T> {
public void onTaskComplete(T... results);
}
Note that a 'handler' can be used instead of an async task and that each handler may return its message to the message looper with the msg wrapping different data. Easy to handle many data types because its just a message component.
Sample here
See 'process_entity() ' where the messages containing result component are sent by sample code.
Note that the definition of onTaskComplete() accepts as argument a generic type, type T:
onTaskComplete(T result);
this means that you can pass in any object type, single object, array, list, etc.
Regarding on how to handle different results using a single callback, you can use a Bundle to put the result in, associated with a key, and then in updateUI() check for that key and take appropriate actions.
Something like this (pseudocode, not sure if it will compile):
#Override
protected void onPostExecute(String JSONResponse) {
Bundle bundle = new Bundle();
bundle.putString("key_json_response", result); //put the response in a Bundle
listener.onTaskComplete(bundle);
}
// .....
public void updateUI(Object result) {
Bundle bundle = (Bundle)result;
if(bundle.containsKey("key_json_response")){
String json = bundle.getString("key_json_response");
// process json
} else if(bundle.containsKey("key_another_response")){
// process another response
}
}

Getter and Setter return null in Android

so I've been working on a project that receives data from server, for example sessionKey. I created getter and setter method like this :
public class sEngine
{
private static String sessionKey;
public static String getSessionKey() {
return sessionKey;
}
public static void setSessionKey(
String sessionKey) {
sEngine.sessionKey = sessionKey;
}
}
Then I have activity A. In this activity A, I insert a value into the setter method.
public class A extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState) {
sEngine.setSessionKey("Hello world")
}
}
I also have activity B. In this activity B, I call the getter method
public class B extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState) {
String sessionKey = sEngine.getSessionKey();
}
}
It occurs to me, that Android sometimes wipes all the data in order to free some memory, for example when I let the program idle for too long, or I used Advanced Task Killer. The problem is if those happen, I will get null in activity B when I call the getter method although I've set the value in activity A. Is there any way for me to maintain the value stored via the setter method (other than using SharedPreference) so the value will still be there although Android wipes the memories/data?
Is there any way for me to maintain the value stored via the setter
method (other than using SharedPreference) so the value will still be
there although Android wipes the memories/data?
Not sure why you wouldn't want to use SharedPreferences, despite it being the perfect candidate in your requirement. When somethings as simple as this can store it:
SharedPreferences sharedPrefs = getApplicationContext().getSharedPreferences(SOME_KEY, Context.MODE_PRIVATE);
Editor editor = sharedPrefs.edit();
editor.putString("session_key", sessionKey );
This will ensure your sessionkey always remains stored for easy retrieval. Unless the user clears your app data that is.
Your only alternatives as opposed to SharedPreferences are saving the sessionkey to a Database which in my opinion, considering the task it will perform, is absolutely unnecessary.
You could also consider writing the sessionkey to a text file and then read it to retrive the data.
Both the alternatives to SharedPreferences are truly unfit for the purpose you need it for. And I would really urge you to re-consider using SharedPreferences .
Try this, Declare you non activity class in A Activity. and then set your session value.
sEngine mengine = new sEngine();
mengine.setSessionKey("Hello world");
And also get session value in B activity.
sEngine mengine = new sEngine();
String str = mengine.getSessionKey();
change
sEngine.sessionKey = sessionKey;
in your code to
this.sessionKey = sessionKey;
or simply
sessionKey = sessionKey;
using sEngine. makes one believe that your class is static.Which it isnt!
or if you want to use this sEngine. everywhere in your code you need to declare this class as static.In that case you just need to make the class declaration to static:
public static class sEngine {

Mocking inside a Java class

So I have this GWT code that handles RPC requests maintain states(ready, waiting, error etc).
And I would like to check if the class change its states correctly after each call, set response variables etc.
Now how should I proceed to test that without making actual requests to the server(that could run into errors in the server it self).
I think I could mock the request callback class somehow but it is invisible to the test.
I'm lost, help!
Sample of the code below(I'll post the whole thing later in case anyone wants).
public class RPCHandler
{
public RPCHandler(String method,String[] argumentsName,
String[][] argumentsValues)
{
this.method = method;
this.argumentsName = argumentsName;
this.argumentsValues = argumentsValues;
}
/**
* Method that creates a RPC request using JSON in a POST
*
*/
public void rpcRequest(){
if(currentState == HandlerState.WAITING_RESPONSE)return;
currentState = HandlerState.WAITING_RESPONSE;
// Append watch list stock symbols to query URL.
url = URL.encode(url);
url += "action=";
url += method;
// Send request to server and catch any errors.
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, url);
String requestData = parseToJSON(argumentsName, argumentsValues);
try{
Request request = builder.sendRequest(requestData, new RequestCallback()
{
public void onError(Request request, Throwable exception)
{
setRPCException(new Exception("Error while saving. Action="+method));
setCurrentState(HandlerState.ON_ERROR);
}
//Few other error, response received hander methods after this point.
}
}
It looks like you're trying to mock out the actual transport so you should build a mock of the RequestBuilder class. In JMockit, you could write:
public class MockRequestBuilder
{
public void $init( int method, String url)
{
/* check values and/or store for later */
}
public Request sendRequest( String data, RequestCallback callback )
{
/* check values and/or store for later */
}
}
You'll need to fill in the details of the what you want the mock to do. Also, you can isolate the callback testing if you moved the callback to a named class instance inside of your outer class:
public class MyGWTClass
{
protected static class RpcCallback extends RequestCallback
{
public void onError(...) { ... }
}
}
By moving the callback object into a class and using a factory method, you can create tests that only check the callback.

Categories