I'm using retrofit for fetching data from resource. But think my architecture is wrong.
So, i have an fragment with listview, for example.
In onCreateView after UI setup i calls API method(async). That returns list of models i need to setup my listview adapter.
Thats i do in callback
private Callback<List<User>> mUsersCallback = new Callback<List<User>>() {
#Override
public void success(List<User> users, Response response) {
mLoadingLayout.hideLoading();
mPeopleAdapter = new PeopleAdapter(getActivity(), users);
lvPeople.setAdapter(mPeopleAdapter);
}
#Override
public void failure(RetrofitError error) {
mLoadingLayout.hideLoading();
Log.d("get users", error.getUrl() + " " + error.toString());
}
};
In this part i sometimes get NPE when call getActivity();
How to do it on right way?
Your activity has been destroyed during the call, when you try to get it in your callback, it may be null.
Simply check if activity is not null and if null ignore the callback.
Try to create your own Callback class and there pass your Activity or Context by constructor.
Similar to this answer: https://stackoverflow.com/a/25665521/2399340
Related
Assuming i have this dummy API
#POST("somepath/setSomething")
Call<ExecuteResponse> setSomething(#Body ExecuteInput input);
and this callBack
Callback<ExecuteResponse> callBack = new Callback<ExecuteResponse>() {
#Override
public void onResponse(Call<ExecuteResponse> call, Response<ExecuteResponse> response) {};
How can i access the body of the call and getting back my ExecuteInput Object?
You can do response.getBody() and you will get the object you specified inside Response<???>. In this case, ExecuteResponse.
Of course, I'm supposing you have defined the #SerializedName inside your object or that you have the right field names inside the ExecuteResponse model class.
I am a beginner so apologies for a possible silly question.
I am trying to retrieve data from a Firebase database. This works but I cannot assign the value to a string variable for use later on.
This is the asynchronous call to the database which returns the right result. (The data its querying from is static so I don't really need an asynchronous call but as far as I am aware, I don't have another option).
public static void getAnimalDetails(String strColl, final String strQueryField, final String strQueryValue,
final MyCallBack myCallback){
mfirebaseDb.collection(strColl)
.whereEqualTo(strQueryField, strQueryValue)
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if(task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
String strResult = document.get("animal_class").toString();
Log.d(TAG, "SSSS:" + strResult );
myCallback.onCallback(strResult);
}
}
}
});
}
This is the callback function passed to the method above.
public interface MyCallBack {
void onCallback(String strValFromAsyncTask);
}
Then this is where I call the asynch task and try and access the data from the callback.
This method fires on a button click
I can see via the log that the right value is populated in strAnimalClass (which is a global variable)
But when I try to use strAnimalClass outside of the call back it is null.
getAnimalDetails("animals", "animal_common_name", animal, new MyCallBack() {
#Override
public void onCallback(String strValFromAsyncTask) {
strAnimalClass = strValFromAsyncTask;
Log.d(TAG, "mmmmm:" + strAnimalClass );
}
});
Can anyone help with how to get a value like this out of the async / callback environment for use later on?
Thank you
You can't use the value outside of the callback. Or more specifically, you can use the value outside of the callback, but not before the callback has been called. This same rule applies to Firebase's onComplete and to your custom onCallback method.
You can verify that with a few log lines:
Log.d(TAG, "Before calling getAnimalDetails")
getAnimalDetails("animals", "animal_common_name", animal, new MyCallBack() {
#Override
public void onCallback(String strValFromAsyncTask) {
Log.d(TAG, "Inside onCallback");
}
});
Log.d(TAG, "After calling getAnimalDetails")
When you run this code, it logs:
Before calling getAnimalDetails
After calling getAnimalDetails
Inside getAnimalDetails
So while you can access strAnimalClass after the code that calls getAnimalDetails, it won't have the expected value yet, because onCallback wasn't called yet.
For this reason any code that needs the value(s) from the database will need to be inside onComplete or inside onCallback, or be called from within there.
Also see:
How to check a certain data already exists in firestore or not
getContactsFromFirebase() method return an empty list
Setting Singleton property value in Firebase Listener
I'm trying to use retrofit for get records from my API and it works fine when i do something like this.
public interface materialAPI {
#GET("/mlearningServices/Course")
public void getMaterials(Callback<List<materialClass>> response); } public void getMaterials()
{
RestAdapter adapter = new RestAdapter.Builder().setEndpoint(Root_Url).build();
Log.i(TAG , "hERE IS THE LINK"+adapter.toString());
materialAPI api = adapter.create(materialAPI.class);
api.getMaterials(new Callback <List<materialClass>>() {
#Override
public void success(List<materialClass> list, Response response) {
materials = list;
showList();
customAdapter customAdapter = new customAdapter();
listView.setAdapter(customAdapter);
}
#Override
public void failure(RetrofitError error) {
}
});
}
The above code works fine and i can get all my materials but what i want to achieve next is get material with any id . When a user selects a paticular material, i want to pass the id into the get url so i can get the records
meaning i have to do something like this
#GET("/mlearningServices/Course/{myId}")
..
How to i add myId to the callback method. This is my first time of using retrofit
Use the #Path annotation
#POST("/mlearningServices/Course/{myId}")
public void getMaterials(#Path("myId") String id, Callback<Response> response);
References:
https://square.github.io/retrofit/2.x/retrofit/index.html?retrofit2/http/Path.html
http://square.github.io/retrofit/
That you are asking about is called a path variable. To set one, you must rewrite your method signature as this:
public void getMaterials(#Path("myId") String id, Callback<List<materialClass>> response);
This way, the variable defined as /path/to/your/endpoint/{nameOfPathVariable} will be injected into that String parameter passed to the method. You could also define it as an Integer, and retrofit will try to cast it accordingly.
Solution:
You can use this to pass your id, Use the #Path annotation
#GET("/mlearningServices/Course/{myId}")
Call<materialClass> getMaterials(#Path("myId") String id);
#Path is some data that you wish to provide it to GET method before Question Mark ("?") and #Query("..") is some data you wish to provide after "?"
Hope you have understood.
In a Fragment, in the onCreateView method, I set the adapter in this way:
final BusStopAdapter mAdapter = new BusStopAdapter(nearbyBusStations);
mRecyclerView.setAdapter(mAdapter);
Then there is a retrofit method which set the nearbyBusStations in the adapter:
service.getJson(origin, dest).enqueue(new Callback<DirectionResults>() {
#Override
public void onResponse(Call<DirectionResults> call, Response<DirectionResults> response) {
Route routeA = response.body().getRoutes().get(0);
Legs legs = routeA.getLegses().get(0);
nearbyBusStations.add(new NearbyBusStation(currentItem.getName(), String.valueOf(legs.getDistance().getValue()), legs.getDuration().getText()));
mAdapter.swapItems(nearbyBusStations);
mAdapter.notifyItemInserted(nearbyBusStations.size() - 1);
}
#Override
public void onFailure(Call<DirectionResults> call, Throwable t) {
Log.d("CallBack", " Throwable is " + t);
}
});
These lines of code works, but the problem is that I want to clear the mRecyclerView in another AsyncTask. I've tried some ways but nothing works.
I need to clear the mRecyclerView because I have to refresh the near bus stop station.
Simply clear your arraylist and notify your adapter where you want to clear your recyclerview.
Example:-
nearbyBusStations.clear();
mAdapter.setnotifyDataSetChanged();
Hope its help you..
You should clear your list and notify the adapter as below
nearbyBusStations.clear();
mAdapter.setnotifyDataSetChanged();
My AsyncTaskLoader is loading data from a remote server. When new data arrives, naturally a call is made to onLoadFinished. At this point I don't know how to give the new data to the RecyclerView.Adapter. Calling notifyDataSetChanged does not give it the new data: it simply tells it there is new data. So any advice on how I might do this? Right now the best I can think of is to create my own setData method in my implementation of RecyclerView.Adapter as
public void setData(List<MyObject> data){
if(null != data && !data.isEmpty()){
synchronized(mItems){
mItems.clear();
mItems.addAll(data);
}
notifyDataSetChanged();
}
}
Is my idea the best there is? Or is there a more sound way of doing this?
Expose a public method in your adapter to update data.
For example, you could put it like this
public void updateItems(ArrayList<MyObject> myObjects) {
this.data = myObjects;
notifyDataSetChanged();
}
You have two options,
1. Re- instantiate your adapter with your new data and reset the adapter.
2. The way you do it.
I can not think of any other methods.
Ok, I had the same issue and here is the solution what I did.
1st I passed list object from onLoadFinished and in the RecyclerViewAdapter I have created method name setCardInfoList() and there I passed this object to global List object which I define in adapter class.
In onLoadFinished method..
#Override
public void onLoadFinished(android.content.Loader<List<Earthquake>> loader, List<Earthquake> earthquakes) {
if (earthquakes != null && !earthquakes.isEmpty()) {
adapter.setCardInfoList(earthquakes);
adapter.notifyDataSetChanged();
}else {
emptyView.setText("No Earthquake Found...");
}
}
Inside Adapter class
public void setCardInfoList(List<Earthquake> earthquakes){
this.earthquakeList = earthquakes;
}