Estoy trabajando en la aplicación de Android que obtiene datos usando RecyclerView y Retrofit para analizar JSON Url. Tuve el siguiente tutorial sobre este Github

Mi proyecto en Android Studio no tiene errores y puedo ejecutar la aplicación. Pero cuando abro MainActivity se bloquea.

Error que recibo

java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0

Esta es la siguiente línea está en setUpAndLoadNativeExpressAds

final NativeExpressAdView adView = (NativeExpressAdView) mRecyclerViewItems.get(i);

Esto es actividad principal

    public class MainActivity extends AppCompatActivity{

    public static final int ITEMS_PER_AD = 3;
    private static final int NATIVE_EXPRESS_AD_HEIGHT = 150;
    private static final String AD_UNIT_ID = ADMOB_NATIVE_MENU_ID;

    private StartAppAd startAppAd = new StartAppAd(this);
    private RecyclerView mRecyclerView;
    private List<Object> mRecyclerViewItems = new ArrayList<>();
    private KontenAdapter kontenAdapter;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);

        // Use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView.
        mRecyclerView.setHasFixedSize(true);

        // Specify a linear layout manager.
        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(layoutManager);

//        mRecyclerViewItems = new ArrayList<>();

        // Update the RecyclerView item's list with menu items and Native Express ads.
//        addMenuItemsFromJson();
        loadJSON();
//        initData();
        setUpAndLoadNativeExpressAds();

    }



    /**
     * Adds Native Express ads to the items list.
     */
    private void addNativeExpressAds() {

        // Loop through the items array and place a new Native Express ad in every ith position in
        // the items List.
        for (int i = 0; i <= mRecyclerViewItems.size(); i += ITEMS_PER_AD) {
            final NativeExpressAdView adView = new NativeExpressAdView(MainActivity.this);
            mRecyclerViewItems.add(i, adView);
        }
    }

    /**
     * Sets up and loads the Native Express ads.
     */
    private void setUpAndLoadNativeExpressAds() {
        mRecyclerView.post(new Runnable() {
            @Override
            public void run() {
                final float scale = MainActivity.this.getResources().getDisplayMetrics().density;
                // Set the ad size and ad unit ID for each Native Express ad in the items list.
                for (int i = 0; i <= mRecyclerViewItems.size(); i += ITEMS_PER_AD) {
                    final NativeExpressAdView adView = (NativeExpressAdView) mRecyclerViewItems.get(i);
                    final CardView cardView = (CardView) findViewById(R.id.ad_card_view);
                    final int adWidth = cardView.getWidth() - cardView.getPaddingLeft()
                            - cardView.getPaddingRight();
                    AdSize adSize = new AdSize((int) (adWidth / scale), NATIVE_EXPRESS_AD_HEIGHT);
                    adView.setAdSize(adSize);
                    adView.setAdUnitId(AD_UNIT_ID);
                }

                // Load the first Native Express ad in the items list.
                loadNativeExpressAd(0);
            }
        });
    }

    /**
     * Loads the Native Express ads in the items list.
     */
    private void loadNativeExpressAd(final int index) {

        if (index >= mRecyclerViewItems.size()) {
            return;
        }

        Object item = mRecyclerViewItems.get(index);
        if (!(item instanceof NativeExpressAdView)) {
            throw new ClassCastException("Expected item at index " + index + " to be a Native"
                    + " Express ad.");
        }

        final NativeExpressAdView adView = (NativeExpressAdView) item;

        // Set an AdListener on the NativeExpressAdView to wait for the previous Native Express ad
        // to finish loading before loading the next ad in the items list.
        adView.setAdListener(new AdListener() {
            @Override
            public void onAdLoaded() {
                super.onAdLoaded();
                // The previous Native Express ad loaded successfully, call this method again to
                // load the next ad in the items list.
                loadNativeExpressAd(index + ITEMS_PER_AD);
            }

            @Override
            public void onAdFailedToLoad(int errorCode) {
                // The previous Native Express ad failed to load. Call this method again to load
                // the next ad in the items list.
                Log.e("MainActivity", "The previous Native Express ad failed to load. Attempting to"
                        + " load the next Native Express ad in the items list.");
                loadNativeExpressAd(index + ITEMS_PER_AD);
            }
        });

        // Load the Native Express ad.
        adView.loadAd(new AdRequest.Builder().build());
    }


    private void loadJSON() {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.myjson.com/") //https://api.myjson.com/bins/v4dzd
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        RequestInterface request = retrofit.create(RequestInterface.class);
        Call<JSONResponse> call = request.getJSON();
        call.enqueue(new Callback<JSONResponse>() {
            @Override
            public void onResponse(Call<JSONResponse> call, Response<JSONResponse> response) {
                JSONResponse jsonResponse = response.body();

                mRecyclerViewItems = new ArrayList<Object>(Arrays.asList(jsonResponse.getKonten()));
                addNativeExpressAds();

                RecyclerView.Adapter adapter = new KontenAdapter(mRecyclerViewItems, getApplicationContext());
                mRecyclerView.setAdapter(adapter);
            }

            @Override
            public void onFailure(Call<JSONResponse> call, Throwable t) {
                Log.d("Error", t.getMessage());
            }
        });

    }

    /**
     * Adds {@link KontenItem}'s from a JSON file.
     */
    private void addMenuItemsFromJson() {
        try {
            String jsonDataString = readJsonDataFromFile();
            JSONArray menuItemsJsonArray = new JSONArray(jsonDataString);

            for (int i = 0; i < menuItemsJsonArray.length(); ++i) {

                JSONObject menuItemObject = menuItemsJsonArray.getJSONObject(i);

                String menuItemName = menuItemObject.getString("name");
                String menuItemDescription = menuItemObject.getString("description");
                String menuItemPrice = menuItemObject.getString("price");
                String menuItemCategory = menuItemObject.getString("category");
                String menuItemImageName = menuItemObject.getString("photo");
                String menuItemUrl = menuItemObject.getString("url");

                KontenItem kontenItem = new KontenItem(menuItemName, menuItemDescription, menuItemPrice,
                        menuItemCategory, menuItemImageName, menuItemUrl);
                mRecyclerViewItems.add(kontenItem);
            }
        } catch (IOException | JSONException exception) {
            Log.e(MainActivity.class.getName(), "Unable to parse JSON file.", exception);
        }
    }

    /**
     * Reads the JSON file and converts the JSON data to a {@link String}.
     *
     * @return A {@link String} representation of the JSON data.
     * @throws IOException if unable to read the JSON file.
     */
    private String readJsonDataFromFile() throws IOException {

        InputStream inputStream = null;
        StringBuilder builder = new StringBuilder();

        try {
            String jsonDataString = null;
            inputStream = getResources().openRawResource(R.raw.menu_items_json);
            BufferedReader bufferedReader = new BufferedReader(
                    new InputStreamReader(inputStream, "UTF-8"));
            while ((jsonDataString = bufferedReader.readLine()) != null) {
                builder.append(jsonDataString);
            }
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }

        return new String(builder);
    }



}

Este es MyAdapter

    public class KontenAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    // A menu item view type.
    private static final int MENU_ITEM_VIEW_TYPE = 0;

    // The Native Express ad view type.
    private static final int NATIVE_EXPRESS_AD_VIEW_TYPE = 1;

    // An Activity's Context.
    private final Context mContext;

    // The list of Native Express ads and menu items.
    private final List<Object> mRecyclerViewItems;


    public KontenAdapter(List<Object> recyclerViewItems, Context context) {
        this.mContext = context;
        this.mRecyclerViewItems = recyclerViewItems;
    }

    /**
     * The {@link MenuItemViewHolder} class.
     * Provides a reference to each view in the menu item view.
     */
    public class MenuItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
        private TextView menuItemName;
        private TextView menuItemDescription;
        private TextView menuItemPrice;
        private TextView menuItemCategory;
        private ImageView menuItemImage;
        private TextView menuItemUrl;

        MenuItemViewHolder(View view) {
            super(view);
            menuItemImage = (ImageView) view.findViewById(R.id.menu_item_image);
            menuItemName = (TextView) view.findViewById(R.id.menu_item_name);
            menuItemPrice = (TextView) view.findViewById(R.id.menu_item_price);
            menuItemCategory = (TextView) view.findViewById(R.id.menu_item_category);
            menuItemDescription = (TextView) view.findViewById(R.id.menu_item_description);
            menuItemUrl = (TextView) view.findViewById(R.id.menu_item_url);

            view.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {

            Intent detailIntent = new Intent(v.getContext(), PostActivity.class);
            detailIntent.putExtra("name",menuItemName.getText().toString() );
            detailIntent.putExtra("url", menuItemUrl.getText().toString());

            mContext.startActivity(detailIntent);

        }
    }

    /**
     * The {@link NativeExpressAdViewHolder} class.
     */
    public class NativeExpressAdViewHolder extends RecyclerView.ViewHolder {

        NativeExpressAdViewHolder(View view) {
            super(view);
        }
    }

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

    /**
     * Determines the view type for the given position.
     */
    @Override
    public int getItemViewType(int position) {
        return (position % MainActivity.ITEMS_PER_AD == 0) ? NATIVE_EXPRESS_AD_VIEW_TYPE
                : MENU_ITEM_VIEW_TYPE;
    }

    /**
     * Creates a new view for a menu item view or a Native Express ad view
     * based on the viewType. This method is invoked by the layout manager.
     */
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        switch (viewType) {
            case MENU_ITEM_VIEW_TYPE:
                View menuItemLayoutView = LayoutInflater.from(viewGroup.getContext()).inflate(
                        R.layout.menu_item_container, viewGroup, false);
                return new MenuItemViewHolder(menuItemLayoutView);
            case NATIVE_EXPRESS_AD_VIEW_TYPE:
                // fall through
            default:
                View nativeExpressLayoutView = LayoutInflater.from(
                        viewGroup.getContext()).inflate(R.layout.native_express_ad_container,
                        viewGroup, false);
                return new NativeExpressAdViewHolder(nativeExpressLayoutView);
        }

    }

    /**
     *  Replaces the content in the views that make up the menu item view and the
     *  Native Express ad view. This method is invoked by the layout manager.
     */
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        int viewType = getItemViewType(position);
        switch (viewType) {
            case MENU_ITEM_VIEW_TYPE:
                MenuItemViewHolder menuItemHolder = (MenuItemViewHolder) holder;
                KontenItem kontenItem = (KontenItem) mRecyclerViewItems.get(position);

                // Get the menu item image resource ID.
                String imageName = kontenItem.getImageName();
                int imageResID = mContext.getResources().getIdentifier(imageName, "drawable",
                        mContext.getPackageName());

                // Add the menu item details to the menu item view.
                menuItemHolder.menuItemImage.setImageResource(imageResID);
                menuItemHolder.menuItemName.setText(kontenItem.getName());
                menuItemHolder.menuItemPrice.setText(kontenItem.getPrice());
                menuItemHolder.menuItemCategory.setText(kontenItem.getCategory());
                menuItemHolder.menuItemDescription.setText(kontenItem.getDescription());
                menuItemHolder.menuItemUrl.setText(kontenItem.getInstructionUrl());
                break;
            case NATIVE_EXPRESS_AD_VIEW_TYPE:
                // fall through
            default:
                NativeExpressAdViewHolder nativeExpressHolder =
                        (NativeExpressAdViewHolder) holder;
                NativeExpressAdView adView =
                        (NativeExpressAdView) mRecyclerViewItems.get(position);
                ViewGroup adCardView = (ViewGroup) nativeExpressHolder.itemView;
                // The NativeExpressAdViewHolder recycled by the RecyclerView may be a different
                // instance than the one used previously for this position. Clear the
                // NativeExpressAdViewHolder of any subviews in case it has a different
                // AdView associated with it, and make sure the AdView for this position doesn't
                // already have a parent of a different recycled NativeExpressAdViewHolder.
                if (adCardView.getChildCount() > 0) {
                    adCardView.removeAllViews();
                }
                if (adView.getParent() != null) {
                    ((ViewGroup) adView.getParent()).removeView(adView);
                }

                // Add the Native Express ad to the native express ad view.
                adCardView.addView(adView);
        }
    }

}

KontenItem.java

    class KontenItem {

    private final String name;
    private final String description;
    private final String price;
    private final String category;
    private final String imageName;
    private final String instructionUrl;

    public KontenItem(String name, String description, String price, String category,
                      String imageName, String instructionUrl) {
        this.name = name;
        this.description = description;
        this.price = price;
        this.category = category;
        this.imageName = imageName;
        this.instructionUrl = instructionUrl;
    }

    public String getName() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    public String getPrice() {
        return price;
    }

    public String getCategory() {
        return category;
    }

    public String getImageName() {
        return imageName;
    }

    public String getInstructionUrl() {
        return instructionUrl;
    }
}

¡Gracias de antemano!

0
Renshi Ramon 29 may. 2017 a las 13:47

2 respuestas

La mejor respuesta

Estás llamando a addNativeExpressAds() y for (int i = 0; i <= mRecyclerViewItems.size(); i += ITEMS_PER_AD) { final NativeExpressAdView adView = (NativeExpressAdView) mRecyclerViewItems.get(i);

En diferentes hilos Entonces, mRecyclerViewItems.get(i) se usa antes de su inicialización.

Sugeriría usar

 @Override
            public void onResponse(Call<JSONResponse> call, Response<JSONResponse> response) {
                JSONResponse jsonResponse = response.body();

                mRecyclerViewItems = new ArrayList<Object>(Arrays.asList(jsonResponse.getKonten()));
                addNativeExpressAds();

                RecyclerView.Adapter adapter = new KontenAdapter(mRecyclerViewItems, getApplicationContext());
                mRecyclerView.setAdapter(adapter);

setUpAndLoadNativeExpressAds(); // << ADD THIS METHOD HERE AND REMOVE THE OTHER CALLING
            }
0
Vyacheslav 29 may. 2017 a las 11:33

Su problema es que mRecyclerViewItems es una matriz / colección basada en cero. Sin embargo, en setUpAndLoadNativeExpressAds() iterará incluso si no tiene elementos, y luego solicitará el elemento 0.

for (int i = 0; i <= mRecyclerViewItems.size(); i += ITEMS_PER_AD) {
  final NativeExpressAdView adView = (NativeExpressAdView) mRecyclerViewItems.get(i);
  ..
}

Deberías en cambio

for (int i = 0; i < mRecyclerViewItems.size(); i += ITEMS_PER_AD) {
  ..
}
0
William 30 may. 2017 a las 23:30