question

litpro avatar image
litpro asked Jeffrey Blattman edited

ReceiptRegistrationConnector onServiceConnected called after disconnect()

I'm registering my app to add custom text to the end of receipts, but I've encountered something strange. Whenever I connect to register my app, everything works as expected.

However, whenever I connect to *unregister* my app, the onServiceConnected callback gets called twice. Once when I connect so that I can unregister, and once after I've disconnected (and nulled out my reference to) the ReceiptRegistrationConnector object.

I've checked to make sure it's the same ReceiptRegistrationConnector whose onServiceConnected is getting called, and it is.

Is there something obvious I'm missing? Applicable code below (see THIS BIT GETS CALLED AFTER DISCONNECT).

// Receipt notification setup and teardown

public static void ensureReceiptNotifications(Context context) {
    connectAccount(context);
    receiptConnector = new ReceiptRegistrationConnector(context, account, new ServiceConnector.OnServiceConnectedListener() {
        @Override
        public void onServiceConnected(ServiceConnector<? extends IInterface> serviceConnector) {
            registerAsReceiptListener();
        }

        @Override
        public void onServiceDisconnected(ServiceConnector<? extends IInterface> serviceConnector) {
        }
    });
    receiptConnector.connect();
}

public static void stopReceiptNotifications(Context context) {
    connectAccount(context);
    receiptConnector = new ReceiptRegistrationConnector(context, account, new ServiceConnector.OnServiceConnectedListener() {
        @Override
        public void onServiceConnected(ServiceConnector<? extends IInterface> serviceConnector) {
// THIS BIT GETS CALLED AFTER DISCONNECT()
            unregisterAsReceiptListener();
        }

        @Override
        public void onServiceDisconnected(ServiceConnector<? extends IInterface> serviceConnector) {
        }
    });
    receiptConnector.connect();
}

private static void connectAccount(Context context) {
    if (account == null) {
        account = CloverAccount.getAccount(context);
    }
}

private static void registerAsReceiptListener() {
    new AsyncTask<Void, Void, Void>() {
        @Override
        protected Void doInBackground(Void... params) {
            receiptConnector.register(Uri.parse(ReceiptContentProvider.CONTENT_DIRECTORY_URI.toString()), new ReceiptRegistrationConnector.ReceiptRegistrationCallback<Void>());
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);
            receiptConnector.disconnect();
            receiptConnector = null;
        }
    }.execute();
}

private static void unregisterAsReceiptListener() {
    new AsyncTask<Void, Void, Void>() {

        @Override
        protected Void doInBackground(Void... params) {
            receiptConnector.unregister(Uri.parse(ReceiptContentProvider.CONTENT_DIRECTORY_URI.toString()), new ReceiptRegistrationConnector.ReceiptRegistrationCallback<Void>());
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);
            receiptConnector.disconnect();
            receiptConnector = null;
        }
    }.execute();


Merchant
6 comments
10 |2000

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Jeffrey Blattman avatar image Jeffrey Blattman ♦♦ commented ·

Why are you unregistering? I think the idea is you register 1x for your app, then your provider gets call on each receipt print.

0 Likes 0 ·
litpro avatar image litpro Jeffrey Blattman ♦♦ commented ·

Basically, if the user logs out of our service, I'm trying to be a good citizen and clean up my mess, including unregistering the app for receipt notifications. I assumed that was the point of having an unregister call. But maybe I'm misunderstanding how receipt registration is supposed to work?

0 Likes 0 ·
Jeffrey Blattman avatar image Jeffrey Blattman ♦♦ commented ·

I don't see any disconnect() call in there. Every connect should be paired with a disconnect. I suspect you have multiple listeners registered. Generally the pattern would to be connect / disconnect in on start / stop or on create / destroy.

0 Likes 0 ·
litpro avatar image litpro Jeffrey Blattman ♦♦ commented ·

It's up there. When the connector successfully connects for the purpose of unregistering the receipt listener, I instantiate an AsyncTask. Its doInBackground calls unregister(), and then its onPostExecute() disconnects the connector.

0 Likes 0 ·
Jeffrey Blattman avatar image Jeffrey Blattman ♦♦ litpro commented ·

Scrolling down is an important skill I've failed to master apparently. First observation is that depending on how you are calling this code it has threading problems. If you just call ensure / stop in succession you get an NPE because one thread is trying to use it while another is setting it to null.

0 Likes 0 ·
Show more comments

1 Answer

Jeffrey Blattman avatar image
Jeffrey Blattman answered Jeffrey Blattman edited
I created this:
https://pastebin.com/xVXGhXMF

And it seems to work okay. As I mentioned in the comment, your code is not thread safe as you are modifying the receiptConnector static on multiple threads. I don't know if the behavior you are seeing is related to that since I don't know how you are calling those methods.

Don't use static fields as that can leak resources. If you have an object like receiptConnector that's used in multiple threads, make sure you only modify the reference from a single thread (preferably the UI thread). The alternative to that is to synchronize access. First rule of concurrent programming: don't do it.

With connectors, you don't have to wait for them to be connected before using them. If they are not connected when you use them, either because it just hasn't occurred yet or because you never called connect() in the first place, they will connect themselves (if you haven't called it) and block waiting for the connect.
10 |2000

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Welcome to the
Clover Developer Community