Уведомление асинхронного загрузчика задач об изменениях данных

Я перепробовал все, что мог за последние пять дней, как получить AsyncTask Loader, чтобы получать уведомления об изменениях данных с помощью контент-провайдера, но не повезло, и все ответы, представленные в stackru, все равно не получают меня. Мне действительно нужна твоя помощь.

Я использую 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 );



}

}

фрагмент кода поставщика контента

@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();
    }



}

3 ответа

Решение

Вам необходимо зарегистрировать ContentObserver на Cursor и переопределить onChange во что звонить onContentChanged в AsyncTaskLoader,

Если вы используете CursorLoader Вы получаете эту способность, уже запеченную в.

Может быть, вы должны использовать свой собственный вид переработчика. Я просто даю вам идею. Вы должны реализовать метод 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();
}

Это получилось благодаря реализации обозревателя контента в моем расширенном классе 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;
            }
Другие вопросы по тегам