How to simplify Retrofit by ommiting the mediator class - java

I want to retrieve a Json from server and pass it to WebView and consume it in a JavaScript function. So I think I dont need a Java class to hold the data and consume it in Java environment. Actually I want to omit the mediator calss (Users) I used in Retrofit. It was complicated for me to understand the structrure of retrofit request so I have no idea how to consume the response directly without a mediator class. This is my current code:
in Main Activity:
public void getUserList(){
GetUsers users = RetrofitInstance.getRetrofitInstance().create(GetUsers.class);
Call<Users> call = users.getUserList("myUsername");
call.enqueue(new Callback<Users>() {
#Override
public void onResponse(Call<Users> call, Response<Users> response) {
PassUsersToJavaScript(response.body().getList());
}
#Override
public void onFailure(Call<Users> call, Throwable t) {
Toast.makeText(MainActivity.this, "Something went wrong...Please try later!", Toast.LENGTH_SHORT).show();
}
});
}
public void PassUsersToJavaScript(String users){
//Here I pass users to WebView
}
This is the mediator Users class that I want to omit:
public class Users {
#SerializedName("users")
private String users;
public Users(String users) {
this.users = users;
}
public String getList(){
return users;
}
}
and this is the mediator Interface GetUsers :
public interface GetUsers {
#GET("list/")
Call<Users> getUserList(#Query("user") String user);
}
How can I omit the class Users.java and use the response.body() driectly in the line below:
public void onResponse(Call<Users> call, Response<Users> response) {
PassUsersToJavaScript(response.body().getList());
}

I found that I can assign retrofit to a class, object, page and even a string. The model class users in the question above can be replaced with anything else like this JsonObject directly:
public void getUserList(){
GetUsers users = RetrofitInstance.getRetrofitInstance().create(GetUsers.class);
Call<JsonObject> call = users.getUserList("myUsername");
call.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
PassUsersToJavaScript(response.body().getList());
}
#Override
public void onFailure(Call<JsonObject> call, Throwable t) {
Toast.makeText(MainActivity.this, "Something went wrong...Please try later!", Toast.LENGTH_SHORT).show();
}
});
}

Related

Android - Keep Listener with Firebase function out of the class

I would like to code my app in MVC. The problem is that I'm new in Android and I don't know how to have a listener/callback if the function is out of the main Class.
public void addNewUser(String firstname, String lastname, String email, Integer gender, String uid, String profileImageUrl){
Map<String, Object> data = new HashMap<>();
data.put("firstname", firstname);
data.put("lastname", lastname);
data.put("email", email);
data.put("gender", gender);
data.put("boxId", "independent");
data.put("notificationsEnabled", true);
data.put("profileImageUrl", profileImageUrl);
mFirebaseFirestore.collection("usersP").add(data)
.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
#Override
public void onSuccess(DocumentReference documentReference) {
mProgressBar.setVisibility(View.GONE);
mIRegisterActivity.inflateFragment("Register Box", mHashMap);
Log.d(TAG, "DocumentSnapshot written with ID: " + documentReference.getId());
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "Error adding document", e);
}
});
}
I would like to have this function in a different Java Class. But if I do that I don't know how to still be able to launch an action only when the function is completed execution -> in other words, when it's addOnSuccessListener.
Do you know how I could do that?
I'm used to coding in swift, it would be something like that:
func addUser(id: String, completion: #escaping (User) -> Void) {
// Code and then
completion(user)
}
You should create your own custom Listener for that say MyFirebaseListener and update your things in activity by implementing this interface
public interface MyFirebaseListener {
void onSuccess(DocumentReference documentReference)
void onFailure(Exception e)
}
Now pass the Activity as parameter to MyFirebaseListener to addNewUser() method as below
public class UserApi{
public void addNewUser(String firstname,
String lastname,
String email,
Integer gender,
String uid,
String profileImageUrl,
MyFirebaseListener myFirebaseListener){
Map<String, Object> data = new HashMap<>();
data.put("firstname", firstname);
data.put("lastname", lastname);
data.put("email", email);
data.put("gender", gender);
data.put("boxId", "independent");
data.put("notificationsEnabled", true);
data.put("profileImageUrl", profileImageUrl);
mFirebaseFirestore.collection("usersP").add(data)
.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
#Override
public void onSuccess(DocumentReference documentReference) {
myFirebaseListener.onSuccess(documentReference)
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
myFirebaseListener.onFailure(e)
}
});
}
}
Implement MyFirebaseListener interface in your Activity so you need to override following methods and perform your UI modification in those implemented methods as follows
public class MyActivity extends AppCompatActivity implements MyFirebaseListener {
void someMethod(){
addNewUser(firstname,
lastname,
email,
gender,
uid,
profileImageUrl,
this) // <- This will be reference to Activity with Type of MyFirebaseListener
}
void onSuccess(DocumentReference documentReference){
mProgressBar.setVisibility(View.GONE);
mIRegisterActivity.inflateFragment("Register Box", mHashMap);
Log.d(TAG, "DocumentSnapshot written with ID: " + documentReference.getId());
}
void onFailure(Exception e){
Log.d(TAG, "Error adding document", e);
}
}
This is how you can separate UI Logic and service logic using Custom Interfaces
Refer this link for custom Listener:
https://guides.codepath.com/android/Creating-Custom-Listeners
If you want keep listener always call,
For firebase listener, Use application class for listener.
If you want method global(where you need response of Firebase), Put that function Either in Application class, or Use BroadcastReceiver or Listener or EventBus.
Comment here if you have any query.
First add listenerInterface
public interface FirebaseResponse {
void onSuccess();
void onFailure();
}
Create separate class like below
public class ResponseHandler implements
OnSuccessListener<DocumentReference>() , OnFailureListener() {
private FirebaseListener listener;
public ResponseHandler(FirebaseListener listener){
this.listener = listener;
}
#Override
public void onSuccess(DocumentReference documentReference) {
//Todo handle onSuccess
listener.onSuccess();
}
#Override
public void onFailure(#NonNull Exception e) {
//Todo handle onFailure
listener.onFailure();
}
}
Then add this class as response handler in your class that implements FirebaseListener
ResponseHandler responseHandler = new ResponseHandler(this);
mFirestore.collection("usersP").add(data)
.addOnSuccessListener(responseHandler).
.addOnFailureListener(responseHandler)
There are two different approaches which you can use...
First:
U can make a callback interface just like we do in case of RecyclerView click callbacks...
Second:
U can use livedata observers if this u must have little bit idea about rxJava2, arch life or Agera....
So lets consider the first approch
Consider ur class is
class otherClass{
callbackInterface mCallBackInterface;
public(callbackInterface mCallBackInterface){
this.mCallBackInterface=mCallBackInterface;
}
interface callbackInterface{
void onSucuss(DocumentReference documentReference);
}
public void addNewUser(String firstname, String lastname, String email, Integer gender, String uid, String profileImageUrl){
Map<String, Object> data = new HashMap<>();
data.put("firstname", firstname);
data.put("lastname", lastname);
data.put("email", email);
data.put("gender", gender);
data.put("boxId", "independent");
data.put("notificationsEnabled", true);
data.put("profileImageUrl", profileImageUrl);
mFirebaseFirestore.collection("usersP").add(data)
.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
#Override
public void onSuccess(DocumentReference documentReference) {
mCallBackInterface.onSucuss(decumentReference);
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "Error adding document", e);
}
});
}
}
///The Class which will be calling it will be something like this
class CallingClass implements CallBackInterface{
#Override
void CallBackINterface(DocumentReference documentReference){
//Your code goes here
}
}
This will do the job broadcast receivers can also be used but above solution is best for beginners...
just add an interface ...
public interface IFirebaseTask {
void OnSuccess(DocumentReference reference);
void OnFailure(Exception e);
}
and then implement that interface, where it may be required to listen for:
public SomeActivity extends FragmentActivity implements IFirebaseTask {
}
one can also pass the listener into constructors:
public SomeFirebaseTask(Context context, IFirebaseTask listener){
this.setContext(context);
this.listener = listener;
}
... into order to notify the listener defined above from elsewhere.
alternatively, one can let just any class implement the interface methods:
... implements OnSuccessListener<DocumentReference>, OnFailureListener
and then bind the listener alike:
mFirebaseFirestore
.collection("usersP")
.add(data)
.addOnSuccessListener(SomeActivity.this)
.addOnFailureListener(SomeActivity.this);
the Firebase documentation (in case someone would bother to read) has this answered, as well...
see section Detaching Callbacks.

Best practice in java to create my own interface instead of existing one in retrofit for example

I am building the network structure in my android application i one of the things i realized is that i want to do is to create my own interface in my requests class .
i have this method for example :
public static void getUserData(String owner, final DataManager.OnDataReceived<Owner> listener) {
Call<Owner> call = getGitService().getUser(owner);
call.enqueue(new Callback<Owner>() {
#Override
public void onResponse(Call<Owner> call, Response<Owner> response) {
}
#Override
public void onFailure(Call<Owner> call, Throwable t) {
}
});
}
now...all my methods respond same way to the call back so why not handle this one time ?
so the way i did it it to create class that implement retrofit Callback interface :
private static class callbackHandler implements Callback {
final DataManager.OnDataReceived listener;
callbackHandler(DataManager.OnDataReceived listener) {
this.listener = listener;
}
#Override
public void onResponse(Call call, Response response) {
listener.onDataReceived(response.body(), getErrorFromResponse(response));
}
#Override
public void onFailure(Call call, Throwable t) {
listener.onDataReceived(null, t.toString());
}
}
so now all the request look like this :
public static void getUserData(String owner, final DataManager.OnDataReceived<Owner> listener) {
Call<Owner> call = getGitService().getUser(owner);
call.enqueue(new callbackHandler(listener));
}
much clearer ...
1 . what do you think about this solution ? i had better way to handle all Callback same way ?
2 . the compiler shout at me that
call.enqueue(new callbackHandler(listener));
and
#Override
public void onResponse(Call call, Response response) {
listener.onDataReceived(response.body(), getErrorFromResponse(response));
}
#Override
public void onFailure(Call call, Throwable t) {
listener.onDataReceived(null, t.toString());
}
and my interface :
public interface OnDataReceived<T> {
void onDataReceived(T data, String error);
}
is unchecked assignment...i understand what that means but not sure how to fix this ?
UPDATE : solution for the unchecked
private static class callbackHandler<T> implements Callback<T> {
final DataManager.OnDataReceived<T> listener;
callbackHandler(DataManager.OnDataReceived<T> listener) {
this.listener = listener;
}
#Override
public void onResponse(Call<T> call, Response<T> response) {
listener.onDataReceived(response.body(), getErrorFromResponse(response));
}
#Override
public void onFailure(Call<T> call, Throwable t) {
listener.onDataReceived(null, t.toString());
}
}
what do you think about this solution ?
This is called the adapter pattern. You are adapting one interface (Callback<Owner>) to another (DataManager.OnDataReceived<Owner>).
If your objective is to be able to replace Retrofit with something else in the future, this sort of adapter is perfectly reasonable. Otherwise, you might consider just having DataManager.OnDataReceived extend Callback and change your method names to match (e.g., onDataReceived() turns into onResponse()), to avoid the need for this adapter.
i understand what that means but not sure how to fix this
callbackHandler implements Callback wipes out the Java generics.
If this is only for use with Owner, use callbackHandler implements Callback<Owner> and have it hold a DataManager.OnDataReceived<Owner>. If you plan to use this for multiple model objects (assuming that Owner is a model), use callbackHandler implements Callback<T> and have it hold a DataManager.OnDataReceived<T>.

Building a library that uses Retrofit internally, wrapping responses

I'm trying to build a library that basically wraps our api. Basically, the structure im going for is something like this:
MySDK mySDK = new MySDK("username", "password");
mySDK.getPlaylistInfo("3423", 2323, new CustomCallback<>(){
//on response
//on failure
});
So with vanilla Retrofit, an api call usually looks something like the following:
ApiService api = retrofit.create(ApiService.class);
Call<Response> call = api.getPlaylistInfo()
call.enqueue(new Callback<Response>() {
#Override
public void onResponse(Call<Response> call, Response<Response> response) {
//handle response
}
#Override
public void onFailure(Call<Response> call, Throwable t) {
//handle failure
}
});
Basically, how would I wrap retrofits callback system into my own? Note, the reason for needing to do this is to preprocess the data returned from the api before delivering the final response.
I've written something similar so it might help you getting started, this follows an implementation I'v written for Volley, and re-used when I migrated to Retrofit2 so it resembles it (this SO question).
Create a global object (what you would refer to as MySDK) as a singelton class that handles your requests:
create a singleton class, which you instatiate when you're application comes up:
public class NetworkManager
{
private static final String TAG = "NetworkManager";
private static NetworkManager instance = null;
private static final String prefixURL = "http://some/url/prefix/";
//for Retrofit API
private Retrofit retrofit;
private ServicesApi serviceCaller;
private NetworkManager(Context context)
{
retrofit = new Retrofit.Builder().baseUrl(prefixURL).build();
serviceCaller = retrofit.create(ServicesApi.class);
//other stuf if you need
}
public static synchronized NetworkManager getInstance(Context context)
{
if (null == instance)
instance = new NetworkManager(context);
return instance;
}
//this is so you don't need to pass context each time
public static synchronized NetworkManager getInstance()
{
if (null == instance)
{
throw new IllegalStateException(NetworkManager.class.getSimpleName() +
" is not initialized, call getInstance(...) first");
}
return instance;
}
public void somePostRequestReturningString(Object param1, final SomeCustomListener<String> listener)
{
String url = prefixURL + "this/request/suffix";
Map<String, Object> jsonParams = new HashMap<>();
jsonParams.put("param1", param1);
Call<ResponseBody> response;
RequestBody body;
body = RequestBody.create(okhttp3.MediaType.parse(JSON_UTF), (new JSONObject(jsonParams)).toString());
response = serviceCaller.thePostMethodYouWant("someUrlSufix", body);
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...");
}
}
});
}
public void someGetRequestReturningString(Object param1, final SomeCustomListener<String> listener)
{
// you need it all to be strings, lets say id is an int and name is a string
Call<ResponseBody> response = serviceCaller.theGetMethodYouWant
(String.valueOf(param1.getUserId()), param1.getUserName());
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...");
}
}
});
}
}
This works with your interface (example with POST and GET request, GET could be without params):
public interface BelongServicesApi
{
#POST("rest/of/suffix/{lastpart}") // with dynamic suffix example
Call<ResponseBody> thePostMethodYouWant(#Path("lastpart") String suffix, #Body RequestBody params);
#GET("rest/of/suffix") // with a fixed suffix example
Call<ResponseBody> theGetMethodYouWant(#Query("userid") String userid, #Query("username") String username);
}
when your application comes up:
public class MyApplication extends Application
{
//...
#Override
public void onCreate()
{
super.onCreate();
NetworkManager.getInstance(this);
}
//...
}
a simple listener interface for your callback (seperate file would do good):
public interface SomeCustomListener<T>
{
public void getResult(T object);
}
and finally, from wherever you want, the context is already in there, just call:
public class BlaBla
{
//.....
public void someMethod()
{
//use the POST or GET
NetworkManager.getInstance().somePostRequestReturningString(someObject, new SomeCustomListener<String>()
{
#Override
public void getResult(String result)
{
if (!result.isEmpty())
{
//do what you need with the result...
}
}
});
}
}
you can use any object with the listener, just parse the response string to a corresponding object, depending on what you need to receive and you can call that from everywhere (onClicks, etc.), just remember the objects need to match between methods.
Hope this Helps!

How do I pass an implemented Interface object to a method in the class its implemented in?

I am working with a "Log In" activity:
LogInActivty extends AppCompatActivity
implements LoaderCallbacks<Cursor>, Firebase.AuthResultHandler, Firebase.AuthStateListener
Now I want to call Firebase.authWithPassword(String, String, Firebase.AuthResultHandler); from within an AsynTask<Void, Void, Boolean>.doInBackground(Void...params).
How exactly can I pass a Firebase.AuthResultHandler to Firebase.authWithPassword(x,y,a); without needing to create an auth handler? Is it possible? Can reference the auth handler value?
P.S. I am new to Firebase and trying to master extending and implementing on classes in java.
I struggled with this too. The way I did it was like you were thinking; I created a AuthHandler interface:
Note: I created a User object that I pass around
public class FirebaseServerAuthenticate implements ServerAuthenticate, Firebase.AuthResultHandler {
public static final String TAG = FirebaseServerAuthenticate.class.getSimpleName();
private ServerAuthenticateCallback callback;
private User user;
#Override
public void userSignIn(User user, ServerAuthenticateCallback callback) {
Log.d(TAG, "Firebase authentication starting: " + user);
this.user = user;
this.callback = callback;
// I check for id and token here. you may or may not need this.
if(isEmpty(user.getAuthToken()) || isEmpty(user.getUid()))
callback.onServerAuthenticateFail("User provided no auth token");
else
FireHelper.getRoot().authWithOAuthToken(AUTH_TOKEN_TYPE_GOOGLE, user.getAuthToken(), this);
}
#Override
public void onAuthenticated(AuthData authData) {
Log.d(TAG, "Firebase authentication: successful");
callback.onServerAuthenticated(user);
}
#Override
public void onAuthenticationError(FirebaseError firebaseError) {
Log.e(TAG, "Firebase authentication: failed");
callback.onServerAuthenticateFail(firebaseError.getMessage());
}
}
And then you need the ServerAuthenticate interface:
public interface ServerAuthenticate {
interface ServerAuthenticateCallback {
void onServerAuthenticated(User user);
void onServerAuthenticateFail(String error);
}
void userSignIn(final User user, ServerAuthenticateCallback callback);
}
To authenticate, use FirebaseServerAuthenticate.userSignIn in your activity:
FirebaseServerAuthenticate serverAuthenticate = new FirebaseServerAuthenticate();
public void authenticateUser(User user){
new AsyncTask<User, Void, Void>() {
#Override
protected Void doInBackground(User... params) {
User user = params[0];
// I did some more stuff here. Got my auth token from google etc.
serverAuthenticate.userSignIn(user, new ServerAuthenticate.ServerAuthenticateCallback() {
#Override
public void onServerAuthenticated(User user) {
// Here you are authenticated!
// I kill this activity and open in to my mainActivity
finishLogin(user);
}
#Override
public void onServerAuthenticateFail(String error) {
// Handle the error. For debug, I just show an alert dialog
onError(error);
}
});
return null;
}
}.execute(user);
}

Receiving custom parameter in retrofit callback

This is my post :
#POST("/path")
#FormUrlEncoded
void postIt(#Field("id") String id , Callback<Response> response);
and this is the Callback:
private Callback<Response> responseCallBack = new Callback<Response>() {
#Override
public void success(Response response, Response response2) {
// get the id
}
#Override
public void failure(RetrofitError error) {
// do some thing
}
};
Question:
in the callback i want to receive the id which posted in #POST, how should i do that?
and i can't change the server API
to do this we need an abstract class
abstract class CallBackWithArgument<T> implements Callback<T> {
String arg;
CallBackWithArgument(String arg) {
this.arg = arg;
}
CallBackWithArgument() {
}
and make an instance
new CallBackWithArgument<Response>(id) {
#Override
public void success(Response response, Response response2) {
//do something
}
#Override
public void failure(RetrofitError error) {
//do something
}
}
It's easy. You can simply make Callback to hold requested id and create new callback every time
class MyCallback extends Callback<Response> {
private final String id;
MyCallback(String id) {
this.id = id
}
#Override
public void success(Response response, Response response2) {
// get the id
}
#Override
public void failure(RetrofitError error) {
// do some thing
}
}
So when you call service
myService.postIt("777", new MyCallback("777"))

Categories