I am updating my app to use a Content Provider and a CursorLoader in place of using startManagingCursor.
I have 2 tables in my sqlite database, table errors and table messages. When i start the activities that show the contents of these tables in a listview, everything seems to work fine.
One thing i have noticed is that when i call the viewMessages Activity the Case statement that is matching the URI in the content provider is getting called twice.
This does not happen when a call viewPhoneNumbers activity.
It seems like i am calling the following twice
cursorLoader = new CursorLoader(this, RR3ContentProvider.CONTENT_URI_MESSAGES, projection, null, null, null);
I'll show the logging statements first so you can see what i mean. It logs 'CASE Messages' twice, but when i do the same for the phone table it just logs 'Case PHONES' only once, which i think it should only do it once.
Can anyone tell me why the CASE MESSAGES is executing twice?
07-22 10:17:11.845: E/ViewMessagesActivityAsync(10808): inside ViewMessagesActivityAsync oncreate
07-22 10:17:11.845: E/RR3ContentProvider(10808): inside RR3ContentProvider query method
07-22 10:17:11.845: E/RR3ContentProvider(10808): CASE MESSAGES
07-22 10:17:11.845: E/RR3ContentProvider(10808): About to do the query method in content provider
07-22 10:17:11.855: E/ViewMessagesActivityAsync(10808): inside3 onCreateLoader in ViewMessagesActivityAsync
07-22 10:17:11.855: E/RR3ContentProvider(10808): inside RR3ContentProvider query method
07-22 10:17:11.855: E/RR3ContentProvider(10808): CASE MESSAGES
07-22 10:17:11.855: E/RR3ContentProvider(10808): About to do the query method in content provider
07-22 10:17:11.915: V/PhoneStatusBar(1219): setLightsOn(true)
07-22 10:17:11.945: E/ViewMessagesActivityAsync(10808): inside myadapter getview for messages
07-22 10:17:11.945: E/ViewMessagesActivityAsync(10808): (Cursor)getItem(position) = android.content.ContentResolver$CursorWrapperInner@67a0e5b8position = 0
07-22 10:17:11.945: E/ViewMessagesActivityAsync(10808): inside myadapter getview for messages
07-22 10:17:11.945: E/ViewMessagesActivityAsync(10808): (Cursor)getItem(position) = android.content.ContentResolver$CursorWrapperInner@67a0e5b8position = 1
07-22 10:17:11.955: E/ViewMessagesActivityAsync(10808): inside myadapter getview for messages
07-22 10:17:11.955: E/ViewMessagesActivityAsync(10808): (Cursor)getItem(position) = android.content.ContentResolver$CursorWrapperInner@67a0e5b8position = 2
.
07-22 10:17:21.345: E/ViewPhoneNumberAsync(10808): inside ViewPhoneNumberAsync onCreate
07-22 10:17:21.345: E/RR3ContentProvider(10808): inside RR3ContentProvider query method
07-22 10:17:21.345: E/RR3ContentProvider(10808): CASE PHONES
07-22 10:17:21.345: E/RR3ContentProvider(10808): About to do the query method in content provider
07-22 10:17:21.405: V/PhoneStatusBar(1219): setLightsOn(true)
07-22 10:17:21.425: E/ViewPhoneNumberAsync(10808): inside myadapter getview
07-22 10:17:21.435: E/ViewPhoneNumberAsync(10808): inside myadapter getview
. ViewPhoneNumberAsync
public class ViewPhoneNumberAsync extends Activity implements LoaderCallbacks<Cursor> {
ListView listView;
MyAdapter mAdapter;
LoaderManager loadermanager;
CursorLoader cursorLoader;
private static final String TAG = ViewPhoneNumberAsync.class.getSimpleName();
final String BACKPRESS_ACTION = "com.carefreegroup.rr3.BACKPRESS_ACTION";
private final String TABLEPHONE = "phone";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e(TAG, "inside ViewPhoneNumberAsync onCreate");
loadermanager=getLoaderManager();
// setup UI
setContentView(R.layout.viewphonenumbers);
//transactionCount = (TextView)findViewById(R.id.textviewtransactionsfordaycount);
listView = (ListView) findViewById(R.id.listviewphonenumbers);
String[] from = { LoginValidate.C_PHONE_NAME, LoginValidate.C_PHONE_NUMBER};
int[] to = { R.id.phonename, R.id.phonenumber };
// String[] uiBindFrom = { TABLEPHONE};
// int[] uiBindTo = {android.R.id.text1};
/*Empty adapter that is used to display the loaded data*/
mAdapter = new MyAdapter(this, R.layout.rowphonenumbers, null, from, to,0);
listView.setAdapter(mAdapter);
listView.setOnItemClickListener(mAdapter);
loadermanager.initLoader(1, null, this);
}
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
String[] projection = { LoginValidate.C_ID_PHONE, LoginValidate.C_PHONE_NAME, LoginValidate.C_PHONE_NUMBER };
cursorLoader = new CursorLoader(this, RR3ContentProvider.CONTENT_URI_PHONE_NUMBERS, projection, null, null, null);
return cursorLoader;
}
public void onLoadFinished(Loader<Cursor> loader,Cursor cursor) {
if(mAdapter!=null && cursor!=null)
mAdapter.swapCursor(cursor); //swap the new cursor in.
else
Log.v(TAG,"OnLoadFinished: mAdapter is null");
}
public void onLoaderReset(Loader<Cursor> arg0) {
if(mAdapter!=null)
mAdapter.swapCursor(null);
else
Log.v(TAG,"OnLoadFinished: mAdapter is null");
}
@Override
public void onBackPressed() {
super.onBackPressed();
Intent i = new Intent(this, NfcscannerActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//i.setAction(QRCODE_ACTION);
i.setAction(BACKPRESS_ACTION);
startActivity(i);
}
private class MyAdapter extends SimpleCursorAdapter implements OnItemClickListener {
public MyAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
}
.
ViewMessagesAsync
public class ViewMessagesActivityAsync extends Activity implements LoaderCallbacks<Cursor>{
private static final String TAG = ViewMessagesActivityAsync.class.getSimpleName();
final String BACKPRESS_ACTION = "com.carefreegroup.rr3.BACKPRESS_ACTION";
NfcScannerApplication nfcscannerapplication;
Cursor cursorMessages;
ListView listView;
SimpleCursorAdapter adapter;
MyAdapter myAdapter;
TextView noMessages;
ArrayList<String> guidArray;
LoaderManager loadermanager;
CursorLoader cursorLoader;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e(TAG, "inside ViewMessagesActivityAsync oncreate");
setContentView(R.layout.viewmessages);
nfcscannerapplication = (NfcScannerApplication) getApplication();
loadermanager=getLoaderManager();
guidArray = new ArrayList<String>();
listView = (ListView) findViewById(R.id.listviewmessages);
noMessages = (TextView)findViewById(R.id.textviewnomessageslabel);
cursorMessages = getContentResolver().query(RR3ContentProvider.CONTENT_URI_MESSAGES, null, null, null, null);
if(cursorMessages.getCount() == 0){
noMessages.setVisibility(View.VISIBLE);
listView.setVisibility(View.GONE);
}else{
listView.setVisibility(View.VISIBLE);
noMessages.setVisibility(View.GONE);
}
String[] from = { LoginValidate.C_MESSAGE_CREATED_AT, LoginValidate.C_MESSAGE_TEXT, LoginValidate.C_MESSAGE_SENDER};
int[] to = { R.id.messagecreatedat, R.id.messagetext, R.id.messagesender};
myAdapter = new MyAdapter(nfcscannerapplication, R.layout.rowmessages, null, from, to, 0);
listView.setAdapter(myAdapter);
listView.setOnItemClickListener(myAdapter);
loadermanager.initLoader(1, null, this);
}//end of onCreate
@Override
protected void onResume() {
super.onResume();
}
private class MyAdapter extends SimpleCursorAdapter implements OnItemClickListener {
Cursor c;
String messageGuid;
public MyAdapter(Context context, int layout, Cursor c, String[] from,
int[] to, int flags) {
super(context, layout, c, from, to, flags);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Log.e(TAG, "inside3 onCreateLoader in ViewMessagesActivityAsync");
String[] projection = { LoginValidate.C_ID_MESSAGE,LoginValidate.C_MESSAGE_GUID,LoginValidate.C_MESSAGE_CREATED_AT, LoginValidate.C_MESSAGE_TEXT,
LoginValidate.C_MESSAGE_SENDER , LoginValidate.C_MESSAGE_REPLIED, LoginValidate.C_MESSAGE_SEEN, LoginValidate.C_MESSAGE_DISPLAYED,
LoginValidate.C_MESSAGE_REPLY_MESSAGE, LoginValidate.C_MESSAGE_IS_STANDALONE};
cursorLoader = new CursorLoader(this, RR3ContentProvider.CONTENT_URI_MESSAGES, projection, null, null, null);
return cursorLoader;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if(myAdapter!=null && cursor!=null)
myAdapter.swapCursor(cursor); //swap the new cursor in.
else
Log.v(TAG,"OnLoadFinished: mAdapter is null");
}
@Override
public void onLoaderReset(Loader<Cursor> arg0) {
if(myAdapter!=null)
myAdapter.swapCursor(null);
else
Log.v(TAG,"OnLoadFinished: mAdapter is null");
}
}
ContentProvider
public class RR3ContentProvider extends ContentProvider {
private static final String TAG = RR3ContentProvider.class.getSimpleName();
NfcScannerApplication nfcAppObj;
static final String PROVIDER_NAME = "com.carefreegroup.rr3.ContentProvider";
static final String URLPHONENUMBERS = "content://" + PROVIDER_NAME + "/phonenumbers";
static final Uri CONTENT_URI_PHONE_NUMBERS = Uri.parse(URLPHONENUMBERS);
static final String _ID_PHONENUMBERS = "_id";
static final String PHONE_NAME = "phonename";
static final String PHONE_NUMBER = "phonenumber";
static final String TABLEPHONE = "phone";
private static HashMap<String, String> PHONE_PROJECTION_MAP;
static final int PHONES = 1;
static final int PHONE_ID = 2;
static final String URLMESSAGES = "content://" + PROVIDER_NAME + "/messages";
static final Uri CONTENT_URI_MESSAGES = Uri.parse(URLMESSAGES);
static final String _ID_MESSAGES = "_id";
static final String MESSAGE_CREATED_AT = "messagecreatedat";
static final String MESSAGE_TEXT = "messagetext";
static final String MESSAGE_SENDER = "messagesender";
static final String TABLEMESSAGE = "message";
private static HashMap<String, String> MESSAGE_PROJECTION_MAP;
static final int MESSAGES = 3;
static final int MESSAGE_ID = 4;
static final UriMatcher uriMatcher;
static{
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(PROVIDER_NAME, "phonenumbers", PHONES);
uriMatcher.addURI(PROVIDER_NAME, "phonenumbers/#", PHONE_ID);
uriMatcher.addURI(PROVIDER_NAME, "messages", MESSAGES);
uriMatcher.addURI(PROVIDER_NAME, "messages/#", MESSAGE_ID);
}
/**
* Database specific constant declarations
*/
private SQLiteDatabase db;
@Override
public boolean onCreate() {
Context context = getContext();
nfcAppObj = (NfcScannerApplication) getContext();
Log.e(TAG, "inside RR3ContentProvider onCreate");
return (nfcAppObj == null)? false:true;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
db = nfcAppObj.getDb();
/**
* Add a new student record
*/
long rowID = db.insert( TABLEPHONE, "", values);
/**
* If record is added successfully
*/
if (rowID > 0)
{
Uri _uri = ContentUris.withAppendedId(CONTENT_URI_PHONE_NUMBERS, 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) {
Log.e(TAG, "inside RR3ContentProvider query method");
db = nfcAppObj.getDb();
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
switch (uriMatcher.match(uri)) {
case PHONES:
Log.e(TAG, "CASE PHONES");
qb.setTables(TABLEPHONE);
qb.setProjectionMap(PHONE_PROJECTION_MAP);
break;
case PHONE_ID:
Log.e(TAG, "CASE PHONE_ID");
qb.setTables(TABLEPHONE);
qb.appendWhere( _ID_PHONENUMBERS + "=" + uri.getPathSegments().get(1));
break;
case MESSAGES:
Log.e(TAG, "CASE MESSAGES");
qb.setTables(TABLEMESSAGE);
qb.setProjectionMap(MESSAGE_PROJECTION_MAP);
break;
case MESSAGE_ID:
Log.e(TAG, "CASE MESAAGE_ID");
qb.setTables(TABLEMESSAGE);
qb.appendWhere( _ID_MESSAGES + "=" + uri.getPathSegments().get(1));
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
switch (uriMatcher.match(uri)) {
case PHONES:
if (sortOrder == null || sortOrder == ""){
sortOrder = PHONE_NAME;
}
break;
case PHONE_ID:
if (sortOrder == null || sortOrder == ""){
sortOrder = PHONE_NAME;
}
break;
case MESSAGES:
if (sortOrder == null || sortOrder == ""){
sortOrder = MESSAGE_CREATED_AT + " DESC";
}
break;
case MESSAGE_ID:
if (sortOrder == null || sortOrder == ""){
sortOrder = MESSAGE_CREATED_AT + " DESC";
}
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
Log.e(TAG, "About to do the query method in content provider");
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;
}//end of query
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
db = nfcAppObj.getDb();
int count = 0;
switch (uriMatcher.match(uri)){
case PHONES:
count = db.delete(TABLEPHONE, selection, selectionArgs);
break;
case PHONE_ID:
String id = uri.getPathSegments().get(1);
count = db.delete( TABLEPHONE, _ID_PHONENUMBERS + " = " + id +
(!TextUtils.isEmpty(selection) ? " AND (" +
selection + ')' : ""), selectionArgs);
break;
case MESSAGES:
count = db.delete(TABLEMESSAGE, selection, selectionArgs);
break;
case MESSAGE_ID:
String id2 = uri.getPathSegments().get(1);
count = db.delete( TABLEMESSAGE, _ID_MESSAGES + " = " + id2 +
(!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) {
db = nfcAppObj.getDb();
int count = 0;
switch (uriMatcher.match(uri)){
case PHONES:
count = db.update(TABLEPHONE, values,
selection, selectionArgs);
break;
case PHONE_ID:
count = db.update(TABLEPHONE, values, _ID_PHONENUMBERS +
" = " + uri.getPathSegments().get(1) +
(!TextUtils.isEmpty(selection) ? " AND (" +
selection + ')' : ""), selectionArgs);
break;
case MESSAGES:
count = db.update(TABLEMESSAGE, values,
selection, selectionArgs);
break;
case MESSAGE_ID:
count = db.update(TABLEMESSAGE, values, _ID_MESSAGES +
" = " + 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;
}//end of delete
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)){
/**
* Get all records
*/
case PHONES:
return "vnd.android.cursor.dir/vnd.example.phonenumbers";
/**
* Get a particular record
*/
case PHONE_ID:
return "vnd.android.cursor.item/vnd.example.phonenumbers";
/**
* Get all records
*/
case MESSAGES:
return "vnd.android.cursor.dir/vnd.example.messages";
/**
* Get a particular record
*/
case MESSAGE_ID:
return "vnd.android.cursor.item/vnd.example.messages";
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
}
}
SOLVED
My appologies, i make an extra call to the content provider passing in
RR3ContentProvider.CONTENT_URI_MESSAGES
in the viewMessagesAsync Activity. This is to get the count of the messages, where i can switch views if there are no messages. i don't do this in the ViewPhoneNumbers Activity.