ListFragment不会更新CursorLoader中的数据

时间:2014-03-21 10:11:03

标签: android android-listview android-contentprovider android-cursoradapter android-cursorloader

我需要从服务器显示一些数据。有几个片段可以显示项目,因此不容易保留更新。我决定在数据库中缓存数据并使用ContentProvider来访问它。但我有一个奇怪的问题。

最初,我的数据库中没有数据。我创建CursorLoader并将CursorAdapter设置为ListFragment的适配器。然后我从服务器收到一些东西并插入几条记录。我认为Loader会自动通知他们并重新加载数据。但没有任何反应,ListFragment保持为空。

另一个奇怪的事情是相关的。我写了一些代码来为这个片段添加ActionBar菜单。在这种情况下,onCreateOptionsMenu未被调用(尽管setHasOptionsMenu(true)中有onCreate)并且未添加菜单。当DB已经有一些数据时,它可以正常工作。

似乎Fragment内部出现了问题,但日志中没有任何内容。你怎么想,我做错了什么?

这是样本片段,其工作方式如下:

import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.SimpleCursorAdapter;
import android.view.Menu;
import android.view.MenuInflater;

public class FeedFragment3 extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
    private static final int LOADER_ID = 1;

    private SimpleCursorAdapter mAdapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }

    @Override
    public void onResume() {
        super.onResume();
        getActivity().getActionBar().setTitle(R.string.feed_ab_title);
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.feed, menu);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        mAdapter = new SimpleCursorAdapter(getActivity(), android.R.layout.simple_list_item_1, null,
            new String[] {"title"},
            new int[] {android.R.id.text1}
        );
        setListAdapter(mAdapter);

        getActivity().getSupportLoaderManager().initLoader(LOADER_ID, null, this);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return new CursorLoader(getActivity(), Consts.getFeedContentUri(), null, null, null, null);
    }

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

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

以下是ContentProvider的一部分。我想,在notifyChange中调用insert和在setNotificationUri中调用query会让ContentProvider收到有关更改的通知。可能还不够吗?

public class FeedContentProvider extends ContentProvider {
    ...

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        DPLog.it(TAG, "Query: [%s]", uri);

        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
        qb.setTables("feed");
        qb.setProjectionMap(mProjectionMap);

        Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, null);
        c.setNotificationUri(getContext().getContentResolver(), uri);
        return c;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        DPLog.it(TAG, "insert: [%s]", uri);

        String tableName = mTableNamesForCode.get(mUriMatcher.match(uri));
        mDb.getWritableDatabase().insert(tableName, null, values);

        getContext().getContentResolver().notifyChange(uri, null);

        return uri;
    }

    ...
}

在活动中打开片段:

private void openFeedFragment() {
    mFeedFragment = new FeedFragment3();
    showRootFragment(mFeedFragment);
    updateFeed();
}

private void updateFeed() {
    mApi.requestFeed(
        new Response.Listener<FeedResponse>() {
            @Override
            public void onResponse(FeedResponse response) {
                Uri uri = PostsContract.getFeedContentUri();
                Set<Long> existingIds = getPostIds(uri);

                ArrayList<ContentProviderOperation> operations = createMergeOperations();

                try {
                    getContentResolver().applyBatch(PostsContract.AUTHORITY, operations);
                } catch (RemoteException e) {
                    ...
                } catch (OperationApplicationException e) {
                    ...
                }
            }
        },
        null
    );
}

创建片段时的日志

03-21 19:12:35.041  22005-22344/com.app D/app.ContentProvider﹕ Cursor size: [0] 03-21 19:12:35.061  22005-22005/com.app V/app.checkpoint-checkpoint﹕ com.app.ui.fragment.FeedFragment3.onLoadFinished (FeedFragment3.java:60)
03-21 19:12:36.012  22005-22066/com.app D/app.API﹕ Request 'feed' finished
03-21 19:12:36.092  22005-22066/com.app D/dalvikvm﹕ GC_FOR_ALLOC freed 1417K, 11% free 12805K/14256K, paused 25ms, total 25ms
03-21 19:12:36.122  22005-22005/com.app I/app.ContentProvider﹕ Query: [content://com.app.content.posts/feed] (PostsContentProvider.java:59)
03-21 19:12:36.122  22005-22005/com.app D/app.ContentProvider﹕ Cursor size: [0] (PostsContentProvider.java:78)
03-21 19:12:36.142  22005-22005/com.app I/app.ContentProvider﹕ insert: [content://com.app.content.posts/feed] (PostsContentProvider.java:97)
03-21 19:12:36.262  22005-22005/com.app D/app.ContentProvider﹕ Inserted id: [1] (PostsContentProvider.java:102)
...
03-21 19:12:37.453  22005-22005/com.app D/app.ContentProvider﹕ Inserted id: [21] (PostsContentProvider.java:102)
03-21 19:12:37.453  22005-22348/com.app I/app.ContentProvider﹕ Query: [content://com.app.content.posts/feed] (PostsContentProvider.java:59)
03-21 19:12:37.453  22005-22348/com.app D/app.ContentProvider﹕ Cursor size: [21] (PostsContentProvider.java:78)
03-21 19:12:37.463  22005-22073/com.app I/app.ContentProvider﹕ Query: [content://com.app.content.posts/feed] (PostsContentProvider.java:59)
03-21 19:12:37.463  22005-22073/com.app D/app.ContentProvider﹕ Cursor size: [21] (PostsContentProvider.java:78)
03-21 19:12:37.463  22005-22005/com.app V/app.checkpoint-checkpoint﹕ com.app.ui.fragment.FeedFragment3.onLoadFinished (FeedFragment3.java:60)

3 个答案:

答案 0 :(得分:0)

对于第一个问题,错误是SimpleCursorAdapter的构造函数,不推荐使用,你必须说要听这样的更改:

mAdapter = new SimpleCursorAdapter(getActivity(), 
        android.R.layout.simple_list_item_1,    
        null,
        new String[] {"title"},
        new int[] {android.R.id.text1},
        SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER)
    );

第二次尝试这个对我有用:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    setHasOptionsMenu(true);
    [...]
}

最后如果您使用

,请不要使用getActivity().getActionBar(),而是使用getSupportActionBar
import android.support.v4.app.ListFragment;

编辑: 改进和修复你的装载机:

@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
    switch (cursorLoader.getId()) {
        case LOADER_ID:
            mAdapter.swapCursor(cursor);
            break;
    }
}

答案 1 :(得分:0)

你的代码中的

: -

使用swapCursor(data)代替changeCursor(data)

 @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return new CursorLoader(getActivity(), Consts.getFeedContentUri(), null, null, null, null);
    }

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

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

试试吧

答案 2 :(得分:0)

不确定这是否会产生影响,但请使用片段的LoaderManager而不是活动的。

替换

getActivity().getSupportLoaderManager().initLoader(LOADER_ID, null, this);

getLoaderManager().initLoader(LOADER_ID, null, this);