Android : Content Provider 一次添加两行
Android : Content Provider adds two rows at a time
我在 Content Providers 上关注了这个 tutorial,它运行良好。
我自己尝试了一些东西并使用 Loaders 加载数据。而且我无法实现以下目标:
- 不显示消息的 "body" 文本,仅显示 ID 和时间戳。 (即使时间戳是字符串但包含数字)
- 最大的问题:2行相同的值一次添加一个
因此,每次我单击 发送消息 按钮时,它都会添加两行。
这是 logcat 文件。
07-17 02:57:18.307 11619-11619/poketpixels.reminder D/DB: 1, , 1468526093018
07-17 02:57:18.307 11619-11619/poketpixels.reminder D/DB: 2, , 1468526093018
07-17 02:57:18.307 11619-11619/poketpixels.reminder D/DB: 3, , 1468526292717
07-17 02:57:18.307 11619-11619/poketpixels.reminder D/DB: 4, , 1468526292717
07-17 02:57:18.307 11619-11619/poketpixels.reminder D/DB: 5, , 1468696845898
07-17 02:57:18.307 11619-11619/poketpixels.reminder D/DB: 6, , 1468696845898
注意BODY没有文字显示,两行的时间戳是相等的。如此自然,同时创建。
以下是 activity 文件:
public class ActivityChat extends AppCompatActivity
implements LoaderManager.LoaderCallbacks<Cursor> {
private static final int URL_LOADER = 0;
String URL = "content://something/messages";
Button sendMessage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
sendMessage = (Button)findViewById(R.id.btnSend);
getLoaderManager().initLoader(URL_LOADER, null, this);
sendMessage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SendMessage();
}
});
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
CursorLoader loader = new CursorLoader(
this,
Uri.parse(URL),
null,
null,
null,
null);
return loader;
}
public void onLoadFinished(
Loader<Cursor> loader,
Cursor c) {
// LOADS ALL ROWS
if (c.moveToFirst()) {
do {
Log.d("DB",
c.getString(c.getColumnIndex(MessageProvider._ID)) +
", " + c.getString(c.getColumnIndex(MessageProvider.BODY)) +
", " + c.getString(c.getColumnIndex(MessageProvider.CREATED_AT)) + "\n"
);
} while (c.moveToNext());
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
public void SendMessage() {
// Add a new message record
ContentValues values = new ContentValues();
values.put(MessageProvider.BODY,
((EditText)findViewById(R.id.eTmessageBody)).getText().toString());
values.put(MessageProvider.CREATED_AT,
(System.currentTimeMillis()));
Log.d("DB", "SENT ALREADY");
Uri messages = Uri.parse(URL);
Cursor c = getContentResolver().query(messages, null, null, null, "created_at");
//loads only the last row
c.moveToLast();
Log.d("LAST ROW",c.getString(c.getColumnIndex(MessageProvider._ID)) +
", " + c.getString(c.getColumnIndex( MessageProvider.BODY)) +
", " + c.getString(c.getColumnIndex( MessageProvider.CREATED_AT)) );
}
}
MessageProvider.java
public class MessageProvider extends ContentProvider {
static final String PROVIDER_NAME = "something";
static final String URL = "content://" + PROVIDER_NAME + "/messages";
public static final Uri CONTENT_URI = Uri.parse(URL);
public static final String _ID = "_id";
public static final String BODY = "body";
public static final String CREATED_AT = "created_at";
private static HashMap<String, String> MESSAGES_PROJECTION_MAP;
static final int MESSAGES = 1;
static final int MESSAGE_ID = 2;
static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(PROVIDER_NAME, "messages", MESSAGES);
uriMatcher.addURI(PROVIDER_NAME, "messages/#", MESSAGE_ID);
}
/**
* Database specific constant declarations
*/
private SQLiteDatabase db;
static final String DATABASE_NAME = "test";
static final String MESSAGES_TABLE_NAME = "message";
static final int DATABASE_VERSION = 1;
static final String CREATE_DB_TABLE =
" CREATE TABLE " + MESSAGES_TABLE_NAME +
" (_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
" body TEXT NOT NULL, " +
" created_at TEXT NOT NULL);";
/**
* Helper class that actually creates and manages
* the provider's underlying data repository.
*/
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_DB_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + MESSAGES_TABLE_NAME);
onCreate(db);
}
}
@Override
public boolean onCreate() {
Context context = getContext();
DatabaseHelper dbHelper = new DatabaseHelper(context);
/**
* Create a write able database which will trigger its
* creation if it doesn't already exist.
*/
db = dbHelper.getWritableDatabase();
return (db == null) ? false : true;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
final long objectId = db.insertOrThrow(MessageProvider.MESSAGES_TABLE_NAME, null, values);
final Uri newObjectUri = ContentUris.withAppendedId(CONTENT_URI, objectId);
getContext().getContentResolver().notifyChange(newObjectUri, null);
/**
* Add a new message record
*/
long rowID = db.insert(MESSAGES_TABLE_NAME, "", values);
/**
* If record is added successfully
*/
if (rowID > 0) {
Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
getContext().getContentResolver().notifyChange(_uri, null);
return _uri;
}
throw new SQLException("Failed to add a record into " + uri);
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(MESSAGES_TABLE_NAME);
switch (uriMatcher.match(uri)) {
case MESSAGES:
qb.setProjectionMap(MESSAGES_PROJECTION_MAP);
break;
case MESSAGE_ID:
qb.appendWhere(_ID + "=" + uri.getPathSegments().get(1));
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (sortOrder == null || sortOrder == "") {
/**
* By default sort on time
*/
sortOrder = CREATED_AT;
}
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
/**
* register to watch a content URI for changes
*/
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
//public int delete(String table, String whereClause, String[] whereArgs) {
final long objectId = db.delete(MessageProvider.MESSAGES_TABLE_NAME, null, null);
final Uri newObjectUri = ContentUris.withAppendedId(CONTENT_URI, objectId);
getContext().getContentResolver().notifyChange(newObjectUri, null);
switch (uriMatcher.match(uri)) {
case MESSAGES:
count = db.delete(MESSAGES_TABLE_NAME, selection, selectionArgs);
break;
case MESSAGE_ID:
String id = uri.getPathSegments().get(1);
count = db.delete(MESSAGES_TABLE_NAME, _ID + " = " + id +
(!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int count = 0;
final long objectId = db.insertOrThrow(MessageProvider.MESSAGES_TABLE_NAME, null, values);
final Uri newObjectUri = ContentUris.withAppendedId(CONTENT_URI, objectId);
getContext().getContentResolver().notifyChange(newObjectUri, null);
switch (uriMatcher.match(uri)) {
case MESSAGES:
count = db.update(MESSAGES_TABLE_NAME, values, selection, selectionArgs);
break;
case MESSAGE_ID:
count = db.update(MESSAGES_TABLE_NAME, values, _ID + " = " + uri.getPathSegments().get(1) +
(!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
/**
* Get all message records
*/
case MESSAGES:
return "vnd.android.cursor.dir/vnd.poketpixels.reminder.messages";
/**
* Get a particular message
*/
case MESSAGE_ID:
return "vnd.android.cursor.item/vnd.poketpixels.reminder.messages";
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
}
}
所以这两个问题是 :
- 我要显示字符串.
- 在一个时间点向数据库添加了两行
编辑:
第二个问题解决了。
但是,我仍然无法显示字符串。在 logcat.
中显示空值
您在插入方法中插入了两次记录。
one by using : final long objectId = db.insertOrThrow(MessageProvider.MESSAGES_TABLE_NAME, null, values);
Another by : long rowID = db.insert(MESSAGES_TABLE_NAME, "", values);
从 class MessageProvider 的插入方法中删除上述行之一。
我在 Content Providers 上关注了这个 tutorial,它运行良好。 我自己尝试了一些东西并使用 Loaders 加载数据。而且我无法实现以下目标:
- 不显示消息的 "body" 文本,仅显示 ID 和时间戳。 (即使时间戳是字符串但包含数字)
- 最大的问题:2行相同的值一次添加一个
因此,每次我单击 发送消息 按钮时,它都会添加两行。 这是 logcat 文件。
07-17 02:57:18.307 11619-11619/poketpixels.reminder D/DB: 1, , 1468526093018
07-17 02:57:18.307 11619-11619/poketpixels.reminder D/DB: 2, , 1468526093018
07-17 02:57:18.307 11619-11619/poketpixels.reminder D/DB: 3, , 1468526292717
07-17 02:57:18.307 11619-11619/poketpixels.reminder D/DB: 4, , 1468526292717
07-17 02:57:18.307 11619-11619/poketpixels.reminder D/DB: 5, , 1468696845898
07-17 02:57:18.307 11619-11619/poketpixels.reminder D/DB: 6, , 1468696845898
注意BODY没有文字显示,两行的时间戳是相等的。如此自然,同时创建。
以下是 activity 文件:
public class ActivityChat extends AppCompatActivity
implements LoaderManager.LoaderCallbacks<Cursor> {
private static final int URL_LOADER = 0;
String URL = "content://something/messages";
Button sendMessage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
sendMessage = (Button)findViewById(R.id.btnSend);
getLoaderManager().initLoader(URL_LOADER, null, this);
sendMessage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SendMessage();
}
});
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
CursorLoader loader = new CursorLoader(
this,
Uri.parse(URL),
null,
null,
null,
null);
return loader;
}
public void onLoadFinished(
Loader<Cursor> loader,
Cursor c) {
// LOADS ALL ROWS
if (c.moveToFirst()) {
do {
Log.d("DB",
c.getString(c.getColumnIndex(MessageProvider._ID)) +
", " + c.getString(c.getColumnIndex(MessageProvider.BODY)) +
", " + c.getString(c.getColumnIndex(MessageProvider.CREATED_AT)) + "\n"
);
} while (c.moveToNext());
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
public void SendMessage() {
// Add a new message record
ContentValues values = new ContentValues();
values.put(MessageProvider.BODY,
((EditText)findViewById(R.id.eTmessageBody)).getText().toString());
values.put(MessageProvider.CREATED_AT,
(System.currentTimeMillis()));
Log.d("DB", "SENT ALREADY");
Uri messages = Uri.parse(URL);
Cursor c = getContentResolver().query(messages, null, null, null, "created_at");
//loads only the last row
c.moveToLast();
Log.d("LAST ROW",c.getString(c.getColumnIndex(MessageProvider._ID)) +
", " + c.getString(c.getColumnIndex( MessageProvider.BODY)) +
", " + c.getString(c.getColumnIndex( MessageProvider.CREATED_AT)) );
}
}
MessageProvider.java
public class MessageProvider extends ContentProvider {
static final String PROVIDER_NAME = "something";
static final String URL = "content://" + PROVIDER_NAME + "/messages";
public static final Uri CONTENT_URI = Uri.parse(URL);
public static final String _ID = "_id";
public static final String BODY = "body";
public static final String CREATED_AT = "created_at";
private static HashMap<String, String> MESSAGES_PROJECTION_MAP;
static final int MESSAGES = 1;
static final int MESSAGE_ID = 2;
static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(PROVIDER_NAME, "messages", MESSAGES);
uriMatcher.addURI(PROVIDER_NAME, "messages/#", MESSAGE_ID);
}
/**
* Database specific constant declarations
*/
private SQLiteDatabase db;
static final String DATABASE_NAME = "test";
static final String MESSAGES_TABLE_NAME = "message";
static final int DATABASE_VERSION = 1;
static final String CREATE_DB_TABLE =
" CREATE TABLE " + MESSAGES_TABLE_NAME +
" (_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
" body TEXT NOT NULL, " +
" created_at TEXT NOT NULL);";
/**
* Helper class that actually creates and manages
* the provider's underlying data repository.
*/
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_DB_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + MESSAGES_TABLE_NAME);
onCreate(db);
}
}
@Override
public boolean onCreate() {
Context context = getContext();
DatabaseHelper dbHelper = new DatabaseHelper(context);
/**
* Create a write able database which will trigger its
* creation if it doesn't already exist.
*/
db = dbHelper.getWritableDatabase();
return (db == null) ? false : true;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
final long objectId = db.insertOrThrow(MessageProvider.MESSAGES_TABLE_NAME, null, values);
final Uri newObjectUri = ContentUris.withAppendedId(CONTENT_URI, objectId);
getContext().getContentResolver().notifyChange(newObjectUri, null);
/**
* Add a new message record
*/
long rowID = db.insert(MESSAGES_TABLE_NAME, "", values);
/**
* If record is added successfully
*/
if (rowID > 0) {
Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
getContext().getContentResolver().notifyChange(_uri, null);
return _uri;
}
throw new SQLException("Failed to add a record into " + uri);
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(MESSAGES_TABLE_NAME);
switch (uriMatcher.match(uri)) {
case MESSAGES:
qb.setProjectionMap(MESSAGES_PROJECTION_MAP);
break;
case MESSAGE_ID:
qb.appendWhere(_ID + "=" + uri.getPathSegments().get(1));
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (sortOrder == null || sortOrder == "") {
/**
* By default sort on time
*/
sortOrder = CREATED_AT;
}
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
/**
* register to watch a content URI for changes
*/
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
//public int delete(String table, String whereClause, String[] whereArgs) {
final long objectId = db.delete(MessageProvider.MESSAGES_TABLE_NAME, null, null);
final Uri newObjectUri = ContentUris.withAppendedId(CONTENT_URI, objectId);
getContext().getContentResolver().notifyChange(newObjectUri, null);
switch (uriMatcher.match(uri)) {
case MESSAGES:
count = db.delete(MESSAGES_TABLE_NAME, selection, selectionArgs);
break;
case MESSAGE_ID:
String id = uri.getPathSegments().get(1);
count = db.delete(MESSAGES_TABLE_NAME, _ID + " = " + id +
(!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int count = 0;
final long objectId = db.insertOrThrow(MessageProvider.MESSAGES_TABLE_NAME, null, values);
final Uri newObjectUri = ContentUris.withAppendedId(CONTENT_URI, objectId);
getContext().getContentResolver().notifyChange(newObjectUri, null);
switch (uriMatcher.match(uri)) {
case MESSAGES:
count = db.update(MESSAGES_TABLE_NAME, values, selection, selectionArgs);
break;
case MESSAGE_ID:
count = db.update(MESSAGES_TABLE_NAME, values, _ID + " = " + uri.getPathSegments().get(1) +
(!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
/**
* Get all message records
*/
case MESSAGES:
return "vnd.android.cursor.dir/vnd.poketpixels.reminder.messages";
/**
* Get a particular message
*/
case MESSAGE_ID:
return "vnd.android.cursor.item/vnd.poketpixels.reminder.messages";
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
}
}
所以这两个问题是 :
- 我要显示字符串.
- 在一个时间点向数据库添加了两行
编辑:
第二个问题解决了。 但是,我仍然无法显示字符串。在 logcat.
中显示空值您在插入方法中插入了两次记录。
one by using : final long objectId = db.insertOrThrow(MessageProvider.MESSAGES_TABLE_NAME, null, values);
Another by : long rowID = db.insert(MESSAGES_TABLE_NAME, "", values);
从 class MessageProvider 的插入方法中删除上述行之一。