我使用了三种ContentProviders方法:bulkInsert()
,query()
和delete()
(用于删除旧数据)。
这是我的ContentProvider类:
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和Loaders(它工作正常):
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);
}
}
最后是我的细节片段类,我使用Loaders更新数据:
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()
方法时),Everythings工作正常,加载程序正确加载数据。但是当我更新数据时(bulkInsert()
,然后query()
和delete()
(删除旧数据)方法调用)CursorLoader不会将新数据加载到DetailFragment看起来像这样:
答案 0 :(得分:0)
也许你可以保留对CursorLoader的引用,当你的数据发生变化时,你可以调用Loader.onContentChanged()来强制数据重新加载。
答案 1 :(得分:0)
因为在SQLite中自动递增id计数器不会重置,所以我只是将其替换为:
Cursor cursor = mMoviesAdapter.getItem(position + 1);
Uri uri = MoviesEntry.buildMoviesUri((long) cursor.getPosition());
由此:
Uri uri = MoviesEntry.buildMoviesUri(mMoviesAdapter.getItemId(position));
在我的主要片段中。一切正常。