在过去的五天里,我已尽力了解如何让AsyncTask Loader使用内容提供商获得数据更改的通知,但没有运气,而且stackoverflow中提供的所有答案都没有得到我。 我真的需要你的帮助。
我正在使用AsyncTaskLoader与内容提供程序从数据库中检索数据,将其添加到我的数组列表并使用recyclerview适配器更新我的片段。新数据由同步适配器异步从服务器下载并存储在数据库中。通常情况下,我希望AsyncTask Loader会在新数据插入数据库时立即得到通知,但是我已经尝试了所有可能的方法,但是从来没有调用takeContentChanged()方法,并且无法更新新数据。
请参阅下面的实施:
public class NewsListLoader extends AsyncTaskLoader<List<News>> {
private static final String LOG_TAG = NewsListLoader.class.getSimpleName();
private List<News> mNews;
private ContentResolver mContentResolver;
private Cursor mCursor;
private Uri mUri;
private String mSortOrder;
public NewsListLoader(Context context, Uri uri, String sortOrder, ContentResolver contentResolver) {
super(context);
mContentResolver = contentResolver;
mUri = uri;
mSortOrder = sortOrder;
}
@Override
public List<News> loadInBackground() {
String[] projection = {NewsContract.NewsEntry.NEWS_TABLE_NAME
+ "." + NewsContract.NewsEntry._ID,
NewsContract.NewsEntry.NEWS_TITLE_COLUMN,
NewsContract.NewsEntry.NEWS_CATEGORY_COLUMN,
NewsContract.NewsEntry.NEWS_IMAGEURL_COLUMN,
NewsContract.NewsEntry.NEWS_URL_COLUMN,
NewsContract.NewsEntry.NEWS_DATE_COLUMN,
NewsContract.NewsEntry.NEWS_AUTHOR_COLUMN};
// NewsContract.SourceEntry.SOURCE_COLUMN};
List<News> entries = new ArrayList<News>();
mCursor = mContentResolver.query(NewsContract.NewsEntry.NEWS_TABLE_CONTENT_URI,
projection, null, null, mSortOrder);
if (mCursor != null){
if (mCursor.moveToFirst()){
do{
//int _id = mCursor.getInt(mCursor.getColumnIndex(NewsContract.NewsEntry._ID));
/*String source = mCursor.getString(
mCursor.getColumnIndex(NewsContract.SourceEntry.SOURCE_COLUMN));*/
String source = "mySource";
String title = mCursor.getString(
mCursor.getColumnIndex(NewsContract.NewsEntry.NEWS_TITLE_COLUMN));
String category = mCursor.getString(
mCursor.getColumnIndex(NewsContract.NewsEntry.NEWS_CATEGORY_COLUMN));
String imageUrl = mCursor.getString(
mCursor.getColumnIndex(NewsContract.NewsEntry.NEWS_IMAGEURL_COLUMN));
String url = mCursor.getString(
mCursor.getColumnIndex(NewsContract.NewsEntry.NEWS_URL_COLUMN));
String date = mCursor.getString(
mCursor.getColumnIndex(NewsContract.NewsEntry.NEWS_DATE_COLUMN));
String author = mCursor.getString(
mCursor.getColumnIndex(NewsContract.NewsEntry.NEWS_AUTHOR_COLUMN));
News news = new News(source,title, category, imageUrl, url, date, author);
entries.add(news);
}while (mCursor.moveToNext());
}
} else {Log.d(LOG_TAG, "Cursor is null");
//mCursor.setNotificationUri(mContentResolver, mUri);
}
return entries;
}
@Override
public void deliverResult(List<News> news) {
if (isReset()){
if (news != null){
mCursor.close();
}
}
List<News> oldNews = mNews;
if (mNews == null || mNews.size() == 0){
Log.d(LOG_TAG, "+++++++ No data returned");
}
mNews = news;
if (isStarted()){
super.deliverResult(news);
}
if (oldNews != null && oldNews != news){
mCursor.close();
}
}
@Override
protected void onStartLoading() {
if (mNews != null){
deliverResult(mNews);
}
if (takeContentChanged() || mNews == null){
// here this forceLoad() is called only when mNews is null and never
// takeContentChanged.
// the ui gets refresh only when i restart the app
forceLoad();
}
}
@Override
protected void onStopLoading() {
cancelLoad();
}
@Override
protected void onReset() {
onStopLoading();
if (mCursor != null){
mCursor.close();
}
mNews = null;
}
@Override
public void onCanceled(List<News> news) {
super.onCanceled(news);
if (mCursor != null){
mCursor.close();
}
}
@Override
public void forceLoad() {
super.forceLoad();
}
public void deleteOldItems (int num){
String where = NewsContract.NewsEntry._ID +
" IN (SELECT " + NewsContract.NewsEntry._ID + " FROM " +
NewsContract.NewsEntry.NEWS_TABLE_NAME +
" ORDER BY " + NewsContract.NewsEntry._ID + " DESC " + " LIMIT ?)";
String limit = "2, 10";
String[] selectionArgs = new String[]{limit};
mContentResolver.delete(
NewsContract.NewsEntry.NEWS_TABLE_CONTENT_URI, where, selectionArgs );
}
}
内容提供商代码snipplet
@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
final SQLiteDatabase db = mHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
Log.d(LOG_TAG, "Insert Uri " + uri);
switch (match) {
case NEWS:
/**
* Begins a transaction in "exclusive" mode. No other mutations can occur on the
* db until this transaction finishes.*/
db.beginTransaction();
int returnCount = 0;
try {
for (ContentValues value : values) {
long _id = db.insert(NewsContract.NewsEntry.NEWS_TABLE_NAME, null, value);
if (_id != -1) {
returnCount++;
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
getContext().getContentResolver().notifyChange(uri, null);
//Log.d(LOG_TAG, "Insert Count is " + returnCount);
return returnCount;
default:
return super.bulkInsert(uri, values);
}
}
@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;
Log.d(LOG_TAG, "Match Uri is " + uri);
Log.d(LOG_TAG, "Matcher is " + sUriMatcher.match(uri));
switch (sUriMatcher.match(uri)) {
case NEWS: {
//retCursor = getNewsWithAuthor( uri,projection, sortOrder);
//String author = NewsContract.NewsEntry.getAuthorFromUri(uri);
//Log.d(LOG_TAG, "URI for News is "+ uri);
//selection = sAuthorSlection;
retCursor = mHelper.getReadableDatabase().query(
NewsContract.NewsEntry.NEWS_TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
sortOrder
);
break;
}
case NEWS_WITH_SOURCE: {
retCursor = getNewsSourceAndCategory(uri, projection, sortOrder);
break;
}
case NEWS_WITH_CATEGORY: {
retCursor = getNewsWithCategory(uri, projection, sortOrder);
break;
}
case NEWS_WITH_SOURCE_AND_CATEGORY:{
Log.d(LOG_TAG, "Provider Uri is "+ uri);
retCursor = getNewsSourceAndCategory(uri, projection, sortOrder);
break;
}
// "news/*/*/*
case NEWS_WITH_SOURCE_DATE_AND_CATEGORY: {
// Log.d(LOG_TAG, "Provider Uri is "+ uri);
retCursor = getNewsWithSourceDateAndCategory(uri, projection, sortOrder);
break;
}
case SOURCE: {
retCursor = mHelper.getReadableDatabase().query(
NewsContract.SourceEntry.NEWS_SOURCE_TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
null,
sortOrder
);
break;
}
// "source/*"
case SOURCE_ID: {
retCursor = mHelper.getReadableDatabase().query(
NewsContract.SourceEntry.NEWS_SOURCE_TABLE_NAME,
projection,
NewsContract.SourceEntry._ID + " = '" + ContentUris.parseId(uri) + "'",
null,
null,
null,
sortOrder
);
break;
}
// "source"
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
retCursor.setNotificationUri(getContext().getContentResolver(), uri);
return retCursor;
}
实现LoaderCallback的片段
public class RecyclerViewFragment extends Fragment implements LoaderManager.LoaderCallbacks<List<News>> {
private static final String LOG_TAG = RecyclerViewFragment.class.getSimpleName();
private RecyclerView mRecyclerView;
private NewsRecyclerViewAdapter mAdapter;
private List<News> mNews;
private static final int LOADER_ID = 0;
private ContentResolver mContentResolver;
private Object mSyncObserverHandle;
Menu mOptionsMenu;
private String[] mUris;
public static RecyclerViewFragment newInstance() {
return new RecyclerViewFragment();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_recyclerview, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
RecyclerView.LayoutManager layoutManager;
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setHasFixedSize(false);
mRecyclerView.addItemDecoration(new MaterialViewPagerHeaderDecorator());
mAdapter = new NewsRecyclerViewAdapter(getActivity(),new ArrayList<News>());
mRecyclerView.setAdapter(mAdapter);
...
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mContentResolver = getActivity().getContentResolver();
getLoaderManager().initLoader(LOADER_ID, null,this);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
....
}
@Override
public void onResume() {
super.onResume();
...
}
@Override
public void onPause() {
super.onPause();
if (mSyncObserverHandle != null) {
ContentResolver.removeStatusChangeListener(mSyncObserverHandle);
mSyncObserverHandle = null;
}
}
@Override
public Loader<List<News>> onCreateLoader(int id, Bundle args) {
mContentResolver = getActivity().getContentResolver();
String source = "techcrunch";
String category = null;
String author = "Anna Escher";
String sortOrder = NewsContract.NewsEntry.NEWS_DATE_COLUMN + " DESC";
return new NewsListLoader(getActivity(), NewsContract.NewsEntry
.buildNewsSource(source), sortOrder, mContentResolver);
}
@Override
public void onLoadFinished(Loader<List<News>> loader, List<News> news) {
mAdapter.loadData(news);
mNews = news;
mAdapter.notifyDataSetChanged();
//mNews = news;
}
@Override
public void onLoaderReset(Loader<List<News>> loader) {
mAdapter.loadData(null);
}
Recyclerview适配器代码
@Override
public View_Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_card_big, parent, false);
return new View_Holder(view);
}
@Override
public void onBindViewHolder(View_Holder holder, int position) {
List<News> newsItem = mNewsItems;
Log.d(LOG_TAG, "Processing "+ newsItem.get(position).getTitle() + " -->" + Integer.toString(position));
String title = newsItem.get(position).getTitle();
holder.title.setText(title);
String author = newsItem.get(position).getAuthor();
holder.author.setText(author);
String source = (newsItem.get(position).getSource());
holder.source.setText(source);
String date = Utility.getDayName(mContext,newsItem.get(position).getPublishedAt());
holder.publisedAt.setText(date);
Picasso.with(mContext).load(newsItem.get(position).getImageToUrl())
.fit()
.error(R.drawable.placeholder)
.placeholder(R.drawable.placeholder)
.into(holder.imageView);
}
@Override
public int getItemCount() {
return (null != mNewsItems ? mNewsItems.size(): 0);
}
public void loadData(List<News> newNews){
mNewsItems.clear();
if (newNews != null){
mNewsItems.addAll(newNews);
notifyDataSetChanged();
}
}
答案 0 :(得分:0)
也许您必须使用自己的回收站视图。我只想给你一个主意。您必须实现checkIfempty方法。
@Override
public void setAdapter(Adapter adapter) {
}
final private AdapterDataObserver observer = new AdapterDataObserver() {
@Override
public void onChanged() {
checkIfEmpty();
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
checkIfEmpty();
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
checkIfEmpty();
}
};
@Override
public void setAdapter(Adapter adapter) {
final Adapter oldAdapter = getAdapter();
if (oldAdapter != null) {
oldAdapter.unregisterAdapterDataObserver(observer);
}
super.setAdapter(adapter);
if (adapter != null) {
adapter.registerAdapterDataObserver(observer);
}
checkIfEmpty();
}
答案 1 :(得分:0)
您需要在ContentObserver
上注册Cursor
并覆盖onChange
,以便在onContentChanged
中致电AsyncTaskLoader
。
如果您使用CursorLoader
,则可以使用该功能。
答案 2 :(得分:0)
通过在我的AsyncTask Loader扩展类中实现内容观察器来实现它。
public class NewsListLoader extends AsyncTaskLoader<List<News>> {
.....
final ForceLoadContentObserver mObserver;
public NewsListLoader(Context context, Uri uri, String sortOrder, ContentResolver contentResolver) {
super(context);
mContentResolver = contentResolver;
mUri = uri;
mSortOrder = sortOrder;
mObserver = new ForceLoadContentObserver();
}
public NewsListLoader(Context context){
super(context);
mObserver = new ForceLoadContentObserver();
}
@Override
public List<News> loadInBackground() {
......
mCursor = mContentResolver.query(mUri,
projection, null, null, mSortOrder);
if (mCursor != null){
try {
// Ensure the cursor window is filled.
mCursor.getCount();
mCursor.registerContentObserver(mObserver);
} catch (RuntimeException ex) {
mCursor.close();
throw ex;
}