【发布时间】: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