vRallev
vRallev

Reputation: 5040

Does Android's NFC foreground dispatch system have a bug?

I have an annoying issue with the foreground dispatch behavior. Sometimes instead of calling onNewIntent(), it completely recreates the activity, which breaks the app's workflow.

My concrete situation: Activity A is the MainActivity, which uses the foreground dispatch. Everything works as it should. However, in my activity B, which is launched from the browser (VIEW action), the foreground dispatch doesn't work under some circumstances anymore.

The workflow:

The code is correct, e.g. if I attach the NFC device in the first scenario twice, it works as it should at the second time, but not at the first time. In the MainActivity and activity B I definitively call the disableForegroundDispatch() method in the activity's onPause() method.

Is there a solution for my specific problem? For me it sounds like a bug.

Edit:

public void resume(Activity targetActivity) {
    if (nfc != null && nfc.isEnabled()) {
        // nfc is the default NFC adapter and never null on my devices

        Intent intent = new Intent(targetActivity, targetActivity.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(targetActivity, 0, intent, 0);
        nfc.enableForegroundDispatch(targetActivity, pendingIntent, null, new String[][] { new String[] { IsoDep.class.getName() } });
    }
}

public void pause(Activity targetActivity) {
    if (nfc != null && nfc.isEnabled()) {
        nfc.disableForegroundDispatch(targetActivity);
    }
}

These methods are called in the corresponding methods in each activity. Thanks for the help!

Solution: After a very long research I finally found the issue. Logcat printed:

I found other issues at Stackoverflow, where people had have the same issue with the NotificationManager, but all the hints didn't help me. Adding the flag singleTask to my activity B did the trick for me, but to be honest I don't understand it, because the context is always an activity.

I removed all the code from the MainActivity and the first scenario still didn't work. I romved the MainActivity from the manifest and after that everything was fine. Maybe it is a problem, that an app instance is running and activity B is launched from the browser? I don't know.

Anyway, thanks for the help NFC guy!

Upvotes: 5

Views: 4385

Answers (2)

Taiko
Taiko

Reputation: 1329

I guess your workflow is as follow : Main --> Detect tag --> Reader Activity --> Writer activity Detect tag --> Write tag

This problem seems to arise when your writer activity (the one with foreground enabled, I suppose that's for writing purposes) belongs to an Activity Stack (e.g. a Task) that was called from a previous tag discovery.

In particular, it doesn't arise if your workflow is as follow : Main Writer activity Detect tag --> Write tag

My workaround is to call the writer activity in a new task to begin with. In the activity that laucnhes the writer, just add the new task flag in the intent that starts the writer.

startActivity(new Intent(this,MyTagWriterActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));

Main --> Detect tag --> Reader Activity -NEW_TASK-> Writer activity Detect tag --> Write tag

It does mess with the activity history, but makes the writer activity more predictable.

Upvotes: 0

NFC guy
NFC guy

Reputation: 10228

The IntentFilter[] that you pass to enableForegroundDispatch is empty. So your NFC intent probably arrive at your Activity due to the IntentFilter(s) in the manifest file. This explains the behaviour you observe, as an NFC intent always creates a new instance when delivered this way.

Add something like this instead to your code for enabling foreground dispatch:

IntentFilter[] iFilters = new IntentFilter[2];
iFilters[0] = new IntentFilter();
iFilters[0].addAction("android.nfc.action.TECH_DISCOVERED");
iFilters[1] = new IntentFilter();
iFilters[1].addAction("android.nfc.action.TAG_DISCOVERED");
iFilters[1].addCategory(Intent.CATEGORY_DEFAULT);

And pass that as parameter to enableForegroundDispatch.

UPDATE: I recently learned more about this specific problem. It is caused by the way Android determines in which task a new Activity should be launched. I don't know or understand the specific details of how that works, but the effect is that:

  1. When Activity B is launched from the Browser, it is created in the Browser's task
  2. When the NFC intent arrives, the system determines that a new Activity B is to be created in Activity A's task

Because of 2., the SINGLE_TOP is not ignored: there is only one instance of Activity B at the top of A's task. When Activity A is closed, it's task has disappeared, so Activity B will always be created in the Browser's task, as you have observed.

You may feel that this is an Android bug in this case (I do, I think), but this behaviour of how to create activities in which task is so fundamental to Android that many apps rely on it. So it is very unlikely that this will ever change.

Possible work-around: declare Activity B with launchMode "singleTask" (or "singleInstance"). Then a new (3rd) task will be created when B is launched.

Upvotes: 2

Related Questions