问题
I'm building a small application that I'd like to automatically update when new data arrives.
From what I've read of the android documentation, I thought that because I'm using the newer techniques that a ContentObserver would be created in the background for me, and I would just get automatic calls to the Loader.
If there's any more information that would be helpful, I'd be glad to add it, or to upload the complete project somewhere.
Additionally, I'm targeting KitKat (sdk version 19).
Application Overview
- We have a ListActivity using the builtin ListView.
- The ListActivity gets data by using a Loader and a SimpleCursorAdapter.
- The Loader retrieves its data by querying a ContentProvider.
- The ContentProvider is backed by a SQLite Database.
Additionally, in the action bar, I've added two actions, one to add an item, and one to clear all items.
As I add items, the ListView is not updated with the new entries, and I can also see that no new query has been run on the ContentProvider. If I close the application, and restart it, those entries are now visible in the ListView.
Source Code Follows
Activity.java
package us.lynch.listdemo;
import java.text.DateFormat;
import java.util.Date;
import us.lynch.listdemo.R;
import android.app.ListActivity;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.ContentValues;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.CursorAdapter;
import android.widget.SimpleCursorAdapter;
public class Activity extends ListActivity implements LoaderCallbacks<Cursor> {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new SimpleCursorAdapter(this, R.layout.listitem, null, new String[] { "string" }, new int[] { R.id.text }, 0));
getLoaderManager().initLoader(0, null, this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
ContentValues values = new ContentValues();
switch (item.getItemId()) {
case R.id.action_add:
values.put("string", DateFormat.getDateTimeInstance().format(new Date()));
getContentResolver().insert(Uri.parse("content://us.lynch.listdemo/data"), values);
return true;
case R.id.action_clear:
getContentResolver().delete(Uri.parse("content://us.lynch.listdemo/data"), null, null);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri uri = Uri.parse("content://us.lynch.listdemo/data");
String[] projection = { "_id", "string" };
return new CursorLoader(this, uri, projection, null, null, "_id");
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
((CursorAdapter) getListAdapter()).swapCursor(cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
((CursorAdapter) getListAdapter()).swapCursor(null);
}
}
ContentProvider.java
package us.lynch.listdemo;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.text.TextUtils;
public class ContentProvider extends android.content.ContentProvider {
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private Database mDatabase;
private static final int MATCH = 1;
private static final int MATCH_ID = 2;
static {
sUriMatcher.addURI("us.lynch.listdemo", "data", MATCH);
sUriMatcher.addURI("us.lynch.listdemo", "data/#", MATCH_ID);
}
@Override
public boolean onCreate() {
mDatabase = new Database(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
switch (sUriMatcher.match(uri)) {
case MATCH:
return database().query("data", projection, selection, selectionArgs, null, null, sortOrder);
case MATCH_ID:
selection = addIdToSelection(uri, selection);
return database().query("data", projection, selection, selectionArgs, null, null, sortOrder);
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
switch (sUriMatcher.match(uri)) {
case MATCH: {
return ContentUris.withAppendedId(uri, database().insertOrThrow("data", null, values));
}
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
switch (sUriMatcher.match(uri)) {
case MATCH:
return database().delete("data", selection, selectionArgs);
case MATCH_ID:
selection = addIdToSelection(uri, selection);
return database().delete("data", selection, selectionArgs);
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
switch (sUriMatcher.match(uri)) {
case MATCH:
return database().update("data", values, selection, selectionArgs);
case MATCH_ID:
selection = addIdToSelection(uri, selection);
return database().update("data", values, selection, selectionArgs);
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
@Override
public String getType(Uri uri) {
throw new IllegalArgumentException("Unknown URI " + uri);
}
private SQLiteDatabase database() {
return mDatabase.getWritableDatabase();
}
private static String addIdToSelection(Uri uri, String selection) {
return DatabaseUtils.concatenateWhere(selection, "_id = " + Long.toString(ContentUris.parseId(uri)));
}
}
class Database extends SQLiteOpenHelper {
Database(Context context) {
super(context, "database", null, 1);
}
@Override
public void onCreate(SQLiteDatabase database) {
final String[] columns = new String[] {
"_id integer primary key autoincrement",
"string text not null",
};
database.execSQL("create table data (" + TextUtils.join(",", columns) + ");");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
assert (false);
}
}
res/layout/listitem.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:text="Large Text"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
res/menu/main.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/action_clear"
android:title="Clear Items"
android:visible="true">
</item>
<item
android:id="@+id/action_add"
android:title="Add Item"
android:visible="true">
</item>
</menu>
回答1:
You don't use the Uri
notification mechanism.
In the query() method write:
Cursor cursor = database().query("data", projection, selection, selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
In the methods where content is modified (insert, update, delete), notify the same Uri:
ContentResolver cr = getContext().getContentResolver();
cr.notifyChange(uri, null);
来源:https://stackoverflow.com/questions/24087741/listactivity-doesnt-update-as-data-arrives