I am using Android Studio I need to retrieve all data from an API which is http://mindicador.cl/api here I need to obtain four objects which are "uf", "dolar", "euro" and "utm". Actually I can retrieve it already, the problem comes when I want to add it to an array because in a future I want to handle this array in order to show them on RecyclerView.
public class MainActivity extends AppCompatActivity {
private List<IEconomicoObject> iEconomicoObjects;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iEconomicoObjects = new ArrayList<>();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(MiIndicadorAPI.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
MiIndicadorAPI api = retrofit.create(MiIndicadorAPI.class);
final Call<IEconomicoAPI> iEconomicoAPICall = api.getAPI();
iEconomicoAPICall.enqueue(new Callback<IEconomicoAPI>() {
#Override
public void onResponse(Call<IEconomicoAPI> call, Response<IEconomicoAPI> response) {
iEconomicoObjects.add(response.body().getUf());
iEconomicoObjects.add(response.body().getUtm());
iEconomicoObjects.add(response.body().getEuro());
iEconomicoObjects.add(response.body().getDolar());
}
#Override
public void onFailure(Call<IEconomicoAPI> call, Throwable t) {
Log.e("onFailure", "" + t.getMessage());
}
});
// here is the problem, the list is empty and should be show the data added it previously
if (!iEconomicoObjects.isEmpty()) {
for (IEconomicoObject o: iEconomicoObjects) {
Log.e("info", "nombre: " + o.getNombre());
}
} else {
Log.e("info", "the list is empty");
}
}
}
List will appear empty at the point where you are trying to read it. Since retrofit return result in a callback that is called in parallel to UIThread so , you should read the list , inside the callback.
Related
I created an app where data is loaded from server with the help of an API using Retrofit 2 . How can I fetch data from different API with the help of single API interface method?
If I request data from first URL then i should get data from first API, And if i request data from second URL then i should get data from second URL only. how can i do this with the help of a single API interface class.
This is my Retrofit
public class RetrofitInstance {
static {
System.loadLibrary("keys");
}
public static native String getUrl();
private static Retrofit retrofit;
public static Retrofit getRetrofit() {
if (retrofit == null) {
retrofit = new Retrofit.Builder().baseUrl(getUrl()).addConverterFactory(GsonConverterFactory.create()).build();
}
return retrofit;
}
}
and This is my API interface class where my api url is stored.
public interface APIInterfaces {
#GET("first.php")
Call<List<VideoModels>> getFirstVid();
#GET("second.php")
Call<List<VideoModels>> getSecondVid();
#GET("third.php")
Call<List<VideoModels>> getThirdVid();
#GET("fourth.php")
Call<List<VideoModels>> getFourthVid();
#GET("fifth.php")
Call<List<VideoModels>> getFifthVid();
}
This is my Activity and API interface method. Where I have to show details from API.
public class TestFragment extends Fragment {
TestFragmentBinding binding;
ArrayList<VideoModels> list = new ArrayList<>();
APIInterfaces interfaces;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = TestFragmentBinding.inflate(inflater, container, false);
ThumbnailViewer adapter = new ThumbnailViewer(list,getContext());
binding.mRecyclerView.setAdapter(adapter);
GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(),2);
binding.mRecyclerView.setLayoutManager(gridLayoutManager);
//Setup Data from API to ArrayList.
interfaces = RetrofitInstance.getRetrofit().create(APIInterfaces.class);
// I want to Change url as request and show data from "getSecondVid()","getThirdVid()" with singple interface method. Is it possible?
interfaces.getFirstVid().enqueue(new Callback<List<VideoModels>>() {
#SuppressLint("NotifyDataSetChanged")
#Override
public void onResponse(Call<List<VideoModels>> call, Response<List<VideoModels>> response) {
list.clear();
if (response.isSuccessful() && response !=null) {
list.addAll(response.body());
adapter.notifyDataSetChanged();
}
else {
Toast.makeText(getContext(), "No Data Found", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<List<VideoModels>> call, Throwable t) {
Toast.makeText(getContext(), "Unable to Connect", Toast.LENGTH_SHORT).show();
}
});
return binding.getRoot();
}
}
Your answer is very helpful for me.
You can do something like this
public interface APIInterfaces {
#GET
Call<List<VideoModels>> getVid(#Url String URL);
}
For every call, you just have to pass the URL to the same method.
you implementation will change to this
// you can pass the URL for the first.php, second.php video etc.
interfaces.getVid("your_url").enqueue(new Callback<List<VideoModels>>() {
#SuppressLint("NotifyDataSetChanged")
#Override
public void onResponse(Call<List<VideoModels>> call, Response<List<VideoModels>> response) {
list.clear();
if (response.isSuccessful() && response !=null) {
list.addAll(response.body());
adapter.notifyDataSetChanged();
}
else {
Toast.makeText(getContext(), "No Data Found", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<List<VideoModels>> call, Throwable t) {
Toast.makeText(getContext(), "Unable to Connect", Toast.LENGTH_SHORT).show();
}
});
I have this method that I am trying to pull data from an API, and then update the text view. Everything works except getRecipeName doesn't finish after the "end Method" log. .getRecipeName() uses RetroFit to pull from an API.
I am currently learning MVP, Dagger, RxJava, and Butterknife all at once using
Mindork's Github page on MVP Architecture
I commented out the .subscribeOn and .observeOn to see the result difference and nothing changed.
#Override
public void onRandomButtonClicked() {
getMvpView().showLoading();
Log.e(TAG, "Random Method Open");
getCompositeDisposable().add(getDataManager()
.getRecipeName()
//.subscribeOn(getSchedulerProvider().io())
//.observeOn(getSchedulerProvider().ui())
.subscribe(new Consumer<String>() {
#Override
public void accept(String s) throws Exception {
Log.e(TAG, "accept");
getMvpView().updateTextView(title);
}
}));
Log.e(TAG, "end method");
}
Here is my getRecipeName() method
#Override
public Observable<String> getRecipeName() {
/*Create handle for the RetrofitInstance interface*/
GetDataService service = RetrofitClientInstance.getRetrofitInstance().create(GetDataService.class);
Call<RecipeList> call = service.getRecipe();
call.enqueue(new Callback<RecipeList>() {
#Override
public void onResponse(#NonNull Call<RecipeList> call, #NonNull retrofit2.Response<RecipeList> response) {
Log.e("onResponse","Recipe is Successful = " + response.isSuccessful());
//if response is false then skip to avoid null object reference
if (response.isSuccessful()) {
RecipeList drinkRecipe = response.body();
List<Recipe> recipes = drinkRecipe.getDrinks();
jokeText = String.valueOf(recipes.size());
Recipe myRecipe = recipes.get(0);
jokeText = myRecipe.getStrDrink();
Log.e("On Response", "Result2: " + jokeText);
}
//jokeText = "null";
}
#Override
public void onFailure(Call<RecipeList> call, Throwable t) {
Log.e("On Response","Failure");
}
});
//return jokeText;
return Observable.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
return jokeText;
}
});
}
Solution
So as the comments stated RxJava Adapter was the correct way to go. I will just post my working code on myself using the adapter. I found it very difficult to find a working example.
//single api call using retrofit and rxjava
#SuppressLint("CheckResult")
private void getRandomButtonClick(){
retrofit = RetrofitClientInstance.getRetrofitInstance();
retrofit.create(GetDataService.class).getRecipe()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::handleResults, this::handleError );
}
private void handleResults(RecipeList recipeList) {
int i = recipeList.getDrinks().size();
Log.e(TAG, "size is: "+ i);
Recipe recipe = recipeList.getDrinks().get(0);
getMvpView().updateTextView(recipe.getStrDrink());
}
private void handleError(Throwable t){
Log.e("Observer", "");
}
My Retrofit Client Instance
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
return retrofit;
}
My Interface
public interface GetDataService {
//#Headers({})
#GET("random.php")
Observable<RecipeList> getRecipe();
I found a great resource to reference for me to correctly implement this. Retrofit Android
The reason is because your observable is returning jokeText every time it is subscribed upon. It returns immediately after invocation and will not wait for your network operation.
One possible solution is to use the RxJavaCallAdapter. Link here: https://github.com/square/retrofit/tree/master/retrofit-adapters/rxjava2
It will automatically convert your API returns to observables. No need to manually invoke retrofit requests. Just process the response and convert it to your desired object from there.
Another approach would be to wrap your entire sequence in an Observable.create or Observable.fromAsync.
I am building an activity in which I'm loading lists of objects from an api. I need to make multiple requests with retrofit which returns different objects. I can make the requests but I don't know how I can check when they're done.
The following code is what I have.
ApiRepository
public interface ApiRepository {
#GET("/api/troopmarker.json")
Call<List<TroopMarker>> getTroopMarkers();
#GET("/api/troop.json")
Call<List<Troop>> getTroops();
#GET("/api/treasure.json")
Call<List<TroopMarker>> getTreasures();
}
RepositoryService
public interface RepositoryService
{
void loadTroops(final TroopCallback callback);
void loadTroopMarkers(final TroopMarkerCallback callback);
//void loadTreasures(final TreasureCallback callback);
}
RepositoryServiceImpl
public class RepositoryServiceImpl implements RepositoryService {
private String url;
private Activity context;
public RepositoryServiceImpl(String url, Activity context) {
this.url = url;
this.context = context;
}
public void loadTroops(final TroopCallback callback) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiRepository repository = retrofit.create(ApiRepository.class);
repository.getTroops().enqueue(new Callback<List<Troop>>() {
public List<Troop> troops;
#Override
public void onResponse(Call<List<Troop>> call, Response<List<Troop>> response) {
if(response.isSuccessful()) {
Log.d("RETROFIT", "RESPONSE " + response.body().size());
callback.onSuccess(response.body());
}
}
#Override
public void onFailure(Call<List<Troop>> call, Throwable t) {
CharSequence text = "Error loading troops.";
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
callback.onSuccess(null);
}
});
}
public void loadTroopMarkers(final TroopMarkerCallback callback) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
ApiRepository repository = retrofit.create(ApiRepository.class);
repository.getTroopMarkers().enqueue(new Callback<List<TroopMarker>>() {
#Override
public void onResponse(Call<List<TroopMarker>> call, Response<List<TroopMarker>> response) {
if(response.isSuccessful()) {
Log.d("RETROFIT", "RESPONSE " + response.body().size());
callback.onSuccess(response.body());
}
}
#Override
public void onFailure(Call<List<TroopMarker>> call, Throwable t) {
CharSequence text = "Error loading troops.";
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
callback.onSuccess(null);
}
});
}
public void loadTreasures() {
}
}
LoadActivity
public class LoadActivity extends AppCompatActivity
{
//TODO LOAD TROOPS AND TROOPMARKERS
//Load troops, troopmarkers, treasures and put on map
public List<Troop> troops;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_loading);
//Start RepositoryService
final RepositoryService repositoryService = new RepositoryServiceImpl("http://internco.eu", this);
//Load troops
repositoryService.loadTroops(new TroopCallback() {
#Override
public void onSuccess(List<Troop> troops) {
Log.d("RETROFIT", "SUCCESFULLY LOADED TROOPS SIZE: " + troops.size());
}
});
//Load troopMarkers
repositoryService.loadTroopMarkers(new TroopMarkerCallback() {
public List<TroopMarker> troopMarkers;
#Override
public void onSuccess(List<TroopMarker> troopMarkers) {
Log.d("RETROFIT", "SUCCESFULLY LOADED TROOPMARKERS SIZE: " + troopMarkers.size());
}
});
//Should now here when I'm done with my requests.
Log.d("RETROFIT", "DONE");
}
}
Can someone point me out on this? I think that I have to use the RxJava library but I can't figure this out.
Your help is much appreciated.
1 hacky way of doing it would be to keep 2 flag variables loadTroopsflag & loadTroopMarkersflag.Then in the onSuccess callbacks of each check whether both are true and if they are then both your requests are complete. There might be edge cases in implementing a workaround like this but it should generally work. In case your requests depend on each other then as you will need to use nested called ie,
repositoryService.loadTroops(new TroopCallback() {
#Override
public void onSuccess(List<Troop> troops) {
Log.d("RETROFIT", "SUCCESFULLY LOADED TROOPS SIZE: " + troops.size());
repositoryService.loadTroopMarkers(new TroopMarkerCallback() {
public List<TroopMarker> troopMarkers;
#Override
public void onSuccess(List<TroopMarker> troopMarkers) {
Log.d("RETROFIT", "SUCCESFULLY LOADED TROOPMARKERS SIZE: " + troopMarkers.size());
}
});
}
});
Something like that,so in case you have more dependencies then your nested callbacks increase which is where Rxjava would come in and solve it in a few lines of code.I don't think you need to jump into Rx just yet as this is a relatively small problem and you Rx java brings in extra space that would increase the size of the app as well as development time.
Also note the part where you mention
//Should now here when I'm done with my requests.
Log.d("RETROFIT", "DONE");
does not imply that the requests are done,it simply means that they are queued up and in progress.These are asynchronous request and will complete when the callback completes.
I am using Retrofit in my application and receiving data from my server.
I receive data from my server in responseBody in Retrofit, but when I want to use these received data, my array is null?!!
Here is my class:
public class ActivityApplicationsList extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_applications_list);
Log.i(TAG , "size of response array: " + populateDataFromServer().size())
//this size is 0 but have to be 4 !
}
private ArrayList<StructApplication> populateDataFromServer() {
final ArrayList<StructApplication> mine = new ArrayList<>();
final APIService service = ServiceGenerator.createService(APIService.class, "2015-03-01 14:26:00", "123456", "123456");
Call<ArrayList<AppModel>> call = service.getApp();
call.enqueue(new Callback<ArrayList<AppModel>>() {
#Override
public void onResponse(Call<ArrayList<AppModel>> call, Response<ArrayList<AppModel>> response) {
ArrayList<AppModel> newAppModel = response.body();
for(int i=0 ; i < newAppModel.size();i++){
StructApplication structApplication = new StructApplication();
structApplication.setName(String.valueOf(newAppModel.get(i).getAppId()));
structApplication.setId(newAppModel.get(i).getAppId());
structApplication.setAppVersionReleaseDate(newAppModel.get(i).getAppVersionReDate());
structApplication.setAppDeleted(newAppModel.get(i).getAppDeleted());
mine.add(structApplication);
}
}
#Override
public void onFailure(Call<ArrayList<AppModel>> call, Throwable t) {
}
});
return mine;
}
}
And I debugged to make sure that all data received in onResponse:
As you can see, I received all data correctly, but when I use this response in onCreate of this class it's null!
I really appreciate your answer about this weird problem.
This is because you are printing your list size before the response comes. As request is being sent asynchronously, and your are trying to get size before onResponse() callback method.
try to add this line
Log.i(TAG , "size of response array: " + populateDataFromServer().size())
in onResponse() method after mine.add(structApplication); you will see the right size.
Its return first and execute later, try this way...
private ArrayList<StructApplication> populateDataFromServer() {
final ArrayList<StructApplication> mine = new ArrayList<>();
final APIService service = ServiceGenerator.createService(APIService.class, "2015-03-01 14:26:00", "123456", "123456");
Call<ArrayList<AppModel>> call = service.getApp();
Response<ArrayList<AppModel>> response = responseCall.execute();
ArrayList<AppModel> newAppModel = response.body();
for(int i=0 ; i < newAppModel.size();i++){
StructApplication structApplication = new StructApplication();
structApplication.setName(String.valueOf(newAppModel.get(i).getAppId()));
structApplication.setId(newAppModel.get(i).getAppId());
structApplication.setAppVersionReleaseDate(newAppModel.get(i).getAppVersionReDate());
structApplication.setAppDeleted(newAppModel.get(i).getAppDeleted());
mine.add(structApplication);
}
return mine;
}
It is happening due to api calling because api taking few seconds to get response and meanwhile you returned your mine array. So please return response once you got value from server.
Do like this
Make a global instance for this class like
public class ActivityApplicationsList extends Activity {
ArrayList<StructApplication> mine = new ArrayList();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_applications_list);
populateDataFromServer();
Log.i(TAG , "size of response array: " + mine.size());
}
private void populateDataFromServer() {
final APIService service = ServiceGenerator.createService(APIService.class, "2015-03-01 14:26:00", "123456", "123456");
Call<ArrayList<AppModel>> call = service.getApp();
call.enqueue(new Callback<ArrayList<AppModel>>() {
#Override
public void onResponse(Call<ArrayList<AppModel>> call, Response<ArrayList<AppModel>> response) {
ArrayList<AppModel> newAppModel = response.body();
for(int i=0 ; i < newAppModel.size();i++){
StructApplication structApplication = new StructApplication();
structApplication.setName(String.valueOf(newAppModel.get(i).getAppId()));
structApplication.setId(newAppModel.get(i).getAppId());
structApplication.setAppVersionReleaseDate(newAppModel.get(i).getAppVersionReDate());
structApplication.setAppDeleted(newAppModel.get(i).getAppDeleted());
mine.add(structApplication);
}
}
#Override
public void onFailure(Call<ArrayList<AppModel>> call, Throwable t) {
}
});
}
}
You need to get the response after the request. See code comments
public class ActivityApplicationsList extends Activity
implements Callback<ArrayList<AppModel>> { // Implement callback here
// These are final, so make them fields
final ArrayList<StructApplication> mine = new ArrayList<>();
final APIService service = ServiceGenerator.createService(APIService.class, "2015-03-01 14:26:00", "123456", "123456");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_applications_list);
populateDataFromServer();
}
// Callback implementation
#Override
public void onResponse(Call<ArrayList<AppModel>> call, Response<ArrayList<AppModel>> response) {
final ArrayList<AppModel> responseBody = response.body();
for(AppModel model : responseBody){
StructApplication structApplication = new StructApplication();
structApplication.setName(String.valueOf(model.getAppId()));
structApplication.setId(model.getAppId());
structApplication.setAppVersionReleaseDate(model.getAppVersionReDate());
structApplication.setAppDeleted(model.getAppDeleted());
mine.add(structApplication);
}
// adapter.notifyDataSetChanged(); // Need this if using ListView
Log.d("SIZE", ""+mine.size()); // Correct size
}
#Override
public void onFailure(Call<ArrayList<AppModel>> call, Throwable t) {
// error
}
private void populateDataFromServer() { // this is void; it can't return
service.getApp().enqueue(ActivityApplicationsList.this);
}
Suggestion, make this constructor
public class StructApplication {
public StructApplication(AppModel model) {
setName(String.valueOf(model.getAppId());
setId(model.getAppId());
setAppDeleted(model.getAppDeleted());
setAppVersionReleaseDate(model.getAppVersionReDate());
}
}
Then, that loop can simply be
mine.add(new StructApplication(model));
i'm doing an android App with Retrofit, the problem i have is i need to return the response.body() from the Callback, this is my code:
public class RoundListProvider {
//Load Rounds
private static RoundsInterface roundClient = RetroBuilder.createService(RoundsInterface.class);
private static RoundList mRoundList;
public static RoundList getRoundList(final Context mContext)
{
Toast.makeText(mContext, "We're in", Toast.LENGTH_LONG).show();
//Fetch info from rounds
Call<RoundList> call = roundClient.getRounds();
call.enqueue(new Callback<RoundList>() {
#Override
public void onResponse(Response<RoundList> response, Retrofit retrofit) {
Log.d("BODY", response.body().toString());
Log.d("MESSAGE", response.message());
Toast.makeText(mContext, "Conexión al servidor hecha con exito", Toast.LENGTH_LONG).show();
mRoundList = response.body();
}
#Override
public void onFailure(Throwable t) {
Log.d("BODY", t.getMessage());
Log.d("ERRRO", t.getStackTrace().toString());
}
});
return mRoundList;
}
}
As you can imagine, the return mRoundList is always null, because never waits for response to be done, can someone help me with this?.
You can't mix synchronous method calls and asynchronous callbacks like that - once you enter Callback-land you have to stay there.
The code that calls getRoundList() (presumably in some Activity or Fragment) then does something with the returned value, right? Instead of just storing the body to mRoundList in the onResponse - do what you want to happen with the value there.
Well, the problem is i needed to use the response data in a Recycler View, so i did this, maybe not the best, but it works. In my provider i added a new function:
public static void savedRoundList(RoundList roundList, Activity activity, Context context)
{
RecyclerView recyclerView = (RecyclerView) activity.findViewById(R.id.rvRounds);
recyclerView.setHasFixedSize(true);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(activity);
recyclerView.setLayoutManager(linearLayoutManager);
List<RoundData> roundData = roundList.getRounds();
RoundAdapter adapter = new RoundAdapter(roundData);
Log.d("RESPONSE & ERROR", roundList.getStatus()+ " "+ roundList.getCode());
recyclerView.setAdapter(adapter);
}
So, in the Callback i just us the function savedRoundList:
savedRoundList(response.body(), mActivity, mContext);
And voilà, it works.