Retrofit callback use to do something else depending on parameter - java

I begin with android dev and retrofit.
My app uses datas from a mysql/apache server. All work fine with retrofit.
But, I would like to add a dummy function in the app, with a accountId == 0 (so no login), which uses datas hard coded in the app, and not on the server.
So, I'd like to modify the call, to use MY class/method if accountID == 0 AND returning the same type of result in the same callbaks as retrofit.
I try to show you with code :
MyService.java
interface MyService {
#GET("get.php")
Call<List<MyObject>> getAll(#Query("a") int accountid);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Consts.URL_SERVER)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
MyCalls.java
public class MyCalls {
public interface CallbacksAll {
void onResponse(#Nullable List<MyObject> myobjetcs);
void onFailure();
}
public static void getAll(CallbacksAll callbacks, int accountId) {
final WeakReference<CallbacksAll> callbacksWeakReference = new WeakReference<>(callbacks);
///////////////////
// actually :
///////////////////
MyService myService = MyService.retrofit.create(MyService.class);
Call<List<MyObject>> call = myService.getAllByAccount(accountId);
///////////////////
// end actually
///////////////////
///////////////////
// what I'ld like
///////////////////
MyService myService;
Call<List<MyObject>> call;
if ( accountId > 0 ) {
myService = MyService.retrofit.create(MyService.class);
call = myService.getAll(accountId);
} else {
myService = ??? // I don't really know what to create and do here
call = MyClass.getAll(); // my class/method nevermind
}
///////////////////
// end what I'd like
///////////////////
///////////////////
// what I'd like to NOT change
///////////////////
call.enqueue(new Callback<List<MyObject>>() {
#Override
public void onResponse(#NonNull Call<List<MyObject>> call, #NonNull Response<List<MyObject>> response) {
if (callbacksWeakReference.get() != null)
callbacksWeakReference.get().onResponse(response.body());
}
#Override
public void onFailure(#NonNull Call<List<MyObject>> call, #NonNull Throwable t) {
if (callbacksWeakReference.get() != null) callbacksWeakReference.get().onFailure();
}
});
}
I hope you will understand my poor english and what I want, and you will be able to give me ideas with simple examples.
Thanks

Not enough rep to comment, so I put my solution here.
So much time spent although the solution was so simple.
I just had to add :
if (accountId == 0) {
callbacks.onResponse(MyObject.getAll());
} else {
final WeakReference<CallbacksAll> callbacksWeakReference = new WeakReference<>(callbacks);
[...]
});
}
in
public static void getAll(CallbacksAll callbacks, int accountId) {

Related

MVVM - response List does not contain any values

I'm just trying to learn mvvm, and I faced some issue - My List which should contain response from API are empty. I'm not sure why it is. Here's some code:
MainActivity
mViewModel = new ViewModelProvider(this).get(ViewModel.class);
mViewModel.getData().observe(this, new Observer<List<Model>() {
#Override
public void onChanged(List<Model> list) {
if (data.size() > 0) {
data.clear();
}
if (list != null) {
data.addAll(list);
Log.i(TAG, "onChanged: " + data.size());
}
}
});
ViewModel
private Repository mRepository;
private MutableLiveData<List<Model> liveData;
public ViewModel(#NonNull Application application) {
super(application);
mRepository = Repository.getInstance();
liveData = mRepository.getData();
}
public MutableLiveData<List<Model> getData(){
return liveData;
}
Repository
public MutableLiveData<List<Model>> getData(){
MutableLiveData<List<Model> mLiveData = new MutableLiveData<>();
mApiCall.callApi()
.enqueue(new Callback<List<Model>() {
#Override
public void onResponse(Call<List<Model> call, Response<List<Model> response) {
mLiveData.setValue(response.body());
}
#Override
public void onFailure(Call<List<Model> call, Throwable t) {
t.getMessage();
}
});
return mLiveData;
}
in function getData() you are returning a live data and in this line in your view model :
liveData = mRepository.getData();
you are assigning it to liveDate which is a mutable live data created in your view model and the problem is here.
when this assignment is happened the observer in the liveData variable in view model will be removed and that's why we should use switchMap like this :
private var result : LiveData<List<Response>> = MutableLiveData()
result : LiveData<Response> = Transformations.map(mRepository.getData()){
it
}
and now all you need to do is to observe on repo in your view like this :
viewmodel.result.observe(this, Observer{ list ->
// to do with the result
})
response.body() will provide with class APIResponse. But what you need as response is List.
To get the expected response as List, try response.body().getMetadata().getResults()
It could be either repository initialisation as well. Do try creating
public void init() {
mRepository = Repository.getInstance();
liveData = mRepository.getData();
}
use viewModel.init();
Model class properties should match with response JSON fields to get the model objects from the response.
Debug on the response.body() and see the data coming. if the model is empty then there would be a mismatch on the fields.

How to create dynamically class in Android

In my application, I should use Material Stepper and for this, I want to use this library : https://github.com/ernestoyaquello/VerticalStepperForm
But I want to add this dynamically from server.
For connecting with server I used Retrofit library and I should check the type of items from server.
when this type is "penny" show one of this steps and when the type is "best" show another step.
I create this steps from library tutorials, but i want when type is penny show me StepDynamicTxt and when the type is best show me StepDynamicEdt!
I write below codes but just add one of the items from each step!
But in API, I have 2 item of penny types and 3 items of best type!
Should show me 5 step, but show me 2 step!
My codes :
public class StepperActivity extends AppCompatActivity {
private ApiServices apiServices;
private ProgressBar loader;
private VerticalStepperFormView stepper;
private StepDynamicEdt stepDynamicEdt;
private StepDynamicTxt stepDynamicTxt;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bidzila_stepper);
//Initialize
apiServices = ApiClient.ApiClient().create(ApiServices.class);
loader = findViewById(R.id.bidStepper_loader);
stepper = findViewById(R.id.bidStepper);
//Api
callAPi();
}
private void callAPi() {
loader.setVisibility(View.VISIBLE);
Call<TodayResponse> call = apiServices.TODAY_RESPONSE_CALL(5);
call.enqueue(new Callback<TodayResponse>() {
#Override
public void onResponse(Call<TodayResponse> call, Response<TodayResponse> response) {
if (response.isSuccessful()) {
if (response.body() != null) {
if (response.body().getRes() != null) {
if (response.body().getRes().getToday().size() > 0) {
loader.setVisibility(View.GONE);
//Foreach
for (int i = 0; i < response.body().getRes().getToday().size(); i++) {
if (response.body().getRes().getToday().get(i).getType().equals("penny")) {
stepDynamicEdt = new StepDynamicEdt(response.body().getRes().getToday().get(i).getName());
} else if (response.body().getRes().getToday().get(i).getType().equals("best")) {
stepDynamicTxt = new StepDynamicTxt(response.body().getRes().getToday().get(i).getName());
}
}
stepper.setup(new StepperFormListener() {
#Override
public void onCompletedForm() {
}
#Override
public void onCancelledForm() {
}
}, stepDynamicEdt, stepDynamicTxt)
.allowNonLinearNavigation(false)
.displayCancelButtonInLastStep(false)
.displayBottomNavigation(false)
.confirmationStepTitle("Confirm")
.stepNextButtonText("Continue")
.lastStepNextButtonText("Finish")
.includeConfirmationStep(false)
.init();
}
}
}
}
}
#Override
public void onFailure(Call<TodayResponse> call, Throwable t) {
Log.e("ResponseErr", t.getMessage());
}
});
}
#Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(ViewPumpContextWrapper.wrap(newBase));
}
}
I think this problem for this line:}, stepDynamicEdt, stepDynamicTxt) because just add 2 step.
How can i add this step dynamically in Android?
In your code, you are making a very fundamental mistake. And that is, you are using the same variable each time in your loop to store dynamic edit type and dynamic text type, which will replace any previously created fields. And hence when you finally create them, you end up with single last values of each type.
What you can do is, create a List with type Step, add new type every time you get them, and finally pass that list to the builder.
The builder accepts a list too, you should check implementation when its open source.
// before the for loop, create a list of type Step
List<Step> steps = new ArrayList();
// your loop on response received from server
for (int i = 0; i < response.body().getRes().getToday().size(); i++) {
if (response.body().getRes().getToday().get(i).getType().equals("penny")) {
StepDynamicEdt stepDynamicEdt = new StepDynamicEdt(response.body().getRes().getToday().get(i).getName());
// add to list
steps.add(stepDynamicEdt);
} else if (response.body().getRes().getToday().get(i).getType().equals("best")) {
StepDynamicTxt stepDynamicTxt = new StepDynamicTxt(response.body().getRes().getToday().get(i).getName());
// add to list
steps.add(stepDynamicTxt);
}
}
// finally create them
stepper.setup(new StepperFormListener() {
#Override
public void onCompletedForm() {
}
#Override
public void onCancelledForm() {
}
}, steps) // pass the list
.allowNonLinearNavigation(false)
.displayCancelButtonInLastStep(false)
.displayBottomNavigation(false)
.confirmationStepTitle("Confirm")
.stepNextButtonText("Continue")
.lastStepNextButtonText("Finish")
.includeConfirmationStep(false)
.init();

Problem passing parameters in Retrofit api using POST method

I want to get notifications from a server using API. I'm using Retrofit2 to work with API.
The problem is when I'm passing parameter in POST method I'm getting "IllegalArgumentException URL does not contain the parameter."
The parameter is correct and working in iOS. I want to implement in android.
Below is the result when debugging the app.
Caused by: java.lang.IllegalArgumentException: URL
"notification/newnotification" does not contain "{userid}". (parameter #1)
I've tried changing parameters and asked the API developer too. He says parameter is correct.
This is code for API Interface:
public interface API{
#FormUrlEncoded
#POST("notification/newnotification")
Call<ResponseBody> getUserNotification(
#Path("userid") int userid
);
}
RetrofitClient.java
public class RetrofitClient {
public static final String BASE_URL = "http://danceglobe.co/dance/api/";
private static RetrofitClient mInstance;
private Retrofit retrofit;
private RetrofitClient(){
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
public static synchronized RetrofitClient getInstance(){
if(mInstance == null) {
mInstance = new RetrofitClient();
}
return mInstance;
}
public API getApi(){
return retrofit.create(API.class);
}
}
Calling Function from MainActivity
private void getNotification(String currentUserId) {
Call<ResponseBody> call = RetrofitClient.getInstance().getApi().getUserNotification(Integer.parseInt(currentUserId));
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if(!response.isSuccessful()){
Toast.makeText(MainActivity.this,response.message(),Toast.LENGTH_SHORT).show();
}
try {
String s = response.body().string();
Toast.makeText(MainActivity.this,s,Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Toast.makeText(MainActivity.this,t.getMessage(),Toast.LENGTH_SHORT).show();
}
});
}
I want it to respond to some data.
Please help me. I got stuck into this from past 2 days.
Caused by: java.lang.IllegalArgumentException: URL
"notification/newnotification" does not contain "{userid}". (parameter #1)
means you should add the userId to the path like
public interface API {
#POST("notification/newnotification/{userid}")
Call<ResponseBody> getUserNotification(
#Path("userid") int userid
);
}
#Path("userid") maps the variable to the placeholder {userid}, that was missing.
I got it working by making some changes in API interface.
Below is new Code for API interface
#FormUrlEncoded
#POST("notification/newnotification")
Call<ResponseBody> getUserNotification(
#Field("userid") String userid // changed line
);

How to send Json data that is returned by Retrofit onResponse method to other class?

I wanted to refactor code for simplicity and readability and that's why I want to move the code outside the class and return a result to class whenever the method is called.
Trying:
ArrayList<MovieReview> movieReview;
public ArrayList<MovieReview> getReviews(String id) {
if (NetworkUtil.isNetworkConnected(getActivity())) {
ApiInterface apiService =
ApiClient.getClient().create(ApiInterface.class);
Call<MovieReviewResponse> call = null;
call = apiService.getMovieReviews(id, BuildConfig.THE_MOVIE_DB_API_KEY);
call.enqueue(new Callback<MovieReviewResponse>() {
#Override
public void onResponse(Call<MovieReviewResponse> call, Response<MovieReviewResponse> response) {
movieReview= (ArrayList<MovieReview>) response.body().getMovieReviews();
}
#Override
public void onFailure(Call<MovieReviewResponse> call, Throwable t) {
// Log error here since request failed
Log.e(TAG, t.toString());
}
});
}
return movieReview;
}
Output: if I used array list outside the on response gives null value.
but if I called a method from on response and pass the result movieReview, as a parameter, it works fine.
Previously used:
public void getReviews(String id) {
if (NetworkUtil.isNetworkConnected(getActivity())) {
ApiInterface apiService =
ApiClient.getClient().create(ApiInterface.class);
Call<MovieReviewResponse> call = null;
call = apiService.getMovieReviews(id, BuildConfig.THE_MOVIE_DB_API_KEY);
call.enqueue(new Callback<MovieReviewResponse>() {
#Override
public void onResponse(Call<MovieReviewResponse> call, Response<MovieReviewResponse> response) {
movieReview = (ArrayList<MovieReview>) response.body().getMovieReviews();
setData(movieReview);
}
#Override
public void onFailure(Call<MovieReviewResponse> call, Throwable t) {
// Log error here since request failed
Log.e(TAG, t.toString());
}
});
}
}`
Output: if I used array list outside the on response gives null value
Because that is how asyncronus methods work. Your return happens before onResponse ever happens, so the list object is null.
Tip: In ideal situations, you want to always return an empty list, not null anyway.
Rename your method.
public ArrayList<MovieReview> getReviews(String id)
To Instead
public ArrayList<MovieReview> getReviews(String id, Callback<MovieReviewResponse> callback)
Replace this code
call.enqueue(new Callback<MovieReviewResponse>() {
...
});
With this
call.enqueue(callback);
Wherever you call that method
// In Activity
String id = "X";
api.getReviews(id);
Now do....
// In Activity
String id = "X";
api.getReviews(id, new Callback<MovieReviewResponse>() {
...
});
And now from within onResponse, you can update a ListView adapter, or whatever you need to do
Instead of using only Retrofit make use of RxAndroid. By using this you will get response of Observable<T> which consists of three Override methods onCompleted(), onError() and onNext().
In onNext() method call your specific activity, pass your data through putExtra and get through getExtra.

Callbacks in Java, similar to Javascript

I'm trying to write a callback system in Java that works similar to that of Javascripts, what I'm doing is I'm sending information across the network that has a "callback id" attached to it. When the client receives this data back from the server, it should locate the callback for that id form a collection and call it with the retrieved data.
Here's the current system I've written up while trying to achieve this:
public class NetworkCallback {
private int id;
private Callable callback;
public NetworkCallback(Callable callback) {
this.callback = callback;
}
public NetworkCallback setId(int id) {
this.id = id;
return this;
}
public int getId() {
return id;
}
public boolean execute(JSONObject data) {
try {
callback.call(); // data?
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
These were stored in a special container a created that would retain index, it's basically just an array with some helper classes. When the client gets information back it will search for the callback and then execute it.
void onMessageReceived(byte[] data) {
JSONObject json = JSONHelper.fromByteArray(data);
int callbackId = json.getInt("cbid");
if(callbackId != 0) {
callbacks.fetch(callbackId).execute(json);
}
}
The issue with this I noticed before even attempting to run the code, pondered for awhile, and ran out of things to think about. The callable class doesn't accept parameters. So, for example, say I wanted to pass a method as a callback like the following:
psuedo
method(param JSONObject data) {
print data
}
Granted this method isn't going to be the same every time it's called, so it will be created on the fly. An example in javascript of what I'm trying to achieve can be found below:
Javascript example of what I want
(function caller() {
called(function(data) {
console.log("Data: " + data);
});
})();
function called(callback) {
callback(Math.random());
}
You will want to use a Consumer for this. A consumer basically is an object on which you can call accept(data), which executes the callback.
An example:
public class Test {
public static void main(String[] args) {
Consumer consumer = new Consumer() {
#Override
public void accept(Object o) {
System.out.println(o.toString());
}
};
new Test().doSomething("Test", consumer);
}
public void doSomething(Object data, Consumer<Object> cb) {
cb.accept(data);
}
}
This prints "Test" in the console.

Categories

Resources