Reputation: 306
Context:
In Android, I am trying to implement a single source for all contacts DataSet<ContactEntry>
. So when any of the contacts are updated by sync process, they should be reflected in existing adapters (Only if there is an active adapter).
However, all the updates/additions/removals from sync are being ignored as the sync always find DataSet<ContactEntry>
null (even though its not null).
Source Code:
public class ContactsDataSet {
private static final String TAG = "ContactsDataSet";
private static final Object lock = new Object();
private static volatile WeakReference<DataSet<ContactEntry>> instance = null;
public static DataSet<ContactEntry> getInstance(Context context) {
synchronized (lock) {
DataSet<ContactEntry> dataSet;
if (instance == null) {
Log.d(TAG, "Creating instance for ContactsDataSet");
dataSet = createDataSet(context);
instance = new WeakReference<>(dataSet);
Log.d(TAG, "Set instance to: " + instance + " on " + Thread.currentThread().getName());
} else {
dataSet = instance.get();
if (dataSet == null) {
Log.d(TAG, "Re-creating Data Set");
dataSet = createDataSet(context);
instance = new WeakReference<>(dataSet);
Log.d(TAG, "Set instance to: " + instance + " on " + Thread.currentThread().getName());
} else {
Log.d(TAG, "Valid instance: " + instance + " on " + Thread.currentThread().getName());
}
}
return dataSet;
}
}
public static void printInstance() {
synchronized (lock) {
Log.d(TAG, "printInstance() => " + instance + " on " + Thread.currentThread().getName());
}
}
private static DataSet<ContactEntry> getInstance() {
synchronized (lock) {
DataSet<ContactEntry> ret= instance == null ? null : instance.get();
Log.d(TAG, "getInstance() => " + instance + " on " + Thread.currentThread().getName());
return ret;
}
}
private static DataSet<ContactEntry> createDataSet(Context context) {
Log.d(TAG, "Fetching contacts from DB");
return new DataSet<>(DbHelper.getInstance(context).fetchValidContacts());
}
public static void addContact(ContactEntry contact) {
if (contact.isDirty() || TextUtils.isEmpty(contact.getUserNumber())) {
Log.d(TAG, "Ignoring dirty/incomplete contact");
return;
}
synchronized (lock) {
DataSet<ContactEntry> dataSet = getInstance();
if (dataSet != null) {
Log.d(TAG, "Adding " + contact + " to Cache");
dataSet.addItem(contact);
} else {
Log.d(TAG, "Ignoring new Contact " + contact + " as instance: " + instance + " isn't valid on " + Thread.currentThread().getName());
}
}
}
public static void updateContact(ContactEntry contact) {
if (contact.isDirty() || TextUtils.isEmpty(contact.getUserNumber())) {
Log.d(TAG, "Removing dirty/incomplete contact");
deleteContact(contact.getContactId());
}
synchronized (lock) {
DataSet<ContactEntry> dataSet = getInstance();
if (dataSet != null) {
Log.d(TAG, "Updating " + contact + " in Cache");
dataSet.updateItem(contact);
} else {
Log.d(TAG, "Ignoring update Contact " + contact + " as instance: " + instance + " isn't valid on " + Thread.currentThread().getName());
}
}
}
public static void deleteContact(int contactId) {
synchronized (lock) {
DataSet<ContactEntry> dataSet = getInstance();
if (dataSet != null) {
Log.d(TAG, "Removing " + contactId + " from Cache");
dataSet.removeItem(contactId);
} else {
Log.d(TAG, "Ignoring delete Contact " + contactId + " as instance: " + instance + " isn't valid on " + Thread.currentThread().getName());
}
}
}
}
DataSet
class is a simple list with list of Observers.
When getInstance(Context context)
is invoked, instance
always has a value (ofcourse, not the first time).
However, when getInstance()
is invoked from any of the methods addContact
, updateContact
or deleteContact
, static variable instance
always has value null
(Even when the thread name is same: main).
I have tried by removing Multidex support, making instance
variable volatile
but couldn't get it working. Any help would be hightly apperciated. Thanks in advance!
Logs:
When I first invoke Adapter:
09-03 18:01:04.367 18369-18369/in.yyyyyyy.xxxxx D/ContactsAdapter: ContactsAdapter()
09-03 18:01:04.369 18369-18369/in.yyyyyyy.xxxxx D/ContactsDataSet: Creating instance for ContactsDataSet
09-03 18:01:04.369 18369-18369/in.yyyyyyy.xxxxx D/ContactsDataSet: Fetching contacts from DB
09-03 18:01:04.398 18369-18369/in.yyyyyyy.xxxxx I/ContactEntry: fetchValidContacts() => [ContactEntry { contactId: 1, contactVersion: 4, phoneNumber: +999999999999, email: null, userNumber: 7dc5baec-1e08-43f4-b124-8d65d097036e, name: CCCCCCCCCCCCCC, dirty: false }, ContactEntry { contactId: 3, contactVersion: 3, phoneNumber: +333333333333, email: null, userNumber: 13d7b667-b523-41b7-ba89-203139e0dba9, name: GGGGGG, dirty: false }, ContactEntry { contactId: 4, contactVersion: 20, phoneNumber: +999999999999, email: null, userNumber: 7dc5baec-1e08-43f4-b124-8d65d097036e, name: MMMMMM, dirty: false }]
09-03 18:01:04.398 18369-18369/in.yyyyyyy.xxxxx D/ContactsDataSet: Set instance to: java.lang.ref.WeakReference@90e9b43 on main
09-03 18:01:04.398 18369-18369/in.yyyyyyy.xxxxx D/ContactsDataSet: printInstance() => java.lang.ref.WeakReference@90e9b43 on main
Triggered Sync (after updating 3rd contact - MMMMMM):
09-03 18:02:43.986 28748-28748/in.yyyyyyy.xxxxx:sync D/SyncContacts: Updating User Number for ContactEntry { contactId: 4, contactVersion: 21, phoneNumber: +333333333333, email: null, userNumber: null, name: MMMMMM, dirty: true } with 13d7b667-b523-41b7-ba89-203139e0dba9
09-03 18:02:43.986 28748-28748/in.yyyyyyy.xxxxx:sync I/ContactEntry: updateAccount(ContactEntry { contactId: 4, contactVersion: 21, phoneNumber: +333333333333, email: null, userNumber: 13d7b667-b523-41b7-ba89-203139e0dba9, name: MMMMMM, dirty: false })
09-03 18:02:43.992 28748-28748/in.yyyyyyy.xxxxx:sync D/ContactsDataSet: getInstance() => null on main
09-03 18:02:43.992 28748-28748/in.yyyyyyy.xxxxx:sync D/ContactsDataSet: Ignoring update Contact ContactEntry { contactId: 4, contactVersion: 21, phoneNumber: +333333333333, email: null, userNumber: 13d7b667-b523-41b7-ba89-203139e0dba9, name: MMMMMM, dirty: false } as instance: null isn't valid on main
When I invoke Adapter again:
09-03 18:07:40.255 18369-18369/in.yyyyyyy.xxxxx D/ContactsAdapter: ContactsAdapter()
09-03 18:07:40.257 18369-18369/in.yyyyyyy.xxxxx D/ContactsDataSet: Valid instance: java.lang.ref.WeakReference@90e9b43 on main
09-03 18:07:40.257 18369-18369/in.yyyyyyy.xxxxx D/ContactsDataSet: printInstance() => java.lang.ref.WeakReference@90e9b43 on main
Upvotes: 2
Views: 100
Reputation: 306
Problem is with Sync Service started as different process. I nerver knew that a single Android process can have multiple Linux processes. Sync service configuration:
<service
android:name=".sync.SyncService" android:process=":sync">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
</service>
I copied that configuration from someother app on internet without knowing what is that android:process
.
I have removed the attribute android:process
, and it started working.
Upvotes: 2