【问题标题】:ListFragment doesn't update data from CursorLoaderListFragment 不更新来自 CursorLoader 的数据
【发布时间】:2014-04-28 15:28:21
【问题描述】:

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

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

另一件奇怪的事有关。我写了一些代码来为这个片段添加 ActionBar 菜单。在这种情况下,它不会调用onCreateOptionsMenu(尽管onCreate 中存在setHasOptionsMenu(true))并且未添加菜单。当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 的一部分。我想在insert 中调用notifyChange 和在query 中调用setNotificationUri 会使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;
    }

    ...
}

在 Activity 中打开片段:

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)

【问题讨论】:

  • 你确定你的数据被插入了吗??
  • 是的。此外,CursorLoader 调用 query 并返回具有 21 行的 Cursor。就像应该的那样。

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


【解决方案1】:

在您的代码中:-

使用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);
    }

试试看

【讨论】:

  • 没用。为什么应该? changeCursor 只需调用 swapCursor 并关闭旧光标
  • 您是否在 logcat 中获得任何值,请发布
  • 添加了一些日志。不要认为它们有用,但谁知道呢
  • 尝试调试您的 mAdapter 是否正在获取数据!
【解决方案2】:

对于第一个问题,错误是 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;
    }
}

【讨论】:

  • 不幸的是没有帮助。关于FLAG_REGISTER_CONTENT_OBSERVER 文档说“使用带有 android.content.CursorLoader 的 CursorAdapter 时不需要此标志”。支持库呢?真的需要吗?将 setHasOptionsMenu 从 onCreate 移动到 onCreateView 也没有帮助。
  • 我已经编辑了我的答案,你能发布你的菜单栏错误活动吗?
  • 关于支持库,如果该代码将由android API执行
  • 关于光标 id 的条件几乎无法修复
【解决方案3】:

不确定这是否会有所不同,但请使用片段的 LoaderManager 而不是 Activity 的。

替换

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

与

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

【讨论】:

    猜你喜欢
    • 2011-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-31
    相关资源
    最近更新 更多