Search code examples
c#androidxamarinzxingfreshmvvm

Android application throwing: Java.Lang.IllegalStateException, with message: Can not perform this action after onSaveInstanceState


To shorten the problem: I am developing an android application which requires users to log in, and since multiple users could be logged in at the same time, I want to cycle between the authenticated users using NFC touch cards. Everything works fine except after using ZXing.Mobile bar-code scanner, when code returns from scanning any bar-code, and tries to push a page model, this particular exception is being thrown Java.Lang.IllegalStateException: Can not perform this action after onSaveInstanceState. Please note that I am using Xamarin.Forms, FreshMVVM, ZXing.Mobile, and of course C#.

Fragments of code used:

AndroidManifest.xml:

<activity android:name="com.name.SplashActivity">
    <intent-filter>
        <category android:name="android.intent.category.LAUNCHER" />
        <action android:name="android.intent.action.MAIN" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="application/com.name.nfc" />
    </intent-filter>
</activity>
<activity android:name="com.name.MainActivity">
</activity>

The above code is used to enable the application to launch using NFC tags. SplashActivity launches MainActivity.

SplashActivity.cs:

protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);
    StartActivity(typeof(MainActivity));
}

protected override void OnResume()
{
    base.OnResume();

    if (NfcAdapter.ActionNdefDiscovered == Intent.Action)
    {
        ProcessIntent(Intent);
    }
}

protected override void OnNewIntent(Intent intent)
{
    Intent = intent;
}

public void ProcessIntent(Intent intent)
{
    //Code omitted to simplify the question.
}

Above code is shown just to know how I am using the NFC touch event.

Code to open the bar-code scanner from main page model:

public ICommand OpenCameraCommand => new Command(async () =>
{
    IsAvailable = false;
    ((Command) OpenCameraCommand).ChangeCanExecute();
    string checkBarcode = await _scanService.CameraScanAsync().ConfigureAwait(true);
    if (!string.IsNullOrWhiteSpace(checkBarcode))
    {
        Barcode = checkBarcode;
    }
    IsAvailable = true;
}, () => IsAvailable);

From scan service:

public async Task<string> CameraScanAsync()
{
    //AutoFocus code omitted to simplify the question

    Result result = await _mobileBarcodeScanner.Scan(new MobileBarcodeScanningOptions { PossibleFormats = _listOfBarcodeFormats }).ConfigureAwait(false);

    return result == null ? string.Empty : result.Text;
}

EDIT: code containing the push page model method:

switch (response.Status)
{
    case Case.Second:
        await CoreMethods.PushPageModel<SecondaryPageModel>(response).ConfigureAwait(true);
        Barcode = string.Empty;
        return;
    case Case.Third:
        await CoreMethods.PushPageModel<ThirdPageModel>(response).ConfigureAwait(true);
        Barcode = string.Empty;
        return;
    case Case.Fourth:
        await CoreMethods.PushPageModel<FourthPageModel>(response).ConfigureAwait(true);
        Barcode = string.Empty;
        return;
    case Case.Invalid:
        break;
    default:
        throw new InvalidOperationException();
}

This code is fired directly after returning from scanning a bar-code.
END EDIT

All of this works after the NFC card is being touched and the application launched, until hitting the next line of code. After a bar-code is returned from the scanner:
await CoreMethods.PushPageModel<SecondaryPageModel>(response).ConfigureAwait(true);

The exception is being thrown right here. I debugged my code to check what's going on. When the camera is open, it first fires MainActivity OnSaveInstanceState event, after successfully scanning a bar-code, MainActivity OnResume > MainActivity OnPostResume events are fired in that order. Then the PushPageModel method is called. Note that when I manually enter the bar-code in the relative field, everything works fine, it's just the scanner that throws this exception.

I have searched for solutions here in SO. I found some answers that says to opt-out the base.OnSaveInstanceState() line, I tried that with no luck, another answer said to enter junk values to work around the issue, tried that with no luck as well. I tried different launch modes in the AndroidManifest file like singleTop or singleTask or singleInstance with no luck as well.

I will be glad with any help that could be given. Thanks in advance.


Solution

  • MainActivity is running multiple instances when you are switching using NFC, Add to MainActivity LaunchMode as SingleTask, then when you launch using NFC clear the Task and create a new one. A good way to specify Activity flags is using Xamarin attributes, it is a better way than adding them in Manifest.xml