I am trying to use Jetpack in my Android development, I am able to display data from the the sports api but some of the data is null and not displaying, I am using data binding and this is was my first instinct check if null during my interview
android:text="#{model.league != null ? model.league : #string/app}"
During my interview however I failed I suppose due to not being able to identify what other location I can check for null data when the I display data. So my question is how do I change this code to ensure no textview is null or how do how I handle null api calls, and where, I just want to be able to not make the same mistake again.
Reponse Class
public LiveData<List<Match>> getEvents(String teamId) {
final MutableLiveData<List<Match>> events = new MutableLiveData<>();
Call<AppResponse> call = RetrofitClient.getEvents(teamId);
call.enqueue(new Callback<AppResponse>() {
#Override
public void onResponse(#NonNull Call<AppResponse> call, #NonNull Response<AppResponse> response) {
if (response.isSuccessful()) {
AppResponse appResponse = response.body();
if (appResponse != null)
events.setValue(appResponse.getEvents());
} else
Log.i(TAG, "Error: " + response.errorBody());
}
#Override
public void onFailure(#NonNull Call<AppResponse> call, #NonNull Throwable t) {
Log.i(TAG, "Error " + t.getMessage());
}
});
return events;
}
Related
From my fragment I call: videoViewModel.fetchContentSections();
From my vm I call: public void fetchContentSections(){repository.getContent();}
From my repo I do this:
apiService.getContent(request).enqueue(new Callback<Content>() {
#Override
public void onResponse(Call<Content> call, Response<Content> response) {
List<Section> sections = response.body() != null ? response.body().getSections() : null;
if (sections != null && !sections.isEmpty()) {
final List<Section> sectionList = new ArrayList<>();
for (Section section : sections) {
sectionList.add(section);
}
}
}
#Override
public void onFailure(Call<Content> call, Throwable t) {
Log.d(TAG, "onFailure" + Thread.currentThread().getName());
}
});
Data is returned, but in this scenario the list is null.
If I substitute the if statement for: sectionsMutableLiveList.postValue(response.body().getSections());
...everything works fine. But I need to use a non-LiveData list so I can then write the sectionList to a file. I'm hoping to then read the list from the file and post the value to a LiveData list to my vm.
Does anyone know what I've done wrong?
i'm using api requests that returns a list.
-the first api request returns a list of object that contains (user_id,content,date,title)
-the second response returns list of object too that contains (user_id,user_name).
i want to merge the two list the display them into one recycler view but keep user name instead of user_id.this image breaks down what i want clearly.
apprecuiate any help i'm really stuck in this and i need it ty .
EDIT
this is the first api call :
followuplist=new ArrayList<>();
Retrofit retrofit = RetrofitInstance.getRetrofitInstance();
final Api api = retrofit.create(Api.class);
Call<List<TraitementTicketModel>> call = api.getfollowup(id, sestoken);
call.enqueue(new Callback<List<TraitementTicketModel>>() {
#Override
public void onResponse(Call<List<TraitementTicketModel>> call, Response<List<TraitementTicketModel>> response) {
if (!response.isSuccessful()) {
Toast.makeText(getApplicationContext(), "Something is wrong !! ", Toast.LENGTH_LONG).show();
Log.e("TAG", "onResponse: something is wrong");
} else if (response.body() == null) {
return;
}
List<TraitementTicketModel> followups = response.body();
for (TraitementTicketModel followup : followups) {
followuplist.add(followup);
}
followuplist.add(firstfollowup());
}
#Override
public void onFailure(Call<List<TraitementTicketModel>> call, Throwable t) {
Toast.makeText(getApplicationContext(),"Pas de connextion internet",Toast.LENGTH_LONG).show();
}
});
this is the second api call :
List<User> userList;
SharedPreferences sp =getApplicationContext().getSharedPreferences("tokenPref", Context.MODE_PRIVATE);
String sestoken = sp.getString("token","");
Retrofit retrofit= RetrofitInstance.getRetrofitInstance();
final Api api= retrofit.create(Api.class);
Call<List<User>> call = api.getUser(sestoken);
call.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
if (response.code() != 200){
Log.e("TAG", "onResponse: something is wrong"+response.code() );
}
List<User> users = response.body();
for (User user : users){
userList.add(user);
}
swipeRefreshLayout.setRefreshing(false);
}
so I have two liststhe first one is :
followuplist (user_id,title,content,date)
and the second :
userList(user_id,user_name)
but i didn't know what to do after that to get to my goal
You can do something like that.
In this example UserDetails is the object on the left in your image, UserInfo the one on the right, and MergeData the result.
You should use Kotlin instead of Java, it's far easier to manipulate lists.
List<MergedData> mergeList(
List<UserDetails> listUserDetails,
List<UserInfo> listUserInfo
) {
// Resulting list
final List<MergedData> result = new ArrayList<>();
// We iterate through the first list
for (UserDetails details : listUserDetails) {
// For each element of the list we will try to find one with the same user id in the other list
for (UserInfo info : listUserInfo) {
// if the current element of the second list has the same user id as the current one from the first list, we merge the data in a new object and this object is then added to the result list.
if (details.getUserId().equals(info.getUserId())) {
result.add(
new MergedData(
info.getName(),
details.getContent(),
details.getTitre(),
details.getDate()
)
);
// Once the object is found it is unnecessary to continue looping though the second list, so we break the for loop.
break;
}
}
}
// Once we finished to iterate through the first list, we return the result.
return result;
}
Same example in Kotlin:
fun mergeList(
listUserDetails: List<UserDetails>,
listUserInfo: List<UserInfo>
): List<MergedData> =
listUserDetails.mapNotNull { details ->
listUserInfo
.firstOrNull { it.userId == details.userId }
?.let { info ->
MergedData(
info.name,
details.content,
details.titre,
details.date
)
}
}
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.
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
Is this script wrong, because the data I receive is null while I've added data on the Cloud Firestore. I do not use RecyclerView because I only need one data only.
This is the script:
private void getCustomer(){
firestoreDB.collection("customer")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
customers = new ArrayList<>();
for (DocumentSnapshot doc : task.getResult()) {
Customer customer = doc.toObject(Customer.class);
customer.setId_customer(doc.getId());
customers.add(customer);
}
} else {
// Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
firestoreListener = firestoreDB.collection("customer")
.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
if (e != null) {
// Log.e(TAG, "Listen failed!", e);
return;
}
customers = new ArrayList<>();
for (DocumentSnapshot doc : documentSnapshots) {
Customer customer = doc.toObject(Customer.class);
customer.setId_customer(doc.getId());
customers.add(customer);
}
}
});
id_customer = customers.get(0).getId_customer();
}
and this is my firestore:
You cannot use something now that hasn't been loaded yet. With other words, you cannot simply use the following line of code:
id_customer = customers.get(0).getId_customer();
Outside the onSuccess() method because it will always be null due the asynchronous behaviour of this method. This means that by the time you are trying to use the id_customer variable outside that method, the data hasn't finished loading yet from the database and that's why is not accessible.
A quick solve for this problem would be to use that result only inside the onSuccess() method, or if you want to use it outside, I recommend you see the last part of my anwser from this post in which I have exaplined how it can be done using a custom callback. You can also take a look at this video for a better understanding.