callback doesnt work properly after data onResponse - java

I have this method
private void setNews(final GetDataCallback getDataCallback){
GetDataService service = RetrofitClientInstance.getRetrofitInstance().create(GetDataService.class);
Call<ItemsAPI> call = service.getAllItems();
call.enqueue(new Callback<ItemsAPI>() {
#Override
public void onResponse(Call<ItemsAPI> call, Response<ItemsAPI> response) {
if (response.isSuccessful()) {
Log.d(TAG, "onResponse");
items = response.body();
getDataCallback.onGetData(items.getItems());
}
else {
getDataCallback.onError();
}
}
#Override
public void onFailure(Call<ItemsAPI> call, Throwable t) {
getDataCallback.onError();
Log.d(TAG, "onFailure "+ t.getMessage());
}
});
}
where I get callball with information from API
And I need to give this info to another callball
public MutableLiveData<List<News>> getNews(){
setNews(new GetDataCallback() {
#Override
public void onGetData(List<News> newsData) {
dataSet = newsData;
Log.d(TAG, "size: "+dataSet.size());
}
#Override
public void onError() {
}
});
MutableLiveData<List<News>> data = new MutableLiveData<>();
Log.d(TAG, "size before setValue: "+dataSet.size());
data.setValue(dataSet);
return data;
}
When I check log I can see
2019-05-18 10:45:17.575 2250-2250/? D/NewsRepository: size before setValue: 0
2019-05-18 10:45:18.334 2250-2250/com.krasnov.rxjavalearning D/NewsRepository: onResponse
2019-05-18 10:45:18.334 2250-2250/com.krasnov.rxjavalearning D/NewsRepository: size: 30
From another class I call getNews() method. I need to do setNews() first, have elements in data set and after return value from getNews().
How can I do this?

Retrofit.enqueue() is asynchronous call and execute in future.
If you want to update ui from Livedata from getNews()
LiveData<Data> getNews() {
final MutableLiveData<Data> data = new MutableLiveData<>();
setNews( new MyCallback(){
Void onSuccess(Data data){
data.setValue(data); // for success live data get call back
}
void onError(){
data.setValue(null); // for error case can pass null or empty list
}
});
return data;
}
You will get update with response

Related

How to notify UI on volley success MVVM architecture

I am using mvvm architecture I would like to notify view when volley post request is successful, what i could do is to instantiate ViewModel in appRepository class and then post values to a liveData, but i guess that's not a good approach as I haven't seen a similar practice. Can anyone suggest me a good approach to return my response to ui, or at least notify that post request has been successful.
From fragment/View I trigger this method
// save data to api
checkInViewModel.updateEventPersonEntity(eventPersonsEntity);
ViewModel forwards it to apprespository
public void updateEventPersonEntity(EventPersonsEntity eventPersonsEntity) {
mRepository.updateEventPersonEntity(eventPersonsEntity);
}
AppRepository.Java class
public void updateEventPersonEntity(EventPersonsEntity eventPersonsEntity) {
executor.execute(() -> {
// mDb.eventPersonsDao().update(eventPersonsEntity);
if (isNetworkAvailable(context)) {
post_updateEventPersonEntity(eventPersonsEntity);
}
});
}
private void post_updateEventPersonEntity(EventPersonsEntity eventPersonsEntity) {
Map<String, Object> params = new HashMap<>();
params.put("EventPersonId", eventPersonsEntity.getEventPersonId());
params.put("EventId", eventPersonsEntity.getEventId());
params.put("PersonId", eventPersonsEntity.getPersonId());
params.put("CashStart", parseDoubleToGerman(eventPersonsEntity.getCashStart()));
params.put("CashEnd", parseDoubleToGerman(eventPersonsEntity.getCashEnd()));
params.put("StartingTime", String.valueOf(eventPersonsEntity.getStartingTime()));
params.put("EndingTime", String.valueOf(eventPersonsEntity.getEndingTime()));
params.put("isChekcedIn", eventPersonsEntity.getIsCheckedIn());
params.put("isChekcedOut", eventPersonsEntity.getIsCheckedOut());
JSONObject objRegData = new JSONObject(params);
String eventPersonApi = APP_URL.EVENT_PERSONS_API + eventPersonsEntity.getEventPersonId();
RequestQueueSingleton.getInstance(context).objectRequest(eventPersonApi, Request.Method.PUT, this::onSuccess_updateEventPersonEntity, this::onError, objRegData);
}
private void onError(VolleyError error) {
Log.d(APP_REPOSITORY_TAG, "requestError: " + error);
}
private void onSuccess_updateEventPersonEntity(JSONObject jsonObject) {
// notify ui
}
You can do this same as you did for your success response logic in repository. Simply create new callback interface:
interface OnEventUpdatedListener{
void eventUpdated();
}
Then, update your method to look like this, passing the listener to the actual method that does the work:
public void updateEventPersonEntity(EventPersonsEntity eventPersonsEntity, OnEventUpdatedListener listener) {
mRepository.updateEventPersonEntity(eventPersonsEntity, listener);
}
Pass this inside your:
if (isNetworkAvailable(context)) {
post_updateEventPersonEntity(eventPersonsEntity, listener);
}
After that, in your onSuccess() method simply call:
private void onSuccess_updateEventPersonEntity(JSONObject jsonObject) {
listener.eventUpdated();
}
Finally, you will have the info when the update happens, in the calling site, if you call your repository like this:
updateEventPersonEntity(null, new OnEventUpdatedListener() {
#Override
public void EventUpdated() {
// Do your logic here
}
});

Performing Callbacks one after another

I am new to doing asynchronous programming in Android Java. I am wondering if there is a way to run another Callback after an initial Callback function has completed. Right now, I think they are running in parallel even though the second relies on the first.
First Callback:
// GETTING USER
private interface FirestoreUserCallback {
void onCallback (User myUser);
}
private void getUser(final FirestoreUserCallback firestoreCallback) {
Task<DocumentSnapshot> task = fStore.collection("users").document(fAuth.getCurrentUser().getUid()).get();
task.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
user = documentSnapshot.toObject(User.class);
firestoreCallback.onCallback(user);
Log.d(TAG, "user created");
}
});
task.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "user creation failed");
}
});
}
Second Callback:
// GETTING ALL DOCUMENTS
private interface FirestoreDocumentCallback {
void onCallback (List<TableEntries> myEntries);
}
private void getDocuments (final FirestoreDocumentCallback firestoreDocumentCallback) {
fStore.collection("result")
.document(Integer.toString(user.getCompanyNumber())) // need to use User object returned from the first Callback
.collection("SAM").get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
List<TableEntries> results = new ArrayList<>();
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
// add objects to results ArrayList ...
Log.d(TAG, document.getId() + " => " + document.getData());
}
firestoreDocumentCallback.onCallback(results);
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
}
onCreate:
getUser(new FirestoreUserCallback () {
#Override
public void onCallback(User myUser) {
user = myUser;
}
});
getDocuments(new FirestoreDocumentCallback() {
#Override
public void onCallback(List<TableEntries> myEntries) {
entries = myEntries;
}
});
getDocuments() relies on the user variable being given its value from the first Callback. I'm receiving this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'double java.lang.Double.doubleValue()' on a null object reference
Callbacks are looking fine. You just need to check if your value is null or not before accessing it. Just add a null check
if(doubleValue!=null)
Using RxJava. First, we fetch the user and then fetch the documents. Rx-Java has an operator flatmap. flatmap is used to execute the sequential tasks, where the second task is dependent on the data from the first task.
final CompositeDisposable disposable = new CompositeDisposable();
//function to fetch user data
Single<User> getUser(){
return API.getUserData(...);
}
//function to fetch ducuments
Sinlge<UserDetail> getDocuments(int userId){
return API.getUserDetail(userId, ...);
}
//Subscribe
disposable.add(getUser()
.flatmap(user-> return getDocuments(...))
.subscribeOn(Scheduler.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObservable(){
#Override
public void onSuccess(UserDetail userDetail){
Log.v("Api result", "Successful";
//Do some work
}
#Override
public void onError(Throwable e)
Log.v("Api result", "Error Returned");
}
}));
If either of the API call fails, onError() is called. If first API fails, second API call is not executed and onError() is called.
The simplest solution for your use-case is to pass both queries to Tasks.whenAllSuccess() method, as explained in my answer from the following post:
Firestore - Merging two queries locally
So once the task is complete, you can use the elements from both queries. Another solution might be to use Android Jetpack with LiveData along with ViewModel, as the Android team recommends.

how to url calling return from list function with retrofit

i tried to return list from the url that i get with retrofit. it works and i get the data but it wont return.
this is my code
public List<MovieResponse> loadCourses() {
ArrayList<MovieResponse> list = new ArrayList<>();
ApiServices apiService =
NetworkClient.getRetrofitClient().create(ApiServices.class);
Call<MovieResult> call = apiService.getMovies();
call.enqueue(new Callback<MovieResult>() {
#Override
public void onResponse(Call<MovieResult> call, Response<MovieResult> response) {
if (response.body() != null) {
ArrayList<MovieResponse> movies = new ArrayList<>();
movies = response.body().getResults();
Log.d("",""+movies);
list.addAll(movies);
Log.d("",""+list);
}
}
#Override
public void onFailure(Call<MovieResult> call, Throwable t) {
// Log error here since request failed
Log.e("error", t.toString());
}
});
return list;
}
when i print list inside onResponse it works and there are the data. but when i return it or trying to print list outside onResponse for example below ArrayList<MovieResponse> list = new ArrayList<>(); it not show the data.
please help what is actually wrong with it. i really appreciate it.
The simplest way is to define your movies list directly inside activity or fragment(in other words, a field member of the class).
It's not a good idea to return data from an asynchronous method.
Change the return type of the loadCourses method to void and instantiate the filed movies inside onResponse().
public class SomeActivity extends AppCompatActivity {
private ArrayList<MovieResponse> movies = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_some);
}
public void loadCourses() {
ApiServices apiService =
NetworkClient.getRetrofitClient().create(ApiServices.class);
Call<MovieResult> call = apiService.getMovies();
call.enqueue(new Callback<MovieResult>() {
#Override
public void onResponse(Call<MovieResult> call, Response<MovieResult> response) {
if (response.body() != null) {
movies = response.body().getResults();
...
}
}
#Override
public void onFailure(Call<MovieResult> call, Throwable t) {
...
}
});
}
}
It is because you are making asynchronous call which is being handled by a separate thread. So after call.enqueue(), the main thread directly jumps to return statement without waiting for API response, that's why you are getting empty list.
Assuming your API takes 1 sec to respond,
just for an experiment, you can try adding a sleep() for 3 sec right before your return statement, it should return all the movies.
If you must return from the method then go for retrofit synchronous call.
To make a sync call create a new thread in main thread and make call from there, it is not allowed to make network call from main thread because it blocks the thread.

How can i save on arraylist the response that i receive from my api?

i'm new on retrofit and i want to save the response from my api like an object on an array list.
i've search solutions but i dont know how callback method works and i can't understand very well.
public ArrayList<Match> recolectar_partido(){
final ArrayList<Match> datos=new ArrayList<Match>();
Call<List<MatchResponse>> call = RetrofitClient.getInstance().getApi().getmatch();
call.enqueue(new Callback<List<MatchResponse>>() {
#Override
public void onResponse(Call<List<MatchResponse>> call, Response<List<MatchResponse>> response) {
matchlist=response.body();
for (MatchResponse fix:matchlist) {
Integer idfix=fix.getId_fixture();
Integer idsta=fix.getId_stadium();
String fecha=fix.getFecha();
String hora=fix.getHora();
Match variable= new Match(idfix,idsta,fecha,hora);
datos.add(variable);
}
}
#Override
public void onFailure(Call<List<MatchResponse>> call, Throwable t) {
Toast.makeText(getApplicationContext(),"error de conexion",Toast.LENGTH_SHORT).show();
}
});
return datos;
}
i want the arraylist to be filled.
Do call.execute() instead of enqueue.
E.g.
final ArrayList<Match> datos=new ArrayList<Match>();
Call<List<MatchResponse>> call = RetrofitClient.getInstance().getApi().getmatch();
matchlist= call.execute().body();
for (MatchResponse fix:matchlist) {
Integer idfix=fix.getId_fixture();
Integer idsta=fix.getId_stadium();
String fecha=fix.getFecha();
String hora=fix.getHora();
Match variable= new Match(idfix,idsta,fecha,hora);
datos.add(variable);
}
return datos;

addAll() method is not supported by RealmResults

What can I use instead of addAll() method in my adapter, I'm using realm version 2.0.1 and that method is deprecated, I'm trying to get all the data from the API, save it to my database and pass it to my adapter, I'm using like this:
public void getData(int page) {
if (GlobalModel.existeConexao()) {
Call<PedidosResponse> call = ((NavigationMain) getActivity()).apiService.getPedidos(GlobalModel.getToken(), GlobalModel.geEmpresaId(), page);
call.enqueue(new Callback<PedidosResponse>() {
#Override
public void onResponse(Call<PedidosResponse> call, Response<PedidosResponse> response) {
if (response.isSuccessful()) {
for (int i = 0; i < response.body().getPedidos().size(); i++) {
Pedidos mPedido = response.body().getPedidos().get(i);
int myInt = (mPedido.isProjecao()) ? 1 : 0;
if (!mRepositorio.checkIfExists(mPedido.getId())) {
mRepositorio.addPedido(mPedido.getId(), mPedido.getCliente_id(), mPedido.getData_hora(), mPedido.getData_pedido_cliente(), mPedido.getPrevisao_entrega(), mPedido.getFrete_tipo(), myInt, mPedido.getObservacao(), mPedido.getAliquota_projecao(), mPedido.getStatus(), mPedido.getPedido_cliente());
}
}
arraypedidos = mRepositorio.findAllPedidos();
if (mPedidosAdapter == null) {
mPedidosAdapter = new PedidosAdapter(getActivity(), arraypedidos);
listpedidos.setAdapter(mPedidosAdapter);
} else {
mPedidosAdapter.setData(arraypedidos);
}
}
}
#Override
public void onFailure(Call<PedidosResponse> call, Throwable t) {
if (t.getMessage() != null) {
Log.v("pedidos", t.getMessage());
}
}
});
} else {
Toast.makeText(getActivity(), "Verifique sua conexão", Toast.LENGTH_SHORT).show();
}
}
But when I run the app I get this message:
java.lang.UnsupportedOperationException: This method is not supported by RealmResults.
That's because RealmResults is just a set of pointers that satisfy the condition defined in the query. You can't manipulate it, nor should you if you just intend to show every element in your adapter.
In fact, Realm was explicitly designed to simplify the workflow of "downloading data on a background thread and saving the data in a database", and "showing the data downloaded on a background thread automatically on the UI thread".
This is what RealmChangeListener is for.
Simply put, all of this code is unnecessary:
arraypedidos = mRepositorio.findAllPedidos();
if (mPedidosAdapter == null) {
mPedidosAdapter = new PedidosAdapter(getActivity(), arraypedidos);
listpedidos.setAdapter(mPedidosAdapter);
} else {
mPedidosAdapter.setData(arraypedidos);
}
And could be replaced with this:
public class SomeActivity extends AppCompatActivity {
PedidosAdapter pedidosAdapter;
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.some_view);
pedidosAdapter = new PedidosAdapter(context, mRepositorio.findAllPedidos());
// set adapter, ...
}
}
And
public class PedidosAdapter extends RealmRecyclerViewAdapter<Pedidos, PedidosViewHolder> {
public PedidosAdapter(Context context, RealmResults<Pedidos> results) {
super(context, results, true);
}
// onBindViewHolder
// onCreateViewHolder
}
For this, use RealmRecyclerViewAdapter, unless you intend to handle the RealmChangeListener manually.

Categories

Resources