mercredi 4 mai 2016

CursorLoader sometimes fetches from the wrong URI

I have a ViewPager with two fragments(for article and video) both connected to Cursorloaders fetching data from a contentprovider. Both loaders are using two different uris, and two different projections as can be seen below. Yet on rare occasions the app crashes when in the query of the contentprovider the article's uri is matched with the video's projection and viceversa. This scenario is not the rule. The app works as expected for the majority of the time, but it crashes from time to time b/c of this error.

Here is the relevant part of the error message:

Caused by: android.database.sqlite.SQLiteException: no such column: video_post._id (code 1): , 
while compiling: SELECT video_post._id, site._id, site.name, site.icon_url,
video_post.title, video_post.youtube_id, video_post.date, viewed_post._id,
bookmark_video_post._id FROM article_post INNER JOIN site ON
article_post.site_id = site._id LEFT JOIN viewed_post ON 
article_post._id =viewed_post.post_id AND 
viewed_post.post_type = 'article' LEFT JOIN bookmark_article_post 
ON article_post._id = bookmark_article_post._id WHERE (site.active = ?) 
ORDER BY video_post._id DESC

It's a long statement, but moral of the story, the wrong columns are being matched with the table.

Full Error Message:

FATAL EXCEPTION: ModernAsyncTask #3
      Process: com.fentale.dalol, PID: 1682
      java.lang.RuntimeException: An error occurred while executing doInBackground()
          at android.support.v4.content.ModernAsyncTask$3.done(ModernAsyncTask.java:143)
          at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
          at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
          at java.util.concurrent.FutureTask.run(FutureTask.java:242)
          at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
          at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
          at java.lang.Thread.run(Thread.java:818)
      Caused by: android.database.sqlite.SQLiteException: no such column: video_post._id (code 1): , while compiling: SELECT video_post._id, site._id, site.name, site.icon_url, video_post.title, video_post.youtube_id, video_post.date, viewed_post._id, bookmark_video_post._id FROM article_post INNER JOIN site ON article_post.site_id = site._id LEFT JOIN viewed_post ON article_post._id =viewed_post.post_id AND viewed_post.post_type = 'article' LEFT JOIN bookmark_article_post ON article_post._id = bookmark_article_post._id WHERE (site.active = ?) ORDER BY video_post._id DESC
          at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
          at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:887)
          at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:498)
          at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
          at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)
          at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
          at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:44)
          at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1316)
          at android.database.sqlite.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java:400)
          at android.database.sqlite.SQLiteQueryBuilder.query(SQLiteQueryBuilder.java:294)
          at com.fentale.dalol.data.DalolProvider.query(DalolProvider.java:136)
          at android.content.ContentProvider.query(ContentProvider.java:1017)
          at android.content.ContentProvider$Transport.query(ContentProvider.java:238)
          at android.content.ContentResolver.query(ContentResolver.java:491)
          at android.support.v4.content.ContentResolverCompatJellybean.query(ContentResolverCompatJellybean.java:29)
          at android.support.v4.content.ContentResolverCompat$ContentResolverCompatImplJB.query(ContentResolverCompat.java:57)
          at android.support.v4.content.ContentResolverCompat.query(ContentResolverCompat.java:125)
          at android.support.v4.content.CursorLoader.loadInBackground(CursorLoader.java:59)
          at android.support.v4.content.CursorLoader.loadInBackground(CursorLoader.java:37)
          at android.support.v4.content.AsyncTaskLoader.onLoadInBackground(AsyncTaskLoader.java:296)
          at android.support.v4.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:54)
          at android.support.v4.content.AsyncTaskLoader$LoadTask.doInBackground(AsyncTaskLoader.java:42)
          at android.support.v4.content.ModernAsyncTask$2.call(ModernAsyncTask.java:128)
          at java.util.concurrent.FutureTask.run

Fragment 1(ArticleFragment)

public class ArticleListFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor>{

    public static final int ARTICLE_LOADER = 1;

    public static final String[] ARTICLE_COLUMNS = {
        DalolContract.ArticlePostEntry.TABLE_NAME + "." + DalolContract.ArticlePostEntry._ID,
        DalolContract.SiteEntry.TABLE_NAME + "." + DalolContract.SiteEntry._ID,
        DalolContract.SiteEntry.TABLE_NAME + "." + DalolContract.SiteEntry.COLUMN_NAME,
        DalolContract.SiteEntry.TABLE_NAME + "." + DalolContract.SiteEntry.COLUMN_ICON_URL,
        DalolContract.ArticlePostEntry.TABLE_NAME + "." + DalolContract.ArticlePostEntry.COLUMN_TITLE,
        DalolContract.ArticlePostEntry.TABLE_NAME + "." + DalolContract.ArticlePostEntry.COLUMN_URL,
        DalolContract.ArticlePostEntry.TABLE_NAME + "." + DalolContract.ArticlePostEntry.COLUMN_DATE,
        DalolContract.ArticlePostEntry.TABLE_NAME + "." + DalolContract.ArticlePostEntry.COLUMN_THUMB_URL,
        DalolContract.ArticlePostEntry.TABLE_NAME + "." + DalolContract.ArticlePostEntry.COLUMN_DESCRIPTION,
        DalolContract.ViewedPostEntry.TABLE_NAME + "." + DalolContract.ViewedPostEntry._ID,
        DalolContract.BookmarkArticlePostEntry.TABLE_NAME + "." + DalolContract.BookmarkArticlePostEntry._ID
    };

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        getLoaderManager().initLoader(ARTICLE_LOADER, null, this);
        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        String sortOrder = DalolContract.ArticlePostEntry.TABLE_NAME + "." + DalolContract.ArticlePostEntry._ID + " DESC";

        return new CursorLoader(
                getActivity(),
                DalolContract.ArticlePostEntry.CONTENT_URI,
                ARTICLE_COLUMNS,
                null,
                null,
                sortOrder);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        mAdapter.swapCursor(data);
    }

Fragment2(VideoFragment):

public class VideoListFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {

    public static final int VIDEO_LOADER = 0;

    public static final String[] VIDEO_COLUMNS = {
        DalolContract.VideoPostEntry.TABLE_NAME + "." + DalolContract.VideoPostEntry._ID,
        DalolContract.SiteEntry.TABLE_NAME + "." + DalolContract.SiteEntry._ID,
        DalolContract.SiteEntry.TABLE_NAME + "." + DalolContract.SiteEntry.COLUMN_NAME,
        DalolContract.SiteEntry.TABLE_NAME + "." + DalolContract.SiteEntry.COLUMN_ICON_URL,
        DalolContract.VideoPostEntry.TABLE_NAME + "." + DalolContract.VideoPostEntry.COLUMN_TITLE,
        DalolContract.VideoPostEntry.TABLE_NAME + "." + DalolContract.VideoPostEntry.COLUMN_YOUTUBE_ID,
        DalolContract.VideoPostEntry.TABLE_NAME + "." + DalolContract.VideoPostEntry.COLUMN_DATE,
        DalolContract.ViewedPostEntry.TABLE_NAME + "." + DalolContract.ViewedPostEntry._ID,
        DalolContract.BookmarkVideoPostEntry.TABLE_NAME + "." + DalolContract.BookmarkVideoPostEntry._ID
    };

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        getLoaderManager().initLoader(VIDEO_LOADER, null, this);
        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        String sortOrder = DalolContract.VideoPostEntry.TABLE_NAME + "." + DalolContract.VideoPostEntry._ID + " DESC";

        return new CursorLoader(
            getActivity(),
            DalolContract.VideoPostEntry.CONTENT_URI,
            VIDEO_COLUMNS,
            null,
            null,
            sortOrder);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        mAdapter.swapCursor(data);
        updateEmptyView();
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        mAdapter.swapCursor(null);
    }
}

ContentProvider:

public class DalolProvider extends ContentProvider {

    private static final UriMatcher sUriMatcher = buildUriMatcher();
    private DalolDbHelper mDbHelper;

    ...
    private static final int VIDEO_POST = 200;
    private static final int ARTICLE_POST = 500;
    ...

    static UriMatcher buildUriMatcher(){

        UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
        final String authority = DalolContract.CONTENT_AUTHORITY;

        ...
        matcher.addURI(authority, DalolContract.PATH_VIDEO_POST, VIDEO_POST);
        matcher.addURI(authority, DalolContract.PATH_ARTICLE_POST, ARTICLE_POST);
        ...

        return matcher;
    }

    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        SQLiteDatabase db = mDbHelper.getReadableDatabase();
        Cursor retCursor;

        switch (sUriMatcher.match(uri)){
            ...
            case VIDEO_POST: {
                sQueryBuilder.setTables(DataUtilities.getVideoTableJoin());
                retCursor = sQueryBuilder.query(
                    db,
                    projection,
                    DalolContract.SiteEntry.TABLE_NAME + "." + DalolContract.SiteEntry.COLUMN_ACTIVE + " = ?",
                    new String[]{String.valueOf(DalolContract.SiteEntry.VALUE_ACTIVE)},
                    null,
                    null,
                    sortOrder);
                break;
            }

            case ARTICLE_POST: {
                sQueryBuilder.setTables(DataUtilities.getArticleTableJoin());
                retCursor = sQueryBuilder.query(
                    db,
                    projection,
                    DalolContract.SiteEntry.TABLE_NAME + "." + DalolContract.SiteEntry.COLUMN_ACTIVE + " = ?",
                    new String[]{String.valueOf(DalolContract.SiteEntry.VALUE_ACTIVE)},
                    null,
                    null,
                    sortOrder);
                break;
            }
            ...
            default:
                throw new UnsupportedOperationException("Unknown uri: "+ uri);
        }

        retCursor.setNotificationUri(getContext().getContentResolver(), uri);
        return retCursor;
    }
}

Aucun commentaire:

Enregistrer un commentaire