Siva
Siva

Reputation: 306

why single static variable has multiple values on multiple reads?

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

Answers (1)

Siva
Siva

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

Related Questions