-1

I am trying to build an application that is supposed to use an open source sport API to get a list of football (Soccer) tournaments and display it on a RecyclerView, for some reason I am getting two errors:

1- com.mad.footstats E/RecyclerView: No adapter attached; skipping layout

2- E/MainActivity: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $

I couldn't make it work from previous answers on the same questions so please if you can take the time and look at my code: This is the MainActivity:

public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private final static String API_KEY = "w7c74newrykj8m57rda6xwrk";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // Toast if the API key is empty
    if(API_KEY .isEmpty()){
        Toast.makeText(getApplicationContext(), R.string.api_empty_toast, Toast.LENGTH_SHORT).show();
    }

    final RecyclerView mRecyclerView = findViewById(R.id.tournaments_rv);
    mRecyclerView.setLayoutManager(new LinearLayoutManager(this));

    ApiInterface apiService =
            ApiClient.getClient().create(ApiInterface.class);
    Call<TournamentResponse> call = apiService.getTournamentList(API_KEY);
    call.enqueue(new Callback<TournamentResponse>() {
        @Override
        public void onResponse(Call<TournamentResponse> call, Response<TournamentResponse> response) {
            int statusCode = response.code();
            List<Tournament_> tournaments = response.body().getResults();
            mRecyclerView.setAdapter(new TournamentsAdapter(tournaments,R.layout.tournament_item,getApplicationContext()));
        }

        @Override
        public void onFailure(Call<TournamentResponse> call, Throwable t) {
            Log.e(TAG, t.toString());
        }
    });
}

This is the code for the TournamentsAdapter:

public class TournamentsAdapter extends RecyclerView.Adapter<TournamentsAdapter.LeagueViewHolder>{

private List<Tournament_> mTournaments;
private int rowLayout;
private Context context;

public static class LeagueViewHolder extends RecyclerView.ViewHolder {
    LinearLayout TournamentLayout;
    TextView tournamentName, tournamentNation, tournamentYear;

    public LeagueViewHolder(View v) {
        super(v);
        TournamentLayout = v.findViewById(R.id.league_layout);
        tournamentName = v.findViewById(R.id.tournament_name);
        tournamentNation = v.findViewById(R.id.tournament_nation);
        tournamentYear = v.findViewById(R.id.tournament_year);
    }
}

public TournamentsAdapter(List<Tournament_> tournaments, int rowLayout, Context context){
    this.mTournaments = tournaments;
    this.rowLayout = rowLayout;
    this.context = context;
}

@Override
public TournamentsAdapter.LeagueViewHolder onCreateViewHolder(ViewGroup parent,
                                                              int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(rowLayout, parent, false);
    return new LeagueViewHolder(view);
}

@Override
public void onBindViewHolder(LeagueViewHolder holder, final int position){
    holder.tournamentName.setText(mTournaments.get(position).getName());
    holder.tournamentNation.setText(mTournaments.get(position).getCurrentSeason().getYear());
    holder.tournamentYear.setText(mTournaments.get(position).getCategory().getName());
}

@Override
public int getItemCount() {
    return mTournaments.size();
}

And this is the code for the ApiClient Class:

public class ApiClient {

public static final String BASE_URL = "https:api.sportradar.us/soccer-xt3/eu/en/";
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;
}

For some reason the Adapter isn't attached and the API isn't working even thought I saw examples and tutorials having the same code. Thanks.

ADM
  • 20,406
  • 11
  • 52
  • 83
Obi
  • 89
  • 8
  • did you try to put a debugger inside OnResponse? – Zaid Mirza May 13 '18 at 05:45
  • @ZaidMirza as a beginner I tried but couldn't work it out still.. – Obi May 13 '18 at 05:53
  • Your link is not responding! i tried to check your endpoint and i got this error `

    Developer Inactive

    ` . Thats is why you get that xml error and because you do not get response your recycler view adapter would not be set.
    – Omid Heshmatinia May 13 '18 at 05:58
  • 1
    Possible duplicate of [MalformedJsonException with Retrofit API?](https://stackoverflow.com/questions/27485346/malformedjsonexception-with-retrofit-api) – Khemraj Sharma May 13 '18 at 05:59
  • @OmidHeshmatinia that is the base URL :/ in the ApiInterface class I have a get method that adds: `@GET("tournaments.xml") Call getTournamentList(@Query("api_key") String apiKey);` So the final link will be: https://api.sportradar.us/soccer-xt3/eu/en/tournaments.xml?api_key=(API_KEY) – Obi May 13 '18 at 06:05
  • Possible duplicate of [recyclerview No adapter attached; skipping layout](https://stackoverflow.com/questions/29141729/recyclerview-no-adapter-attached-skipping-layout) – EdChum May 13 '18 at 19:14

3 Answers3

0

Your adapter has to be defined when asynchronous operation is done. Move line:

mRecyclerView.setLayoutManager(new LinearLayoutManager(this));

to asynchronous response as below:

call.enqueue(new Callback<TournamentResponse>() {
        @Override
        public void onResponse(Call<TournamentResponse> call, Response<TournamentResponse> response) {
            int statusCode = response.code();
            List<Tournament_> tournaments = response.body().getResults();
            mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
            mRecyclerView.setAdapter(new TournamentsAdapter(tournaments,R.layout.tournament_item,getApplicationContext()));
        }

        @Override
        public void onFailure(Call<TournamentResponse> call, Throwable t) {
            Log.e(TAG, t.toString());
        }
    });
HaroldSer
  • 2,025
  • 2
  • 12
  • 23
  • Is the asynchronous request entering onResponse or onFailure? Can you put a Log.i("onResponse: ", "success"); within onResponse(...) to check whether the request is successful. – HaroldSer May 13 '18 at 06:13
  • Yes it is returning this error: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $ – Obi May 13 '18 at 06:17
0

com.mad.footstats E/RecyclerView: No adapter attached; skipping layout

I want to make you clear that it is not an error, it is just a warning. That occurs when you don't set an adapter in onCreate(). If you set adapter later when api response come, then this warning comes. You can ignore this. If you are setting adapter later.

If you want resolve you should follow correct way to set an RecycleView.

(1) Initialize the RecyclerView & bind adapter in onCreate()

List<Tournament_> tournaments;
TournamentsAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    .....
    RecyclerView mRecycler = (RecyclerView) this.findViewById(R.id.yourid);
    adapter = new TournamentsAdapter(tournaments,R.layout.tournament_item,getApplicationContext());
    mRecycler.setAdapter(adapter);
}

(2) call notifyDataStateChanged when you get the data

 tournaments = response.body().getResults();
 adapter.notifyDataStateChanged();

or you can make an setList() method in your adapter class like below.

public void setList(ArrayList<Tournament_> list) {
    if (list == null) return;
    this.list = list;
    notifyDataSetChanged();
}

and call it like

 adapter.setList(response.body().getResults());

In the dispatchLayout(), we can find there is the error in it:

void dispatchLayout() {
    if(this.mAdapter == null) {
        Log.e("RecyclerView", "No adapter attached; skipping layout");
    } else if(this.mLayout == null) {
        Log.e("RecyclerView", "No layout manager attached; skipping layout");
    } else {
Khemraj Sharma
  • 57,232
  • 27
  • 203
  • 212
0

The problem is with your link.You choose the wrong parameter for your endpoint. You will never get a json response so retrofit can not parse that for you And you are filling your recycler view in the response of retrofit which never called.

just change xml to json to get the right response.

https://api.sportradar.us/soccer-xt3/eu/en/tournaments.xml?api_key=API_KEY

to

https://api.sportradar.us/soccer-xt3/eu/en/tournaments.json?api_key=API_KEY

Omid Heshmatinia
  • 5,089
  • 2
  • 35
  • 50
  • I did notice that after I posted the question and I fixed it but still for some reason it is not working – Obi May 13 '18 at 07:27