2015-04-19 2 views
0

Я использую три метода ContentProviders: bulkInsert(), query() и delete() (для удаления старых данных). Это мой ContentProvider класс:CursorLoader не обновляет данные во второй раз

public class MoviesProvider extends ContentProvider { 
    private static final String LOG_TAG = MoviesProvider.class.getSimpleName(); 

    // The URI Matcher used by this content provider. 
    private static final UriMatcher sUriMatcher = buildUriMatcher(); 
    private MoviesDbHelper mOpenHelper; 

    static final int MOVIES = 100; 
    static final int MOVIE_ID = 101; 

    static UriMatcher buildUriMatcher() { 
     // 1) The code passed into the constructor represents the code to return for the root 
     // URI. It's common to use NO_MATCH as the code for this case. Add the constructor below. 
     final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); 
     final String authority = CONTENT_AUTHORITY; 

     // 2) Use the addURI function to match each of the types. 
     matcher.addURI(authority, PATH_MOVIES, MOVIES); 
     matcher.addURI(authority, PATH_MOVIES + "/#", MOVIE_ID); 

     return matcher; 
    } 

    @Override 
    public boolean onCreate() { 
     mOpenHelper = new MoviesDbHelper(getContext()); 
     return true; 
    } 


    @Override 
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 
     // Here's the switch statement that, given a URI, will determine what kind of request it is, 
     // and query the database accordingly. 
     Cursor retCursor; 
     SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); 
     queryBuilder.setTables(MoviesEntry.TABLE_NAME); 
     switch (sUriMatcher.match(uri)) { 
      case MOVIE_ID: 
      { 
       queryBuilder.appendWhere(MoviesEntry._ID + "=" + uri.getLastPathSegment()); 
       break; 
      } 
      case MOVIES: 
      { 
       break; 
      } 
      default: 
       throw new UnsupportedOperationException("Unknown uri: " + uri); 

     } 

     retCursor = queryBuilder.query(mOpenHelper.getReadableDatabase(), 
       projection, selection, selectionArgs, null, null, sortOrder); 

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

     return retCursor; 
    } 

    @Override 
    public String getType(Uri uri) { 
     // Use the Uri Matcher to determine what kind of URI this is. 
     final int match = sUriMatcher.match(uri); 
     switch (match) { 
      case MOVIES: 
       return MoviesEntry.CONTENT_TYPE; 
      case MOVIE_ID: 
       return MoviesEntry.CONTENT_ITEM_TYPE; 
      default: 
       throw new UnsupportedOperationException("Unknown uri: " + uri); 
     } 
    } 

    @Override 
    public Uri insert(Uri uri, ContentValues values) { 
     final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 
     final int match = sUriMatcher.match(uri); 
     Uri returnUri; 

     switch (match) { 
      case MOVIES: { 
       long _id = db.insert(MoviesEntry.TABLE_NAME, null, values); 

       if (_id > 0) 
        returnUri = MoviesEntry.buildMoviesUri(_id); 
       else 
        throw new android.database.SQLException("Failed to insert row into " + uri); 

       break; 
      } 
      default: 
       throw new UnsupportedOperationException("Unknown uri: " + uri); 
     } 
     getContext().getContentResolver().notifyChange(uri, null); 

     return returnUri; 
    } 

    @Override 
    public int delete(Uri uri, String selection, String[] selectionArgs) { 
     //Start by getting a writable database 
     final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 
     //Use the uriMatcher to match the MOVIES URI's we are going to handle. 
     final int match = sUriMatcher.match(uri); 

     int rowsDeleted; 
     //A null value deletes all rows. In my implementation of this, I only notified 
     // the uri listeners (using the content resolver) if the rowsDeleted != 0 or the selection 
     // is null. 
     switch (match) { 
      case MOVIES: 
       rowsDeleted = db.delete(MoviesEntry.TABLE_NAME, selection, selectionArgs); 
       break; 
      case MOVIE_ID: 
       String id = uri.getLastPathSegment(); 
       if (TextUtils.isEmpty(selection)) { 
        rowsDeleted = db.delete(MoviesEntry.TABLE_NAME, 
          MoviesEntry._ID + "=" + id, 
          null); 
       } else { 
        rowsDeleted = db.delete(MoviesEntry.TABLE_NAME, 
          MoviesEntry._ID + "=" + id + " and " + selection, selectionArgs); 
       } 
       break; 
      default: 
       throw new UnsupportedOperationException("Unknown uri: " + uri); 
     } 

     Log.d(LOG_TAG, "Deleted rows: " + rowsDeleted); 
     Log.d(LOG_TAG, "Uri: " + uri.toString()); 

     if (rowsDeleted != 0) getContext().getContentResolver().notifyChange(uri, null); 

     return rowsDeleted; 
    } 

    @Override 
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 
     //This is a lot like the delete function. We return the number of rows impacted 
     // by the update. 
     final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 

     final int match = sUriMatcher.match(uri); 
     int rowsUpdated; 

     switch (match) { 
      case MOVIES: 
       rowsUpdated = db.update(MoviesEntry.TABLE_NAME, values, selection, selectionArgs); 
       break; 
      case MOVIE_ID: 
       String id = uri.getLastPathSegment(); 
       if (TextUtils.isEmpty(selection)) { 
        rowsUpdated = db.update(MoviesEntry.TABLE_NAME, 
          values, 
          MoviesEntry._ID + "=" + id, 
          null); 
       } else { 
        rowsUpdated = db.update(MoviesEntry.TABLE_NAME, 
          values, 
          MoviesEntry._ID + "=" + id 
          + " and " 
          + selection, 
          selectionArgs); 
       } 
       break; 
      default: 
       throw new UnsupportedOperationException("Unknown uri: " + uri); 
     } 

     if (rowsUpdated != 0) getContext().getContentResolver().notifyChange(uri, null); 

     return rowsUpdated; 
    } 

    @Override 
    public int bulkInsert(Uri uri, ContentValues[] values) { 
     final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 
     final int match = sUriMatcher.match(uri); 
     switch (match) { 
      case MOVIES: 
       db.beginTransaction(); 
       int returnCount = 0; 
       try { 
        for (ContentValues value: values) { 
         long _id = db.insert(MoviesEntry.TABLE_NAME, null, value); 
         if (_id != -1) { 
          returnCount++; 
         } 
        } 
        db.setTransactionSuccessful(); 
       } finally { 
        db.endTransaction(); 
       } 
       getContext().getContentResolver().notifyChange(uri, null); 
       return returnCount; 
      default: 
       return super.bulkInsert(uri, values); 
     } 
    } 

    // You do not need to call this method. This is a method specifically to assist the testing 
    // framework in running smoothly. You can read more at: 
    // http://developer.android.com/reference/android/content/ContentProvider.html#shutdown() 
    @Override 
    @TargetApi(11) 
    public void shutdown() { 
     mOpenHelper.close(); 
     super.shutdown(); 
    } 
} 

Это фрагмент, где я обновляю мои данные:

for (Movie movie: moviesList) { 
    ContentValues movieValues = new ContentValues(); 

    timeCounter++; 

    movieValues.put(MoviesEntry.COLUMN_TITLE, movie.getTitle()); 
    movieValues.put(MoviesEntry.COLUMN_DIRECTORS, movie.getDirectors().get(0).getName()); 
    movieValues.put(MoviesEntry.COLUMN_GENRES, movie.getGenres().toString()); 
    movieValues.put(MoviesEntry.COLUMN_WRITERS, movie.getWriters().get(0).getName()); 
    movieValues.put(MoviesEntry.COLUMN_COUNTRIES, movie.getCountries().get(0)); 
    movieValues.put(MoviesEntry.COLUMN_YEAR, movie.getYear()); 
    movieValues.put(MoviesEntry.COLUMN_RUNTIME, movie.getRuntime().get(0)); 
    movieValues.put(MoviesEntry.COLUMN_URL_POSTER, movie.getUrlPoster()); 
    movieValues.put(MoviesEntry.COLUMN_RATING, movie.getRating()); 
    movieValues.put(MoviesEntry.COLUMN_PLOT, movie.getPlot()); 
    movieValues.put(MoviesEntry.COLUMN_URL_IMDB, movie.getUrlIMDB()); 
    movieValues.put(MoviesEntry.COLUMN_DATE, dateTime + timeCounter); 

    Log.d(LOG_TAG, "" + dateTime); 

    downloadPosters(movie.getUrlPoster()); 

    cVVector.add(movieValues); 
} 

//add to database 
if (cVVector.size() > 0) { 
    ContentValues[] cvArray = new ContentValues[cVVector.size()]; 
    cVVector.toArray(cvArray); 
    int inserted = getContext().getContentResolver() 
      .bulkInsert(MoviesEntry.CONTENT_URI, cvArray); 
    Log.d(LOG_TAG, "Inserted: " + inserted); 
    Log.d(LOG_TAG, "DateTime: " + dateTime); 

    Cursor cursor = getContext().getContentResolver().query(MoviesEntry.CONTENT_URI, 
      null, null, null, null); 
    if (cursor.getCount() > numberOfMovies) { 
     // delete old data so we don't build up an endless history 
     int deleted = getContext().getContentResolver().delete(MoviesEntry.CONTENT_URI, 
       MoviesEntry.COLUMN_DATE + "<?", 
       new String[] {Long.toString(dateTime)}); 
     Log.d(LOG_TAG, "Deleted: " + deleted); 
     Log.d(LOG_TAG, "DateTime#2: " + dateTime); 
    } 

    updateNotifications(); 
} 

Это мой отрывок из основного фрагмента, где я реализовал RecycleView и погрузчики (он отлично работает):

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

    private static final String LOG_TAG = MoviesFragment.class.getSimpleName(); 

    private MoviesAdapter mMoviesAdapter; 
    private RecyclerView mRecyclerView; 
    private RecyclerView.LayoutManager mLayoutManager; 

    private static final String[] CARDS_PROJECTION = { 
      MoviesEntry._ID, 
      MoviesEntry.COLUMN_URL_POSTER, 
      MoviesEntry.COLUMN_TITLE, 
      MoviesEntry.COLUMN_DIRECTORS, 
      MoviesEntry.COLUMN_GENRES, 
      MoviesEntry.COLUMN_RATING, 
      MoviesEntry.COLUMN_YEAR 
    }; 

    private static final int CARDS_LOADER = 0; 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     View rootView = inflater.inflate(R.layout.movies_list, container, false); 

     mRecyclerView = (RecyclerView) rootView.findViewById(R.id.list_of_movies); 
     mLayoutManager = new LinearLayoutManager(getActivity()); 
     mRecyclerView.setLayoutManager(mLayoutManager); 

     mMoviesAdapter = new MoviesAdapter(getActivity(), null, 0); 
     mRecyclerView.setAdapter(mMoviesAdapter); 

     mRecyclerView.addOnItemTouchListener(
       new RecyclerItemClickListener(getActivity(), new RecyclerItemClickListener.OnItemClickListener() { 
        @Override 
        public void onItemClick(View view, int position) { 

         Log.d(LOG_TAG, "POSITION: " + position); 
         //Because position starts from 0 
         Cursor cursor = mMoviesAdapter.getItem(position + 1); 

         Uri uri = MoviesEntry.buildMoviesUri((long) cursor.getPosition()); 
         Intent intent = new Intent(getActivity(), DetailActivity.class); 
         intent.putExtra(Utility.ID_KEY, uri.toString()); 
         startActivity(intent); 
        } 
       }) 
     ); 

     return rootView; 
    } 

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


    @Override 
    public Loader<Cursor> onCreateLoader(int id, Bundle args) { 
     Uri baseUri = MoviesEntry.CONTENT_URI; 

     return new CursorLoader(getActivity(), baseUri, CARDS_PROJECTION, null, null, null); 
    } 

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

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

И, наконец, мой класс подробно фрагмент, где я обновляя данные с помощью погрузчиков:

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

    private static final String LOG_TAG = DetailFragment.class.getSimpleName(); 
    private static final String DETAIL_URI = "URI"; 
    private Uri mUri; 

    private static final int DETAIL_LOADER = 0; 

    private static final String[] DETAIL_COLUMNS = { 
      MoviesEntry.TABLE_NAME + "." + MoviesEntry._ID, 
      MoviesEntry.COLUMN_URL_POSTER, 
      MoviesEntry.COLUMN_TITLE, 
      MoviesEntry.COLUMN_COUNTRIES, 
      MoviesEntry.COLUMN_YEAR, 
      MoviesEntry.COLUMN_RUNTIME, 
      MoviesEntry.COLUMN_RATING, 
      MoviesEntry.COLUMN_GENRES, 
      MoviesEntry.COLUMN_DIRECTORS, 
      MoviesEntry.COLUMN_WRITERS, 
      MoviesEntry.COLUMN_PLOT, 
      MoviesEntry.COLUMN_URL_IMDB 
    }; 


    private static final String SHARE_HASHTAG = " #WhatToWatch"; 
    private ShareActionProvider mShareActionProvider; 
    private String mMovieShareInfo; 

    private ImageView mPosterView; 
    private TextView mTitleView; 
    private TextView mCountriesView; 
    private TextView mYearView; 
    private TextView mRuntimeView; 
    private TextView mRatingView; 
    private TextView mGenresView; 
    private TextView mDirectorsView; 
    private TextView mWritersView; 
    private TextView mPlotView; 

    public DetailFragment() { 
     setHasOptionsMenu(true); 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     View rootView = inflater.inflate(R.layout.fragment_detail, container, false); 


     Bundle extras = getActivity().getIntent().getExtras(); 
     if (extras != null) { 
      mUri = Uri.parse(extras.getString(Utility.ID_KEY)); 
     } 

     mPosterView = (ImageView) rootView.findViewById(R.id.poster); 
     mTitleView = (TextView) rootView.findViewById(R.id.tv_title); 
     mCountriesView = (TextView) rootView.findViewById(R.id.country); 
     mYearView = (TextView) rootView.findViewById(R.id.release_year); 
     mRuntimeView = (TextView) rootView.findViewById(R.id.runtime); 
     mRatingView = (TextView) rootView.findViewById(R.id.rating); 
     mGenresView = (TextView) rootView.findViewById(R.id.genre); 
     mDirectorsView = (TextView) rootView.findViewById(R.id.director); 
     mWritersView = (TextView) rootView.findViewById(R.id.writers); 
     mPlotView = (TextView) rootView.findViewById(R.id.plot); 

     return rootView; 
    } 

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

    @Override 
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 
     // Inflate the menu; this adds items to the action bar if it is present. 
     inflater.inflate(R.menu.detailfragment, menu); 
     // Retrieve the share menu item 
     MenuItem menuItem = menu.findItem(R.id.action_share); 
     // Get the provider and hold onto it to set/change the share intent. 
     mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(menuItem); 

     if (mMovieShareInfo == null) { 
      mShareActionProvider.setShareIntent(createShareMovieIntent()); 
     } 
    } 

    @Override 
    public Loader<Cursor> onCreateLoader(int id, Bundle args) { 

     if (null != mUri) { 
      return new CursorLoader(
        getActivity(), 
        mUri, 
        DETAIL_COLUMNS, 
        null, 
        null, 
        null 
      ); 
     } 

     return null; 
    } 

    @Override 
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 

     updateData(data); 

    } 

    @Override 
    public void onLoaderReset(Loader<Cursor> loader) { 
     Log.d(LOG_TAG, "Data reseted!"); 
    } 

    private Intent createShareMovieIntent() { 
     Intent shareIntent = new Intent(Intent.ACTION_SEND); 
     shareIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 
     shareIntent.setType("text/plain"); 
     shareIntent.putExtra(Intent.EXTRA_TEXT, mMovieShareInfo + SHARE_HASHTAG); 

     return shareIntent; 
    } 

    private void updateData(Cursor data) { 
     if (data != null && data.moveToFirst()) { 
      String posterUrl = data.getString(data.getColumnIndex(MoviesEntry.COLUMN_URL_POSTER)); 
      String title = data.getString(data.getColumnIndex(MoviesEntry.COLUMN_TITLE)); 
      String country = data.getString(data.getColumnIndex(MoviesEntry.COLUMN_COUNTRIES)); 
      String year = data.getString(data.getColumnIndex(MoviesEntry.COLUMN_YEAR)); 
      String runtime = data.getString(data.getColumnIndex(MoviesEntry.COLUMN_RUNTIME)); 
      String rating = data.getString(data.getColumnIndex(MoviesEntry.COLUMN_RATING)); 
      String genres = data.getString(data.getColumnIndex(MoviesEntry.COLUMN_GENRES)); 
      String director = data.getString(data.getColumnIndex(MoviesEntry.COLUMN_DIRECTORS)); 
      String writer = data.getString(data.getColumnIndex(MoviesEntry.COLUMN_WRITERS)); 
      String plot = data.getString(data.getColumnIndex(MoviesEntry.COLUMN_PLOT)); 

      Picasso.with(getActivity()) 
        .load(posterUrl) 
        .placeholder(R.drawable.progress_animation) 
        .resize(205, 310) 
        .centerCrop() 
        .into(mPosterView); 
      mTitleView.setText(title); 
      mCountriesView.setText(country); 
      mYearView.setText(year); 
      mRuntimeView.setText(runtime); 
      mRatingView.setText(rating); 
      mGenresView.setText(genres); 
      mDirectorsView.setText(director); 
      mWritersView.setText(writer); 
      mPlotView.setText(plot); 

      mMovieShareInfo = "Awesome movie «" + title + "»" + "\n" + 
        "which IMDB rating is " + rating + "\n" + 
        "And directed by " + director + "\n" + genres + "\n"; 

      if (mShareActionProvider != null) { 
       mShareActionProvider.setShareIntent(createShareMovieIntent()); 
      } 
     } 
    } 
} 

После первого запуска приложения (когда только вызовы метода bulkInsert()) все работает нормально, а загрузчик правильно загружает данные. Но когда я обновление данных (при bulkInsert(), затем query() и delete() (которые удаляют старые данные) методы вызовов) CursorLoader не загружает новые данные в DetailFragment и это выглядит следующим образом:

enter image description here

ответ

0

Поскольку в SQLite autoincremented счетчик идентификатор не сбрасывается, так что я просто заменил это:

Cursor cursor = mMoviesAdapter.getItem(position + 1); 
Uri uri = MoviesEntry.buildMoviesUri((long) cursor.getPosition()); 

этим:

Uri uri = MoviesEntry.buildMoviesUri(mMoviesAdapter.getItemId(position)); 

в моей основной фрагмент. И все работает нормально.

0

Возможно, вы могли бы сохранить ссылку на свой CursorLoader, и когда ваши данные изменились, вы можете позвонить Loader.onContentChanged(), чтобы заставить данные перезагрузить.

+0

Извините, но где я должен назвать это? – c0nst

+0

Возможно, вокруг updateNotifications() в вашем фрагменте обновления. –