Is there a way to use retrofit with POJO's setters? - android

I have a REST API and i use Retrofit library for calling request.
For exmaple i have a some POJO (Dialog.java):
public class Dialog {
#Expose
#SerializedName("dialogId")
private int _id;
#Expose
private String opponentLogin;
public void setOpponentLogin(String opponentLogin) {
// these should be some magic with opponentLogin before it can be saved in memory
this.opponentLogin = opponentLogin;
}
public String getOpponentLogin() {
return opponentLogin;
}
}
And some call method from my ApiManager:
public void getDialogList() {
Call<List<Dialog>> call = service.getDialogList();
call.enqueue(new Callback<List<Dialog>>() {
#Override
public void onResponse(Response<List<Dialog>> response) {
switch (response.code()) {
case 200:
// work with POJO object
break;
}
}
#Override
public void onFailure(Throwable t) {
// TODO no internet connection
}
});
}
The question is: can i call setOpponentLogin method (and some other setter) when retrofit convert JSON answer to POJO?

Yes, you can do this with setter methods. Setter methods is used for uploading some contents to API like some text and images. It depends upon your API whether to use raw JSON body or something else.

Related

Pattern to add listener to method call? Is there an example?

I have been working with a ChannelClient and saw how you could call a method and add a listener to it. It does not exactly seem like the builder pattern but I was wondering to create my own class using the same technique. Is there an example? This seems like a great way to either add a listener or not.
ChannelClient channelClient = Wearable.getChannelClient(getContext());
channelClient.openChannel(node,path).addOnSuccessListener(new OnSuccessListener<ChannelClient.Channel>() {
#Override
public void onSuccess(ChannelClient.Channel channel) {
}
});
So for my class:
public class TokenMessage {
private String name;
private int id;
public TokenMessage(int id, String name) {
this.id=id;
this.name=name;
}
public void generateCode() {
}
}
How do I add addOnSuccessListener to the method call. I assume I create a public interface in the class, but how do I add it to a method call? How do I add it to the generateCode method?
There are also addOnFailureListener and addOnCompleteListener methods which can be appended to the call as well.
ADDITION
I would want to be able to add multiple listeners and have the call to look like this:
TokenMessage tokenMessage = new TokenMessage(42,"Name");
tokenMessage.generate().addOnSuccessListener(new OnSuccessListener<int>() {
#Override
public void onSuccess(int code) {
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NotNull Exception e) {
}
});
You should be able to reverse engineer what your class will look like from the requirements:
Assuming you want something like this:
TokenMessage message = new TokenMessage(42, "Name");
message.addOnSuccessListener(new TokenMessage.OnSuccessListener() {
void onSuccess() {
doSuccessfulThings();
}
}).addSomeOtherListener(...)
Then clearly you need:
A method to set a listener object that returns the same object for chaining.
Listener interfaces that you can set.
Variable to hold the interface instances in your object.
Calls to the callback methods on the interface objects based on logic of generateCode.
Then your code would look something like this:
public class TokenMessage {
interface OnSuccessListener {
void onSuccess();
}
// Other interfaces for failure etc
private String name;
private int id;
private OnSuccessListener onSuccessListener;
// Other member fields
public TokenMessage(int id, String name) {
this.id=id;
this.name=name;
}
public TokenMessage generate() {
launchTaskThatTakesALongTime();
return this
}
// Setter that returns the object for chaining
public TokenMessage addOnSuccessListener(OnSuccessListener listener) {
onSuccessListener = listener;
return this;
}
private void launchTaskThatTakesALongTime() {
// Use your preferred async / threading paradigm to run your task in the background. When they complete, invoke your listeners:
// if (success && successListener != null) {
// successListener.onSuccess()
//}
}
// Other setters for other callbacks
}

Retrofit parse selective JSON data

I am parsing JSON data from this URL. Calling like this:
call.enqueue(new Callback<List<Gift>>() {
#Override
public void onResponse(Call<List<Gift>> call, Response<List<Gift>> response) {
List<Gift> places = response.body();
}
}
But I don't want this. I want to first check if the flag is 0. If it is, then I want to get only the values of the list key. And want to use the following model:
class Gift extends RealmObject{
String code, name;
//getters & setters
}
How can I achieve this?
Any help is highly appreciated.
Try this way sample
class GiftList{
String flag;
String flag1;
List<Gift> gifts;
}
call.enqueue(new Callback<List<GiftList>>() {
#Override
public void onResponse(Call<List<GiftList>> call, Response<List<GiftList>> response) {
List<GiftList> places = response.body();
if(places.getFlag().equals("0"){
List<Gift> gifts=places.getList();
}
}
You can create another POJO to store your Gift class, which store the outer value like flag and flag1, then you can access the flag to determine if you are going to go into list
here is how it looks like:
class GiftResponse extends RealmObject{
int flag;
String flag1;
Gift[] list;
//getters & setters
}
and keep your Gift class as it is, then you can get the data like:
call.enqueue(new Callback<GiftResponse>() {
#Override
public void onResponse(Call<GiftResponse> call, Response<GiftResponse> response) {
GiftResponse res = response.body();
if(res.getFlag() == 0) {
Gift[] arrGifts = res.getList();
}
}
}
ofcourse using this approach you change the call response type.
You can create a ResponseWrapper class as below:
public class ResponseWrapper<T> {
private T data;
#JsonProperty("flag")
public int flag;
#JsonProperty("flag1")
public String flag1;
public T getData() {
return data;
}
}
And modify your callback
call.enqueue(new Callback<ResponseWrapper<List<Gift>>>() {
#Override
public void onResponse(Call<ResponseWrapper<List<Gift>>> call, Response<ResponseWrapper<List<Gift>>> response) {
int flag = response.body().flag;
List<Gift> places = response.body().getData();
}
}

How to return value in rxJava

I'm new into rxJava and it's making my head spin. Basically I'm pulling data from youtube api with retrofit which gives back Observable and with youtubeDataMapper I'm mappng it into Youtube Pojo object which contains String videoID. So my question is, how to make this method return that string instead of Completable?
This is my method:
#Override
public Completable downloadVideoUrl(String query) {
addSubscription(youtubeApi.getYoutubeId(query, Constants.youtubeApi)
.map(youtubeDataMapper::map)
.subscribeOn(subscribeScheduler)
.observeOn(observeScheduler)
.subscribe());
return Completable.complete();
}
You have two choices:
Make your downloadVideoUrl return Observable instead of Completable:
Preferred way:
#Override
public Completable downloadVideoUrl(String query) {
return youtubeApi.getYoutubeId(query, Constants.youtubeApi)
.map(youtubeDataMapper::map)
.subscribeOn(subscribeScheduler)
.observeOn(observeScheduler);
}
Notice lack of subscribe operator here.
Then wherever you want to get videoId:
downloadVideoUrl(query)
.subscribe(new Subscriber<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String videoId) {
// do whatever you want with videoId
}
});
Use toBlocking().first()
This is not preffered as you block current Thread until Observable finishes
#Override
public String downloadVideoUrl(String query) {
return youtubeApi.getYoutubeId(query, Constants.youtubeApi)
.map(youtubeDataMapper::map)
.subscribeOn(subscribeScheduler)
.observeOn(observeScheduler)
.toBlocking().first();
}
First of all, it is better to make Retrofit return Single instead of Observable because you are expecting a single server response (and not a sequence of responses).
Secondly, Completable.complete() is a factory method for a Completable that does nothing at all. So you don’t need it here.
Regarding String videoID, it depends on what you are planning to do with it. Also, I have no idea what your .addSubscription() is doing.
I would suggest doing something like the following:
class YourClass {
private final CompositeSubscription compositeSubscription = new CompositeSubscription();
// you must call compositeSubscription.clear() either in class .finalize() or on some UI lifecycle event
void yourMethod() {
final Single videoID = youtubeApi.getYoutubeId(query, Constants.youtubeApi)
.map(youtubeDataMapper::map)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
final Subscription subscription = videoID.subscribe(new SingleSubscriber() { 
 #Override
 public void onSuccess(String value) {
// TODO: do whatever with the value
}
#Override
public void onError(Throwable error) {
// TODO: log and/or display error
}
});
compositeSubscription.add(subscription);
}
}

Can't make retrofit and Gson pull data from deployd

I am very new to Android development. I have this little project of mine that I need to get to work by accessing an api that I created locally on my mac with deployd. The api is accessible from localhost:2403/subjects. I have followed the retrofit and gson guide but so far I only keep errors and nothing viable to work with. At this point im kinda stuck. I need to get the subjects from the api and show them in a scrollable list.
I have set up Retrofit and Gson to be able to talk to the API. Therefore I have made a few classes and interfaces.
First the Subject class
public class Subject {
int id;
String name;
int ects;
int grade;
int period;
int main;
public int getMain() {
return main;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getEcts() {
return ects;
}
public int getGrade() {
return grade;
}
public int getPeriod() {
return period;
}
}
then I created a HslSubjects class which is plain and simple to hold all the subjects I get.
public class HslSubjects {
List<Subject> subjects;
}
and the api interface
public interface subjectEndpointApi {
#GET("/subjects")
Call<HslSubjects> loadSubjects();
}
followed by an adapter
public class SubjectResponse {
#SerializedName("subjects")
List<Subject> subjects;
public SubjectResponse() {
subjects = new ArrayList<Subject>();
}
public static SubjectResponse parseJSON(String response) {
Gson gson = new GsonBuilder().create();
SubjectResponse subjectResponse = gson.fromJson(response, SubjectResponse.class);
return subjectResponse;
}
}
Finally in my mainActivity I run everything
public class MainActivity extends ListActivity implements Callback<HslSubjects> {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
requestWindowFeature(Window.FEATURE_PROGRESS);
ArrayAdapter<Subject> arrayAdapter = new ArrayAdapter<Subject>(this, android.R.layout.simple_list_item_1, android.R.id.text1, new ArrayList<Subject>());
setListAdapter(arrayAdapter);
setProgressBarIndeterminateVisibility(true);
setProgressBarVisibility(true);
Log.i("api", "preparing to make the api call");
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://10.0.3.2:2403") //10.0.3.2
.addConverterFactory(GsonConverterFactory.create())
.build();
// preparing the call in retrofit
subjectEndpointApi subjectEndpointApi = retrofit.create(subjectEndpointApi.class);
Call<HslSubjects> call = subjectEndpointApi.loadSubjects();
call.enqueue(this);
}
#Override
public void onResponse(Call<HslSubjects> call, Response<HslSubjects> response) {
setProgressBarIndeterminateVisibility(false);
HslSubjects hslSubjects = response.body();
Log.i("subjects", "end of response");
}
#Override
public void onFailure(Call<HslSubjects> call, Throwable t) {
Toast.makeText(MainActivity.this, t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
Log.i("subjects", "Oops error");
}
}
But somehow it always goes to the onFailure class. The emulator gives the following error:
Seems like your server JSON is not what you are expecting on your device.
Error says that android wants JSON object, but the json you pass is kinda array.
Try to add logging and see what is the problem there.
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
So I guess you should write something like:
public interface subjectEndpointApi {
#GET("/subjects")
Call<List<Subject>> loadSubjects();
}

How to determine if a given request is running?

I'm looking at retrofit for my networking layer. Is there any way to tell if a particular async request is running at any given moment?
For example, I'd like to know if a request is running so that I can update the user interface at various times. I could do this myself by keeping variables around to track state, but wondering if there's something already in the library for this.
Here is what I would normally do when needing a way to keep track of running requests:
First, using retrofit, as soon as you make the request, you can do the following:
Use EventBus library to post an event to your activity or fragment. Now, this can be done inside onSuccess() method of your Callback or onError() method of the same.
In your activity or fragment's onEvent(EventClassName event) method, you can simply check a variable like [isRunning] from your event to make sure that if the event is still running, you update the UI accordingly and if not, do what you need to do respectively.
When the request is completed, obviously isRunning will be false and you can then update the UI as expected by the user.
I am recommending EventBus here simply because it is much easier to decouple your application code with it; you can send different events that notify the activity of the different statuses of your requests and then update your UI that way.
You can find EventBus here
I hope this helps!
What I personally ended up doing in this case was that I was running the example with Retrofit, Android Priority Jobqueue (from yigit's fork) and Otto eventbus.
public enum SingletonBus {
INSTANCE;
private Bus bus;
private Handler handler = new Handler(Looper.getMainLooper());
private SingletonBus() {
this.bus = new Bus(ThreadEnforcer.ANY);
}
public <T> void postToSameThread(final T event) {
bus.post(event);
}
public <T> void postToMainThread(final T event) {
handler.post(new Runnable() {
#Override
public void run() {
bus.post(event);
}
});
}
public <T> void register(T subscriber) {
bus.register(subscriber);
}
public <T> void unregister(T subscriber) {
bus.unregister(subscriber);
}
}
public interface Interactor {
void injectWith(PresenterComponent presenterComponent);
}
public interface SendCertificateRequestInteractor
extends Interactor {
interface Listener {
void onSuccessfulEvent(SuccessfulEvent e);
void onFailureEvent(FailureEvent e);
}
class SuccessfulEvent
extends EventResult<CertificateBO> {
public SuccessfulEvent(CertificateBO certificateBO) {
super(certificateBO);
}
}
class FailureEvent
extends EventResult<Throwable> {
public FailureEvent(Throwable throwable) {
super(throwable);
}
}
void sendCertificateRequest(String username, String password);
}
Pay attention to the Job here:
public class SendCertificateRequestInteractorImpl
implements SendCertificateRequestInteractor {
private Presenter presenter;
private boolean isInjected = false;
#Inject
public JobManager jobManager;
public SendCertificateRequestInteractorImpl(Presenter presenter) {
this.presenter = presenter;
}
#Override
public void sendCertificateRequest(String username, String password) {
if(!isInjected) {
injectWith(presenter.getPresenterComponent());
isInjected = true;
}
InteractorJob interactorJob = new InteractorJob(presenter, username, password);
long jobId = jobManager.addJob(interactorJob); //this is where you can get your jobId for querying the status of the task if you want
}
#Override
public void injectWith(PresenterComponent presenterComponent) {
presenterComponent.inject(this);
}
public static class InteractorJob
extends Job {
private final static int PRIORITY = 1;
private final static String TAG = InteractorJob.class.getSimpleName();
private String username;
private String password;
#Inject
public MyService myService;
public InteractorJob(Presenter presenter, String username, String password) {
super(new Params(PRIORITY).requireNetwork());
presenter.getPresenterComponent().inject(this);
this.username = username;
this.password = password;
}
#Override
public void onAdded() {
// Job has been saved to disk.
// This is a good place to dispatch a UI event to indicate the job will eventually run.
// In this example, it would be good to update the UI with the newly posted tweet.
}
#Override
public void onRun()
throws Throwable {
String certificate = myService.getCertificate(username, password);
SingletonBus.INSTANCE.postToMainThread(new SuccessfulEvent(certificate));
}
#Override
protected void onCancel() {
// Job has exceeded retry attempts or shouldReRunOnThrowable() has returned false.
Log.e(TAG, "Cancelled job.");
}
#Override
protected boolean shouldReRunOnThrowable(Throwable throwable) {
// An error occurred in onRun.
// Return value determines whether this job should retry running (true) or abort (false).
Log.e(TAG, "Failed to execute job.", throwable);
SingletonBus.INSTANCE.postToMainThread(new FailureEvent(throwable));
return false;
}
}
}
And then
#Subscribe
#Override
public void onSuccessfulEvent(SendCertificateRequestInteractor.SuccessfulEvent e) {
String certificate = e.getResult();
//do things
}
#Subscribe
#Override
public void onFailureEvent(SendCertificateRequestInteractor.FailureEvent e) {
Throwable throwable = e.getResult();
//handle error
}
More about android priority jobqueue here.
This way, technically the async handling is referred to the job queue, while Retrofit itself is using the synchronous interface. It works well as long as you don't need to access the headers of the response. Although to be fair, I was also keeping track of whether the job was running with a boolean instead of the job manager and the id as well..
Also, I haven't figured out how to use dependency injection properly with persisted jobs; nor do I really know how they intended to make that work. Of course, it'd work if it was using the application scoped component rather than a supplied presenter scoped one, but that is irrelevant.
You'll probably need to customize this solution to your own scenario, and use only what you actually need.

Categories

Resources