Search code examples
androidxmlandroid-fragmentsandroid-recyclerviewandroid-sqlite

IndexOutOfBound Exception while setting the data in recyclerview from sqlite database


I am fetching data from sqlite and filling it into recyclerview, but while filling the data i am getting IndexOutOfBoundException in my logcat. Can anyone tell me why i am getting this exception ?

Databasehelper class code:

public class DatabaseHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "history.db";
public  static final String TABLE_NAME = "history_table";
public static  final String COL_1 = "ID";
public static  final String COL_2 = "URL";
public  static final String COL_3 = "TITLE";
public DatabaseHelper(@Nullable Context context) {
    super(context, DATABASE_NAME, null, 1);
    SQLiteDatabase db = this.getWritableDatabase();
}

@Override
public void onCreate(SQLiteDatabase db) {
    db.execSQL("CREATE TABLE "+ TABLE_NAME+"(ID INTEGER PRIMARY KEY AUTOINCREMENT, URL TEXT, TITLE TEXT)");
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    db.execSQL("DROP TABLE IF EXISTS "+TABLE_NAME);
    onCreate(db);
}
public  boolean insertData(String url, String title){
    SQLiteDatabase db = this.getWritableDatabase();
    ContentValues contentValues = new ContentValues();
    contentValues.put(COL_2, url);
    contentValues.put(COL_3, title);
    long result = db.insert(TABLE_NAME,null,contentValues);
    if(result == -1) {
        return false;
    }
    else{
        return true;
    }

}
public  boolean updateData(String id, String url, String title) {
    SQLiteDatabase db = this.getWritableDatabase();
    ContentValues contentValue= new ContentValues();
    contentValue.put(COL_1, id);
    contentValue.put(COL_2, url);
    contentValue.put(COL_3, title);
    db.update(TABLE_NAME, contentValue, "ID= ?",new String[]{id} );
    return true;
}
public Cursor getalldata() {
    SQLiteDatabase db = this.getWritableDatabase();
    Cursor res = db.rawQuery("select * from "+ TABLE_NAME, null);
    return res;
}
public Cursor getuniquedata(String id){
    SQLiteDatabase db = this.getWritableDatabase();
    Cursor res = db.rawQuery("select * from "+ TABLE_NAME+" where " +COL_2+" = ?", new String []{id});
    return res;
}
public void deletedata(String id){
    SQLiteDatabase db = this.getWritableDatabase();
    db.delete(TABLE_NAME, COL_2+ "= ?", new String []{id});

} }

Code when i am filling data in my model and setting my adapter:

public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    view =  inflater.inflate(R.layout.fragment_history, container, false);
    button = (Button) view.findViewById(R.id.button1);
    recyclerView = (RecyclerView) view.findViewById(R.id.recyclerview8);
    historyModels = new ArrayList<>();
    LinearLayoutManager mainlayout= new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL,false);
    recyclerView.setLayoutManager(mainlayout);
    recyclerView.setItemAnimator(new DefaultItemAnimator());
    mydb = new DatabaseHelper(getContext());
    Cursor res = mydb.getalldata();
    res.moveToFirst();
    if(res!=null) {
            while (res.moveToNext()) ;
            {
                HistoryModel historyModel = new HistoryModel(res.getString(1), res.getString(2));  //this line is giving error
                historyModels.add(historyModel);
            }
        mydb.close();
        historyRecyclerAdapter = new HistoryRecyclerAdapter(historyModels, getContext());
        recyclerView.setAdapter(historyRecyclerAdapter);
    }
    return view;
}

RecyclerAdapter class:

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
    mydb = new DatabaseHelper(context);
    final HistoryModel historyModel = (HistoryModel) historyModels.get(position);
    Picasso.get().load("https://img.youtube.com/vi/"+historyModel.getDetailimg()+"/mqdefault.jpg").into(holder.imageView);
    holder.textView.setText(historyModel.getDetailtext());
    holder.delbutton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mydb.deletedata(historyModels.get(position).getDetailimg());
            historyModels.remove(position);
            notifyItemRemoved(position);
            notifyItemRangeChanged(position, historyModels.size());
            Toast.makeText(context, "Video Removed Successfully", Toast.LENGTH_LONG).show();

        }
    });
}

LogcatError:

android.database.CursorIndexOutOfBoundsException: Index 8 requested, with a size of 8
    at android.database.AbstractCursor.checkPosition(AbstractCursor.java:460)
    at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:136)
    at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:50)
    at com.example.haryanvigaane.Fragment.HistoryFragment.onCreateView(HistoryFragment.java:77)
    at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2600)
    at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:881)
    at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
    at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
    at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439)
    at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)
    at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869)
    at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824)
    at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727)

Solution

  • First there is this line:

    res.moveToFirst();
    

    which moves the Cursor's index to the 1st row and then you start an empty loop:

    while (res.moveToNext());
    

    because of the semicolon at the end, which even if you removed it the list that you create would miss the 1st row.
    After this empty loop, which only advances the Cursor's index and finally sets it after the last row of the Cursor, in the following block of code inside the curly braces (which is not part of while) you try to retrieve the values of the 2 columns and this is where the error is thrown, because there is no row there where the Cursor's index is.
    Change the code to this:

    Cursor res = mydb.getalldata();
    while (res.moveToNext()) {
        HistoryModel historyModel = new HistoryModel(res.getString(1), res.getString(2));     
        historyModels.add(historyModel);
    }
    res.close();
    mydb.close();
    historyRecyclerAdapter = new HistoryRecyclerAdapter(historyModels, getContext());
    recyclerView.setAdapter(historyRecyclerAdapter);
    

    Note that it's a good practice to use getColumnIndex() instead of explicitly setting the index of a column, so I recommend this:

    HistoryModel historyModel = new HistoryModel(
        res.getString(res.getColumnIndex(DatabaseHelper.URL)), 
        res.getString(res.getColumnIndex(DatabaseHelper.TITLE))
    );