我需要从服务器显示一些数据。有几个片段可以显示项目,因此不容易保留更新。我决定在数据库中缓存数据并使用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)
答案 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);