Android retrofit parsing JSON Expected BEGIN_OBJECT but was BEGIN_ARRAY - android

I'm trying to use retrofit 2 to parse a json array :
[{"question":"question 1"},{"question":"question 2"}]
Here is the main activity of my project :
package maxime.mysqltest;
import android.graphics.Movie;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private final static int idForm = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Questionable questionService =
ApiClient.getClient().create(Questionable.class);
Call<QuestionList> call = questionService.getQuestions(idForm);
call.enqueue(new Callback<QuestionList>() {
#Override
public void onResponse(Call<QuestionList>call, Response<QuestionList> response) {
List<Question> questions = response.body().getQuestions();
Log.d(TAG, "Number of questions received: " + questions.size());
}
#Override
public void onFailure(Call<QuestionList>call, Throwable t) {
// Log error here since request failed
Log.e(TAG, t.toString());
}
});
}
}
I created a class to access my page on the MAMP server :
public class ApiClient {
public static final String BASE_URL = "http://*myIP*:8888";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
I also have two classes for the object that I try to parse :
public class Question {
private final String question;
public Question(String question)
{
this.question=question;
}
}
public class QuestionList {
private List<Question> questions = new ArrayList<>();
public QuestionList(List<Question> questions)
{
this.questions=questions;
}
public List<Question> getQuestions() {
return questions;
}
}
and finally my interface :
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface Questionable {
#GET("/takeQuestions.php")
Call<QuestionList> getQuestions(#Query("idForm") int idForm);
}
When I run the application the following error appeared :
E/MainActivity: com.google.gson.JsonSyntaxException:
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was
BEGIN_ARRAY at line 1 column 2 path $
I searched on the internet but I can't find a solution to my problem...

Gson expected an Object but it received an Array.
This is a plain json array with 2 objects:
[{"question":"question 1"},{"question":"question 2"}]
This is an object that contains a list questions that has the questions in it.
public class QuestionList {
private List<Question> questions = new ArrayList<>();
}
Which would look like:
{
"questions" : [{"question":"question 1"},{"question":"question 2"}]
}
So when you tell retrofit that you expect a Call<QuestionList> Gson expects the above Json, which is not the same as the one on top. It is a list "wrapped" inside an object.
Your call object should look like this:
Call<Question[]> getQuestions()
// or
Call<List<Question>> getQuestions()
either of which is an actual list / array of Question.

Related

TMDB api trending endpoint call returns a null object

I am making an api call to themoviedb.org api in android using retrofit. I am expecting a json array with a list of movies in the response, however i get a null object reference when i call the getResults method of my POJO class. Kindly assist.
This is the error message i am getting;
2020-01-03 12:28:10.396 20851-20851/com.popularmovies E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.popularmovies, PID: 20851
java.lang.NullPointerException: Attempt to invoke virtual method 'com.google.gson.JsonArray com.popularmovies.TrendingPojo.getResults()' on a null object reference
at com.popularmovies.TrendingFragment$1.onResponse(TrendingFragment.java:109)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:71)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7045)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:964)
Here is my api interface
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Path;
import retrofit2.http.Query;
public interface TmdbApi {
final String BASE_URL = "https://api.themoviedb.org/4/";
#GET("list/{id}")
Call<CategoryJsonResponse> getMovies(#Path ("id") String id, #Query("api_key") String api_key);
#GET("discover/movie")
Call<DiscoverPOJO> getDiscover(#Query("sort_by") String sort_by,#Query("api_key") String api_key);
#GET("trending/{media_type}/{time_window}")
Call<TrendingPojo> getTrending(#Path("media_type") String media_type, #Path("time_window") String time_window, #Query("api_key") String api_key);
}
my problem is with the trending endpoint.
Below is my POJO class
package com.popularmovies;
import com.google.gson.JsonArray;
public class TrendingPojo {
private int page;
private JsonArray results;
private int total_results;
private int total_pages;
//Default constructor for api calls
public TrendingPojo(int page, JsonArray results, int total_pages, int total_results) {
this.page = page;
this.results = results;
this.total_results = total_results;
this.total_pages = total_pages;
}
public int getPage() {
return page;
}
public JsonArray getResults() {
return results;
}
public int getTotal_results() {
return total_results;
}
public int getTotal_pages() {
return total_pages;
}
public void setPage(int page) {
this.page = page;
}
public void setResults(JsonArray results) {
this.results = results;
}
public void setTotal_results(int total_results) {
this.total_results = total_results;
}
public void setTotal_pages(int total_pages) {
this.total_pages = total_pages;
}
}
Below is the fragment from which i make the api call using retrofit;
package com.popularmovies;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import static com.popularmovies.DiscoverFragment.API_KEY;
public class TrendingFragment extends Fragment implements MovieAdapter.MovieAdapterOnclickHandler {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private View rootView;
private ProgressBar progressBar;
private RecyclerView mRecyclerView;
private MovieAdapter movieAdapter;
private List<Movies> moviez;
public TrendingFragment() {
// Required empty public constructor
}
// TODO: Rename and change types and number of parameters
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_trending, container, false);
progressBar = rootView.findViewById(R.id.trending_pb);
progressBar.setVisibility(View.VISIBLE);
mRecyclerView = rootView.findViewById(R.id.trending_rv);
GridLayoutManager layoutManager = new GridLayoutManager(rootView.getContext(), 2);
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setHasFixedSize(true);
movieAdapter = new MovieAdapter(this);
mRecyclerView.setAdapter(movieAdapter);
getTrendingResponse();
return rootView;
}
// TODO: Rename method, update argument and hook method into UI event
private void getTrendingResponse(){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(TmdbApi.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
TmdbApi tmdbApi = retrofit.create(TmdbApi.class);
Call<TrendingPojo> call = tmdbApi.getTrending("movie","week",API_KEY);
call.enqueue(new Callback<TrendingPojo>() {
#Override
public void onResponse(Call<TrendingPojo> call, Response<TrendingPojo> response) {
TrendingPojo movies = response.body();
JsonArray allmovies;
allmovies = movies.getResults();
if(allmovies!= null){
for(int i = 0; i < allmovies.size(); i++){
Gson gson = new Gson();
Type listType = new TypeToken<Collection<Movies>>(){}.getType();
moviez = gson.fromJson(allmovies, listType);
}
movieAdapter.setData(moviez);
progressBar.setVisibility(View.INVISIBLE);
}
}
#Override
public void onFailure(Call<TrendingPojo> call, Throwable t) {
Toast.makeText(rootView.getContext(), t.getMessage(), Toast.LENGTH_LONG).show();
}
});
}
#Override
public void onDetach() {
super.onDetach();
}
#Override
public void onClick(int s) {
}
}
Finally here is a link to the api documentation for the trending endpoint.
https://developers.themoviedb.org/3/trending/get-trending
Turns out the problem was I was using a version 4 base URL which returns a 404 error code for some endpoints instead of version 3.
changing
"https://api.themoviedb.org/4/"
to
"https://api.themoviedb.org/3/"
was the solution.

How to fix "java.util.ArrayList cannot be cast to"

I have a problem with transfering data from retrofit to room. I downloaded data from the API but when i try getting data in room database I receive the error :
" Caused by: java.lang.ClassCastException: java.util.ArrayList cannot
be cast to".
Debug shows me on error in
newsDao.insert((News) news);
AND
.subscribe(new Consumer<List<News>>()
Here is my code.
import android.arch.persistence.room.Room;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import java.util.List;
import burtsev.a.k.news.Model.News;
import burtsev.a.k.news.Retrofit.NewsAPI;
import burtsev.a.k.news.Retrofit.RetrofitClient;
import burtsev.a.k.news.Room.NewsDao;
import burtsev.a.k.news.Room.NewsDatabase;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import retrofit2.Retrofit;
public class Splash extends AppCompatActivity {
public static Splash instance;
public NewsDatabase newsDatabase;
NewsAPI newsAPI;
CompositeDisposable compositeDisposable = new CompositeDisposable();
NewsDao newsDao;
public static Splash getInstance() {
return instance;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
newsDatabase = Room.databaseBuilder(getApplicationContext(),
NewsDatabase.class, "database")
.allowMainThreadQueries()
.build();
newsDao = newsDatabase.newsDao();
Retrofit retrofit = RetrofitClient.getInstance();
newsAPI = retrofit.create(NewsAPI.class);
fetchData();
}
private void fetchData() {
compositeDisposable.add(newsAPI.getNews().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<News>>() {
#Override
public void accept(List<News> news) throws Exception {
Intent intent = new Intent(Splash.this, NewsListFragment.class);
// intent.putExtra("news", (Serializable) news);
newsDao.insert((News) news);
startActivity(intent);
}
}));
}
#Override
protected void onStop() {
compositeDisposable.clear();
super.onStop();
}
public NewsDatabase getNewsDatabase() {
return newsDatabase;
}
}
you are trying to insert list but, you are trying to cast it to an object.
so news is not a News model. it is a List<News>. If you want to insert all your news objects. you can try foreach or you can create a new function inside your DAO which will insert all list at once.
foreach;
for(News anews: news){
newsDao.insert(anews);
}
to insert at once. new function inside your DAO class should be;
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAllNews(List<News> news);
in your subscibe you need to use;
newsDao.insertAllNews(news);

Dagger2 Binding Missing

This is the first time i'm using this library, even more i have downloaded the project and implemented with new dependencies. First of all, I'm getting cannot resolve symbol error on DaggerApplicationCompoment. And more details about error is:
error: [Dagger/MissingBinding] #javax.inject.Named("movieDB") retrofit2.Retrofit cannot be provided without an #Provides-annotated method.
#javax.inject.Named("movieDB") retrofit2.Retrofit is injected at
free.movies.freemovies.dagger.modules.HttpClientModule.provideFithubApi(restAdapter)
free.movies.freemovies.data.Api.TheMovieDbAPI is injected at
free.movies.freemovies.ui.main.MainFragment.mDbAPI
free.movies.freemovies.ui.main.MainFragment is injected at
free.movies.freemovies.dagger.components.ApplicationComponent.inject(free.movies.freemovies.ui.main.MainFragment)
This is module class:
package free.movies.freemovies.dagger.modules;
import android.app.Application;
import java.io.File;
import java.util.concurrent.TimeUnit;
import javax.inject.Named;
import dagger.Module;
import dagger.Provides;
import free.movies.freemovies.dagger.AppScope;
import free.movies.freemovies.data.Api.TheMovieDbAPI;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.moshi.MoshiConverterFactory;
/**
* Created by Marcus Gabilheri
*
* #author Marcus Gabilheri
* #version 1.0
* #since 9/4/16.
*/
#Module
public class HttpClientModule {
private static final long DISK_CACHE_SIZE = 50 * 1024 * 1024; // 50MB
public static final String BACKDROP_URL = "http://image.tmdb.org/t/p/w1280";
public static final String POSTER_URL = "http://image.tmdb.org/t/p/w500";
public static final String API_URL = "https://api.themoviedb.org/3/";
public static final String NOW_PLAYING = "movie/now_playing";
public static final String LATEST = "movie/latest";
public static final String POPULAR = "movie/popular";
public static final String TOP_RATED = "movie/top_rated";
public static final String UPCOMING = "movie/upcoming";
public static final String MOVIE = "movie/";
public static final String PERSON = "person/";
public static final String DISCOVER = "discover/movie/";
public static final String SEARCH_MOVIE = "search/movie/";
public static final String TV = "tv/";
#Provides
#AppScope
public OkHttpClient provideOkHttpClient(Application app) {
File cacheDir = new File(app.getCacheDir(), "http");
return new OkHttpClient.Builder()
.readTimeout(1, TimeUnit.MINUTES)
.connectTimeout(1, TimeUnit.MINUTES)
.writeTimeout(1, TimeUnit.MINUTES)
.cache(new okhttp3.Cache(cacheDir, DISK_CACHE_SIZE))
.build();
}
#Provides
#Named("TVDB") // Name is used in case a second Retrofit api is provided.
#AppScope
public Retrofit provideTVDBRestAdapter(MoshiConverterFactory moshiConverterFactory, OkHttpClient okHttpClient) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
okHttpClient = okHttpClient.newBuilder()
.addInterceptor(interceptor)
.build();
return new Retrofit.Builder()
.baseUrl(API_URL)
.client(okHttpClient)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(moshiConverterFactory)
.build();
}
#Provides
public TheMovieDbAPI provideFithubApi(#Named("movieDB") Retrofit restAdapter) {
return restAdapter.create(TheMovieDbAPI.class);
}
#Provides
#AppScope
public MoshiConverterFactory provideMoshiConverterFactory() {
return MoshiConverterFactory.create();
}
}
And fragment where i'm using that module:
package free.movies.freemovies.ui.main;
import android.os.Bundle;
import android.util.SparseArray;
import androidx.core.content.ContextCompat;
import androidx.leanback.app.BrowseSupportFragment;
import androidx.leanback.widget.ArrayObjectAdapter;
import androidx.leanback.widget.HeaderItem;
import androidx.leanback.widget.ListRow;
import androidx.leanback.widget.ListRowPresenter;
import androidx.leanback.widget.OnItemViewSelectedListener;
import androidx.leanback.widget.Presenter;
import androidx.leanback.widget.Row;
import androidx.leanback.widget.RowPresenter;
import javax.inject.Inject;
import free.movies.freemovies.App;
import free.movies.freemovies.Config;
import free.movies.freemovies.R;
import free.movies.freemovies.dagger.modules.HttpClientModule;
import free.movies.freemovies.data.Api.TheMovieDbAPI;
import free.movies.freemovies.data.models.Movie;
import free.movies.freemovies.data.models.MovieResponse;
import free.movies.freemovies.ui.base.GlideBackgroundManager;
import free.movies.freemovies.ui.movies.MoviePresenter;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;
/**
* Created by Marcus Gabilheri
*
* #author Marcus Gabilheri
* #version 1.0
* #since 10/8/16.
*/
public class MainFragment extends BrowseSupportFragment implements OnItemViewSelectedListener {
#Inject
TheMovieDbAPI mDbAPI;
private GlideBackgroundManager mBackgroundManager;
private CompositeDisposable compositeDisposable = new CompositeDisposable();
private static final int NOW_PLAYING = 0;
private static final int TOP_RATED = 1;
private static final int POPULAR = 2;
private static final int UPCOMING = 3;
SparseArray<MovieRow> mRows;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
App.instance().appComponent().inject(this);
// The background manager allows us to manage a dimmed background that does not interfere with the rows
// It is the preferred way to set the background of a fragment
mBackgroundManager = new GlideBackgroundManager(getActivity());
// The brand color will be used as the background for the Headers fragment
setBrandColor(ContextCompat.getColor(getActivity(), R.color.primary_transparent));
setHeadersState(HEADERS_ENABLED);
setHeadersTransitionOnBackEnabled(true);
// The TMDB logo on the right corner. It is necessary to show based on their API usage policy
setBadgeDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.powered_by));
createDataRows();
createRows();
prepareEntranceTransition();
fetchNowPlayingMovies();
fetchTopRatedMovies();
fetchPopularMovies();
fetchUpcomingMovies();
}
/**
* Creates the data rows objects
*/
private void createDataRows() {
mRows = new SparseArray<>();
MoviePresenter moviePresenter = new MoviePresenter();
mRows.put(NOW_PLAYING, new MovieRow()
.setId(NOW_PLAYING)
.setAdapter(new ArrayObjectAdapter(moviePresenter))
.setTitle("Now Playing")
.setPage(1)
);
mRows.put(TOP_RATED, new MovieRow()
.setId(TOP_RATED)
.setAdapter(new ArrayObjectAdapter(moviePresenter))
.setTitle("Top Rated")
.setPage(1)
);
mRows.put(POPULAR, new MovieRow()
.setId(POPULAR)
.setAdapter(new ArrayObjectAdapter(moviePresenter))
.setTitle("Popular")
.setPage(1)
);
mRows.put(UPCOMING, new MovieRow()
.setId(UPCOMING)
.setAdapter(new ArrayObjectAdapter(moviePresenter))
.setTitle("Upcoming")
.setPage(1)
);
}
/**
* Creates the rows and sets up the adapter of the fragment
*/
private void createRows() {
// Creates the RowsAdapter for the Fragment
// The ListRowPresenter tells to render ListRow objects
ArrayObjectAdapter rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
for (int i = 0; i < mRows.size(); i++) {
MovieRow row = mRows.get(i);
// Adds a new ListRow to the adapter. Each row will contain a collection of Movies
// That will be rendered using the MoviePresenter
HeaderItem headerItem = new HeaderItem(row.getId(), row.getTitle());
ListRow listRow = new ListRow(headerItem, row.getAdapter());
rowsAdapter.add(listRow);
}
// Sets this fragments Adapter.
// The setAdapter method is defined in the BrowseFragment of the Leanback Library
setAdapter(rowsAdapter);
setOnItemViewSelectedListener(this);
}
/**
* Fetches now playing movies from TMDB
*/
private void fetchNowPlayingMovies() {
Disposable disposable = mDbAPI.getNowPlayingMovies(Config.API_KEY_URL, mRows.get(NOW_PLAYING).getPage())
.subscribeOn(io.reactivex.schedulers.Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<MovieResponse>() {
#Override
public void accept(MovieResponse response) {
MainFragment.this.bindMovieResponse(response, NOW_PLAYING);
MainFragment.this.startEntranceTransition();
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable e) {
Timber.e(e, "Error fetching now playing movies: %s", e.getMessage());
}
});
compositeDisposable.add(disposable);
}
/**
* Fetches the popular movies from TMDB
*/
private void fetchPopularMovies() {
Disposable disposable = mDbAPI.getPopularMovies(Config.API_KEY_URL, mRows.get(POPULAR).getPage())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<MovieResponse>() {
#Override
public void accept(MovieResponse response) {
MainFragment.this.bindMovieResponse(response, POPULAR);
MainFragment.this.startEntranceTransition();
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable e) {
Timber.e(e, "Error fetching popular movies: %s", e.getMessage());
}
});
compositeDisposable.add(disposable);
}
/**
* Fetches the upcoming movies from TMDB
*/
private void fetchUpcomingMovies() {
Disposable disposable = mDbAPI.getUpcomingMovies(Config.API_KEY_URL, mRows.get(UPCOMING).getPage())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<MovieResponse>() {
#Override
public void accept(MovieResponse response) {
MainFragment.this.bindMovieResponse(response, UPCOMING);
MainFragment.this.startEntranceTransition();
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable e) {
Timber.e(e, "Error fetching upcoming movies: %s", e.getMessage());
}
});
compositeDisposable.add(disposable);
}
/**
* Fetches the top rated movies from TMDB
*/
private void fetchTopRatedMovies() {
Disposable disposable = mDbAPI.getTopRatedMovies(Config.API_KEY_URL, mRows.get(TOP_RATED).getPage())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<MovieResponse>() {
#Override
public void accept(MovieResponse response) {
MainFragment.this.bindMovieResponse(response, TOP_RATED);
MainFragment.this.startEntranceTransition();
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable e) {
Timber.e(e, "Error fetching top rated movies: %s", e.getMessage());
}
});
compositeDisposable.add(disposable);
}
/**
* Binds a movie response to it's adapter
* #param response
* The response from TMDB API
* #param id
* The ID / position of the row
*/
private void bindMovieResponse(MovieResponse response, int id) {
MovieRow row = mRows.get(id);
row.setPage(row.getPage() + 1);
for(Movie m : response.getResults()) {
if (m.getPosterPath() != null) { // Avoid showing movie without posters
row.getAdapter().add(m);
}
}
}
#Override
public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) {
// Check if the item is a movie
if (item instanceof Movie) {
Movie movie = (Movie) item;
// Check if the movie has a backdrop
if(movie.getBackdropPath() != null) {
mBackgroundManager.loadImage(HttpClientModule.BACKDROP_URL + movie.getBackdropPath());
} else {
// If there is no backdrop for the movie we just use a default one
mBackgroundManager.setBackground(ContextCompat.getDrawable(getActivity(), R.drawable.material_bg));
}
}
}
#Override
public void onDestroy() {
super.onDestroy();
compositeDisposable.dispose();
}
}
I saw people had similar problem, but I don't understand what should I do to solve this. If someone could explain me or give me a hint what should I do.
After reviewing the commit that #peterwhy referenced. It seems as if they forgot to update the following dependency in the same HttpClientModule class as well.
To repair, change the following:
#Provides
public TheMovieDbAPI provideFithubApi(#Named("movieDB") Retrofit restAdapter) {
return restAdapter.create(TheMovieDbAPI.class);
}
to
#Provides
public TheMovieDbAPI provideFithubApi(#Named("TVDB") Retrofit restAdapter) {
return restAdapter.create(TheMovieDbAPI.class);
}
in the /dagger/modules/HttpClientModule.java file.
The error message states that there is no module that provides #Named("movieDB") Retrofit restAdapter for your
#Inject
TheMovieDbAPI mDbAPI;
to be injected since this is #Named("movieDB") Retrofit restAdapter it's dependency

Retrofit onResponse not error even operation occured

Even though every thing is working properly and registration is happening. Retrofit on Response don't get called but onFailure gets called. Call.isexecutted returns true.
I am showing the model class and registrationFragment where the error occured. This is taking a lot time. So thanks in advance for help
RegistrationFragment.java
package com.example.milan.hospital;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
/**
* A simple {#link Fragment} subclass.
*/
public class RegistrationFragment extends Fragment {
private EditText Name,UserName, UserPassword;
private Button BnRegister;
public RegistrationFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_registration, container, false);
Name = view.findViewById(R.id.txt_name);
UserName = view.findViewById(R.id.txt_user_name);
UserPassword = view.findViewById(R.id.txt_password);
BnRegister = view.findViewById(R.id.bn_register);
BnRegister.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
performRegistration();
}
});
return view;
}
public void performRegistration()
{
String name = Name.getText().toString();
String username = UserName.getText().toString();
String password = UserPassword.getText().toString();
Call<User> call = MainActivity.apiInterface.performRegistration(name,username,password);
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
if(response.body().getResponse().equals("ok"))
{
MainActivity.prefConfig.displayToast("Registration success...");
}
else if(response.body().getResponse().equals("exist"))
{
MainActivity.prefConfig.displayToast("User already exist....");
}
else if(response.body().getResponse().equals("error"))
{
MainActivity.prefConfig.displayToast("Something went wrong...");
}
}
#Override
public void onFailure(Call<User> call, Throwable t) {
}
});
Name.setText("");
UserPassword.setText("");
UserName.setText("");
}
}
User.java
package com.example.milan.hospital;
import com.google.gson.annotations.SerializedName;
public class User {
#SerializedName("response")
private String Response;
#SerializedName("name")
private String Name;
public String getResponse() {
return Response;
}
public String getName() {
return Name;
}
}
MainActivity.java
package com.example.milan.hospital;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity implements LoginFragment.OnLoginFormActivityListener{
public static PrefConfig prefConfig;
public static ApiInterface apiInterface;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
prefConfig = new PrefConfig(this);
apiInterface = ApiClient.getApiClient().create(ApiInterface.class);
if(findViewById(R.id.fragment_container) != null)
{
if(savedInstanceState != null)
{
return;
}
if(prefConfig.readLoginStatus())
{
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container,new WelcomeFragment()).commit();
}
else
{
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container,new LoginFragment()).commit();
}
}
}
#Override
public void performRegister() {
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
new RegistrationFragment()).addToBackStack(null).commit();
}
#Override
public void performLogin(String name) {
}
}
ApiInterface.java
package com.example.milan.hospital;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface ApiInterface
{
#GET("register.php")
Call<User> performRegistration(#Query("name") String Name,#Query("user_name") String UserName,#Query("user_password") String UserPassword);
#GET("login.php")
Call<User> performUserLogin(#Query("user_name") String UserName,#Query("user_password") String UserPassword);
}
I think performRegistration() in ApiInterface.java should have an annotation of #POST rather than #GET since registration is the act of posting data rather than getting. Give it a try once.
change the ApiClient to this. As there was a well known problem of retrofit while reading json. to resolve we have initialize gson object ourself
public class ApiClient
{
public static final String BASE_URL = "http://10.0.3.2/loginapp/";
public static Retrofit retrofit = null;
public static Retrofit getApiClient()
{
Gson gson = new GsonBuilder()
.setLenient()
.create();
if(retrofit == null)
{
retrofit = new Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create(gson)).build();
}
return retrofit;
}
}

Fetching Json from URL - null object reference [duplicate]

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 5 years ago.
I am trying to fetch an array from the data.police.uk website.
Unfortunately, when running the code an error comes up: java.lang.NullPointerException: Attempt to invoke interface method 'int java.util.List.size()' on a null object reference
Here's the code, no idea, what I am doing wrong:
package com.example.cosmin.crimerate.Latest_crimes;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import com.example.cosmin.crimerate.R;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class JsonCrimesActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_json_crimes);
final ListView listView = findViewById(R.id.crimesList);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Api.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
Api api = retrofit.create(Api.class);
Call<List<Crime>> call = api.getCrimes();
call.enqueue(new Callback<List<Crime>>() {
#Override
public void onResponse(Call<List<Crime>> call, Response<List<Crime>> response) {
List<Crime> Crimes = response.body();
String[] crimeCategories = new String[Crimes.size()];
for (int i=0; i < Crimes.size();i++){
crimeCategories[i]= Crimes.get(i).getCategory();
}
listView.setAdapter(
new ArrayAdapter<String>(
getApplicationContext(),
android.R.layout.simple_list_item_1,
crimeCategories
)
);
}
#Override
public void onFailure(Call<List<Crime>> call, Throwable t) {
Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
}
That's the Api class:
public interface Api{
String BASE_URL = "https://data.police.uk/api/";
#GET("/crimes-no-location?category=all-crime&force=leicestershire&date=2017-02")
Call<List<Crime>> getCrimes();
Do you think it can be because of the array?
If you can please me give me a hint or anything what's happening.
Thank you for your help!
LE: CRIME MODEL:
public class Crime {
private String category;
private String persistent_id;
private String location_subtype;
private String id;
private String location;
private String context;
private String month;
private String location_type;
private String outcome_status;
public Crime(String category, String persistent_id, String location_subtype, String id, String location, String context, String month, String location_type, String outcome_status) {
this.category = category;
this.persistent_id = persistent_id;
this.location_subtype = location_subtype;
this.id = id;
this.location = location;
this.context = context;
this.month = month;
this.location_type = location_type;
this.outcome_status = outcome_status;
}
public String getCategory() {
return category;
}
public String getPersistent_id() {
return persistent_id;
}
public String getLocation_subtype() {
return location_subtype;
}
public String getId() {
return id;
}
public String getLocation() {
return location;
}
public String getContext() {
return context;
}
public String getMonth() {
return month;
}
public String getLocation_type() {
return location_type;
}
public String getOutcome_status() {
return outcome_status;
}
}
Make sure this piece of code if working fine.
List<Crime> Crimes = response.body();
Your Crimes List is probably empty or null.
Your response.body() is returning null. I'm pretty sure it's because you misconfigured your Api. As is, you're making an http call to the following uri (notice the double //):
https://data.police.uk/api//crimes-no-location?category=all-crime&force=leicestershire&date=2017-02
Remove the leading slash in your #GET

Categories

Resources