I don't have any type of error when I run the application, but the problem is I don't retrieve the data from my interface.
I use Dagger to inject dependencies, and retrofit to retrieve the data from Last.fm Api.
When I try to use a log to check if the data is null, the message is not displayed in logcat and don't work with a toast message.
Service interface
public interface GenreTopTracksService {
#GET("?method=tag.gettoptracks&format=json")
Single<GenreTopTracksResponse> getGenreTopTracks(#Query("tag") String user, #Query("limit") int limit, #Query("api_key") String apiKey);
}
Presenter interface
public interface GenreTopTracksPresenter {
void getGenreTopTracks(String tag, int limit, String apiKey);
}
Interactor interface
public interface GenreTopTracksInteractor {
Single<GenreTopTracksResponse> getGenreTopTracks(String tag, int limit, String apiKey);
}
View interface -- here i have the update data method
public interface GenreTopTracksView {
void updateData(List<Track> tracks);
}
this class is an a implementation of the GenreTopTrackPresneter
public class GenreTopTracksPrensenterImpl implements GenreTopTracksPresenter {
Disposable mDisposable;
GenreTopTracksInteractor mInteractor;
GenreTopTracksView mGenreViewData;
public GenreTopTracksPrensenterImpl(GenreTopTracksInteractor mInteractor, GenreTopTracksView mGenreViewData) {
this.mInteractor = mInteractor;
this.mGenreViewData = mGenreViewData;
}
#Override
public void getGenreTopTracks(String tag, int limit, String apiKey) {
disposeRequest();
mDisposable = mInteractor.getGenreTopTracks(tag, limit, apiKey)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(new Function<GenreTopTracksResponse, List<Track>>() {
#Override
public List<Track> apply(#NonNull GenreTopTracksResponse topTracksResponse) throws Exception {
if (topTracksResponse != null && topTracksResponse.getTopTracks() != null && topTracksResponse.getTopTracks().getTracks() != null) {
return topTracksResponse.getTopTracks().getTracks();
}
return new ArrayList<Track>();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<Track>>() {
#Override
public void accept(#NonNull List<Track> tracks) throws Exception {
if (tracks.size() == 0) {
// NO se muestra nada
}else{
mGenreViewData.updateData(tracks);
}
}
}, new Consumer<Throwable>() {
#Override
public void accept(#NonNull Throwable throwable) throws Exception {
Log.d("ERRORGENRE", "Errorxd");
}
});
}
private void disposeRequest() {
if (mDisposable != null && !mDisposable.isDisposed()) {
mDisposable.dispose();
}
}
}
THis is my module class to inject dependencies to my main activity
#Module
public class GenreTopTracksModule {
GenreTopTracksView mView;
public GenreTopTracksModule(GenreTopTracksView view) {
mView = view;
}
// provides the view to create the top tracks presenter
#Singleton
#Provides
public GenreTopTracksView providesTopTracksView() {
return this.mView;
}
// provides a converter factory to create the retrofit instance
#Singleton
#Provides
public Converter.Factory providesConverterFactory() {
return GsonConverterFactory.create();
}
// provides a call adapter factory needed to integrate rxjava with retrofit
#Singleton
#Provides
public CallAdapter.Factory providesCallAdapterFactory() {
return RxJava2CallAdapterFactory.create();
}
// provides a retrofit instance to create the top tracks interactor
#Singleton
#Provides
public Retrofit providesRetrofit(Converter.Factory converter, CallAdapter.Factory adapter) {
return new Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addCallAdapterFactory(adapter)
.addConverterFactory(converter)
.build();
}
// provides top tracks interactor to make an instance of the presenter
#Singleton
#Provides
public GenreTopTracksInteractor providesTopTopTracksInteractor(Retrofit retrofit) {
return new GenreTopTracksInteractorImplementation(retrofit);
}
// provides top track presenter
#Singleton
#Provides
public GenreTopTracksPresenter providesTopTracksPresenter(GenreTopTracksInteractor interactor, GenreTopTracksView mView) {
return new GenreTopTracksPrensenterImpl(interactor, mView);
}
}
And this is my main activity
public class SelectionActivity extends AppCompatActivity implements GenreTopTracksView{
#Inject
GenreTopTracksPresenter mPresenter;
Button mButton;
EditText mEditText;
String tag;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_selection);
DaggerGenreTopTracksComponent.builder().genreTopTracksModule(new GenreTopTracksModule(this)).build().inject(this);
mButton = (Button) findViewById(R.id.button_prueba);
mEditText = (EditText) findViewById(R.id.edit_prueba);
mButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
tag = mEditText.getText().toString();
mPresenter.getGenreTopTracks(tag, Constants.TOP_ITEMS_LIMIT, Constants.API_KEY);
}
});
}
#Override
public void updateData(List<Track> tracks) {
if(tracks != null){
for(int x = 0; x<tracks.size(); x++){
Log.d("datasetTrack", tracks.get(x).getName());
Toast.makeText(SelectionActivity.this, tracks.get(x).getName(), Toast.LENGTH_SHORT).show();
}
}else if(tracks == null){
Log.d("datasetTrack", "datos nulos :(");
Toast.makeText(SelectionActivity.this, "Datos nulos", Toast.LENGTH_SHORT).show();
}
}
}
I need to see the data in the "updateData" method.
First of all recommend you add retrofit client interceptor
Add dependency in build.gradle:
compile 'com.squareup.okhttp3:logging-interceptor:$your_version'
in GenreTopTrackModule change method
#Singleton
#Provides
public Retrofit providesRetrofit(Converter.Factory converter, CallAdapter.Factory adapter) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
return new Retrofit.Builder()
.client(client)
.baseUrl(Constants.BASE_URL)
.addCallAdapterFactory(adapter)
.addConverterFactory(converter)
.build();
}
As a result in Logcat you will see response what last.fm api return. If response return right check you implementation
Related
Android newly here. I am working on a popular movies app that fetches some movie data from moved.org and display to users when tapping on movie thumbnail. Recently, I have been working on setting up retrofit in my app for two weeks. I am trying to use MVVM pattern. My goals is to get response from api.moviedb.org so that, eventually, I can display movie posters in recycler view. For some reason, I am not able to get any response from the api call using retrofit. I think I did something wrong with setting up retrofit but can't find out how I messed up... I feel like I am hard stuck.. Any help from experienced android developers would be much appreciated:)
Github link to my project
On create method in MainActivity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = findViewById(R.id.rvMovies);
movieViewModel = ViewModelProviders.of(this).get(MovieViewModel.class);
// Log.d("viewmodel", String.valueOf(movieViewModel));
movieViewModel.getMoviesLiveData().observe(this, new Observer<MovieResult>() {
#Override
public void onChanged(MovieResult movieResult) {
Log.d("movieResult", String.valueOf(movieResult));
}
});
}
ViewModel Class:
public class MovieViewModel extends AndroidViewModel {
private MutableLiveData<MovieResult> movieLiveData;
private MovieRepository movieRepository;
public MovieViewModel(#NonNull Application application){
super(application);
}
public void init(){
if (movieLiveData != null){
return;
}
movieRepository = MovieRepository.getInstance();
movieLiveData = movieRepository.getMovies("api_key_here");
}
public LiveData<MovieResult> getMoviesLiveData() {
init();
return movieLiveData;
}
}
Respository:
public class MovieRepository {
private static MovieRepository movieRepository;
public static MovieRepository getInstance(){
if (movieRepository == null){
movieRepository = new MovieRepository();
}
return movieRepository;
}
private GetMovieService service;
public MovieRepository(){
service = RetrofitInstance.getRetrofitInstance().create(GetMovieService.class);
}
public MutableLiveData<MovieResult> getMovies(String api){
Log.d("getmovies","called");
final MutableLiveData<MovieResult> movieData = new MutableLiveData<>();
service.getMovieResult(api).enqueue(new Callback<MovieResult>() {
#Override
public void onResponse(Call<MovieResult> call, Response<MovieResult> response) {
if (response.isSuccessful()){
movieData.setValue(response.body());
Log.d("debug", String.valueOf(movieData));
}
}
#Override
public void onFailure(Call<MovieResult> call, Throwable t) {
movieData.setValue(null);
Log.d("onfailure","why");
}
});
return movieData;
}
}
RetrofitInstance:
public class RetrofitInstance {
private static Retrofit retrofit;
private static final String BASE_URL = "http://api.themoviedb.org/3/";
public static Retrofit getRetrofitInstance(){
if(retrofit == null){
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
GetMovieService.Java
public interface GetMovieService {
#GET("movie/popular")
Call<MovieResult> getMovieResult(#Query("api_key") String apiKey);
}
logical:
log output
I am developing an application with the help of Model View Presenter pattern.
I make use of Retrofit and so I have a ApiClient and ApiInterface with endpoints. I implement the interface in a RemoteDataSource class which I call in the Repository class.
My questions is - how do I make use of an Interactor class to make the repository communicate with the Presenter?
Here is my code until now:
ApiInterface
public interface ApiInterface {
#GET("?")
Call<ArrayList<Movie>> getMoviesByTitle(#Query("t") String title,#Query("apiKey") String apiKey);
}
RemoteDataSource class
private static MovieRemoteDataSource instance;
private final ApiInterface service;
public MovieRemoteDataSource(ApiInterface movieApi) {
service = ApiClient.createService(ApiInterface.class);
}
public static MovieRemoteDataSource getInstance(ApiInterface movieApi) {
if (instance == null) {
instance = new MovieRemoteDataSource(movieApi);
}
return instance;
}
#Override
public void getMovies(String title, String apiKey, final LoadMovieCallBack callback) {
service.getMoviesByTitle(title,apiKey).enqueue(new Callback<ArrayList<Movie>>() {
#Override
public void onResponse(Call<ArrayList<Movie>> call, Response<ArrayList<Movie>> response) {
ArrayList<Movie> movies = response.body();// != null ? //response.body().getTitle() : null;
if (movies != null && !movies.isEmpty()) {
callback.onMoviesLoaded(movies);
} else {
callback.onDataNotAvailable();
}
}
#Override
public void onFailure(Call<ArrayList<Movie>> call, Throwable t) {
callback.onError();
}
});
}
DataSource interface with a callback
public interface MovieDataSource {
interface LoadMovieCallBack{
void onMoviesLoaded(ArrayList<Movie> movies);
void onDataNotAvailable();
void onError();
}
void getMovies(String title, String apiKey,LoadMovieCallBack callback);
}
Repository
private MovieRemoteDataSource movieRemoteDataSource;
public MoviesRepository() {//ApiInterface movieApi) {
//this.service = ApiClient.createService(ApiInterface.class);
}
public static MoviesRepository getInstance(ApiInterface service) {
if (instance == null) {
instance = new MoviesRepository();
}
return instance;
}
public void getMovies(String title, String apiKey ) {
movieRemoteDataSource.getMovies(title,apiKey,this);
}
In MoviesRepository you should declare a function with Callback. Your Presenter
should implement MovieDataSource.LoadMovieCallBack and pass it when you call MoviesRepository
public void getMovies(String title, String apiKey,MovieDataSource.LoadMovieCallBack callback) {
movieRemoteDataSource.getMovies(title,apiKey,callback);
}
Here is Google MVP already done for todo app sample, you can refer it. But now it deprecated because Google recommends MVVM
I'm sending a simple get method to my server and get the result using RxJava and Retrofit. The thing that I did is:
Interface
public interface Posts {
#GET("/typicode/demo/{path}")
Observable<List<Beans>> getPosts(#Path("path") String path);
}
RetrofitInstance
public static Retrofit getInstance() {
if (retrofit == null) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
.client(client)
.build();
}
return retrofit;
}
MainActivity
Observer<List<Beans>> observer = new Observer<List<Beans>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(List<Beans> value) {
Log.d("Saket", value.toString());
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
};
Posts client = RetrofitClientInstance.getInstance().create(Posts.class);
client.getPosts("posts").observeOn(Schedulers.newThread()).subscribeOn(AndroidSchedulers.mainThread()).subscribe(observer);
I am getting correct output using HTTPLoggingInterceptor.
You can use subscribeWith like this
also change subscribe on to --> Schedulers.newThread() or Schedulers.io()
right now you are making network call on UI thread
client.getPosts("posts")
.subscribeOn(Schedulers.newThread()) // change
.observeOn(AndroidSchedulers.mainThread()) // change
.subscribeWith(new Observer<Response<Observable<List<Beans>>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Response<Observable<List<Beans>>> response) {
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
}
});
It works when I use Single Observable and DisposableSingleObserver as an observer.
I have a BehaviorRelay object in ExecutionStream class which handles network calls. Please refer to ExecutionStream class.
I can call requestTrackingAndExecution() method from any activity. I have implemented Dagger2 dependency such that I can inject ExecutionStream instance in any activity.
My dagger2 configuration:
#PerApplication
#Provides
public ExecutionStream provideExecutionStream(PmsApi pmsApi) {
return new ExecutionStream(pmsApi);
}
#PerApplication annotation
#Scope
#Retention(RUNTIME)
public #interface PerApplication { }
WHAT I NEED TO DO:
I want to call requestTrackingAndExecution() method from Activity A and subscribe to its emitted data in Activity B.
Currently, suubscriber in Activity B is not getting any data emitted from activity A<--- SEE HERE
I have injected ExecutionStream class in both activities like #Inject ExecutionStream executionStream;
For emitting observable, I am calling internshipAndTrackingRelay.accept(data); in requestTrackingAndExecution() method after getting data from a network call.
Code for subscribing to relay:
executionStream.internshipAndTracking()
.subscribe(
new Consumer<ExecutionStream.InternshipAndTrackingContainer>() {
#Override
public void accept(ResponseData data){
//do some stuff with responsedata
}
});
My ExecutionStream class:
public class ExecutionStream {
#NonNull private PmsApi pmsApi;
#NonNull private final BehaviorRelay<InternExecutionContainer> internExecutionRelay = BehaviorRelay.create();
#NonNull private final BehaviorRelay<InternshipAndTrackingContainer> internshipAndTrackingRelay = BehaviorRelay.create();
public ExecutionStream(#NonNull PmsApi pmsApi) {
this.pmsApi = pmsApi;
}
#NonNull
public Observable<InternshipAndTrackingContainer> internshipAndTracking() {
return internshipAndTrackingRelay.hide();
}
public void requestTrackingAndExecution(String internshipExecutionId, String internExecutionId) {
// Do some network call
// Get response
internshipAndTrackingRelay.accept(new InternshipAndTrackingContainer(responseData));
}
});
}
/**
* This function returns combined response of both apis
* This returns when both apis are finished calling
* #return Observable response
*/
private BiFunction<
InternshipExecutionResponse,
TrackingDataResponse,
TrackingAndExecution>
getMergingBiFuntionForTrackingAndExecution() {
return new BiFunction<InternshipExecutionResponse, TrackingDataResponse, TrackingAndExecution>() {
#Override
public TrackingAndExecution apply(#io.reactivex.annotations.NonNull InternshipExecutionResponse internshipExecutionResponse, #io.reactivex.annotations.NonNull TrackingDataResponse trackingDataResponse) throws Exception {
return new TrackingAndExecution(internshipExecutionResponse,trackingDataResponse);
}
};
}
public class InternshipAndTrackingContainer {
public boolean isError;
public boolean isEmpty;
public TrackingAndExecution trackingAndExecution;
public InternshipAndTrackingContainer() {
this.isError = true;
this.trackingAndExecution = null;
this.isEmpty = false;
}
public InternshipAndTrackingContainer(TrackingAndExecution trackingAndExecution) {
this.trackingAndExecution = trackingAndExecution;
this.isError = false;
this.isEmpty = false;
}
public InternshipAndTrackingContainer(boolean isEmpty) {
this.trackingAndExecution = null;
this.isError = false;
this.isEmpty = isEmpty;
}
}
}
Finally found a solution.
I was reinitializing my ApplicationModule again and again.
Changed this:
public ApplicationComponent getComponent() {
ApplicationComponent component = DaggerApplicationComponent.builder()
.networkModule(new NetworkModule())
.ApplicationModule(new ApplicationModule(this))
.build();
return component;
}
To this:
public synchronized ApplicationComponent getComponent() {
if(component == null) {
component = DaggerApplicationComponent.builder()
.networkModule(new NetworkModule())
.ApplicationModule(new ApplicationModule(this))
.build();
}
return component;
}
Does Activity B subscribe to the internshipAndTrackingRelay before Activity A runs the requestTrackingAndExecution() method?
I'm new to retrofit and i am trying te get a json response to an object called RootObject. The error that i am stuck with is :
"Error:(21, 44) error: incompatible types: NewsController cannot be
converted to Callback>"
Does someone now my mistake here? thanks in regards!
public class NewsController {
public void getNews(){
Retrofit retrofit = new Retrofit.Builder().baseUrl("apilink").addConverterFactory(GsonConverterFactory.create()).build();
GetNewsService service = retrofit.create(GetNewsService.class);
try {
service.GetNewsItems().enqueue(this); //asynchronous
Response<List<RootObject>> response = service.GetNewsItems().execute(); //synchronous
}
catch (IOException e){
}
}
}
class to put the data:
public class RootObject implements Serializable {
public ArrayList<Result> results ;
public int nextId;
public ArrayList<Result> getResults() { return results; }
public int getNextId() { return nextId; }
public String toString() {
return String.format("JEEJ" + nextId);
}
}
Interface:
public interface GetNewsService {
#GET("/Articles")
Call<List<RootObject>> GetNewsItems();
}
First of all,
change your interface to this:
public interface GetNewsService {
#GET("/Articles")
void GetNewsItems(Callback<List<RootObject>> cb);
}
Also change your newsController class.
public class NewsController {
private RestAdapter restAdapter;
static final String API_URL = "[Enter your API base url here]";
public void getNews(){
OkHttpClient mOkHttpClient = new OkHttpClient();
mOkHttpClient.setConnectTimeout(15000,TimeUnit.MILLISECONDS);
mOkHttpClient.setReadTimeout(15000,TimeUnit.MILLISECONDS);
restAdapter = new RestAdapter.Builder().setEndpoint(API_URL).setClient(new OkClient(mOkHttpClient)).setLogLevel(RestAdapter.LogLevel.FULL) .build();
GetNewsService service = restAdapter.create(GetNewsService.class);
Callback<List<RootObject> cb = new Callback<List<RootObject>>() {
#Override
public void success(List<RootObject> rootObjectList, Response response) {
//whatever you want to do with the fetched news items
}
#Override
public void failure(RetrofitError error) {
//whatever you want to do with the error
}
};
service.GetNewsItems(cb);
}
}
You'll need to add the following dependencies in your build.gradle:
compile 'com.squareup.retrofit:retrofit:1.9.0'
compile 'com.google.code.gson:gson:2.3.1'
compile 'com.squareup.okhttp:okhttp:2.4.0'
#megh vidani's answer works, but he had you switch your code from Retrofit 2 to Retrofit 1. Here is how to do it in Retrofit 2. You would need to go back to your original gradle settings, etc. --
public class NewsController {
public void getNews(){
Retrofit retrofit = new Retrofit.Builder().baseUrl("apilink").addConverterFactory(GsonConverterFactory.create()).build();
GetNewsService service = retrofit.create(GetNewsService.class);
service.GetNewsItems().enqueue(new Callback<List<RootObject>>() {
#Override
public void onResponse(Response<List<RootObject>> response) {
// Handle your response
// Note HTTP errors are delivered here, you can check
// response.isSuccess() or response.code() to determine
// HTTP failures
}
#Override
public void onFailure(Throwable t) {
// Network errors
}
});
}
}