What is the correct way to implement MainActivity.OnCreate with XF 1.3

KasperOvergrdNielsenKasperOvergrdNielsen DKBeta, University ✭✭

Since upgrading to XF 1.3 I use the following pattern for my MainActivity:

[Activity (Label = "AppCreateTwice.Android.Android", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : FormsApplicationActivity
{
    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);
        Xamarin.Forms.Forms.Init (this, bundle);
        try {
            LoadApplication(new App()); // <- this throws System.InvalidOperationException: Can not create more than one Application instance, if OnCreate is called again
        } 
        catch (Exception ex) {
            Console.WriteLine (ex); // <- good place for a breakpoint :-)
            throw;
        }
    }
}

This seems to be inline with what others are doing. However this doesn't handle back navigation correctly, as it may instantiate the App class twice. This situation can be re-created like this:

  1. debug App,
  2. press back soft-key,
  3. press app switcher soft-key,
  4. choose app from list to have OnCreate called again,
  5. now LoadApplication(new App()) will throw exception

The stacktrace is:

    [MonoDroid] UNHANDLED EXCEPTION:
    [MonoDroid] System.InvalidOperationException: Can not create more than one Application instance.
    [MonoDroid]   at Xamarin.Forms.Application.set_Current (Xamarin.Forms.Application value) [0x00000] in <filename unknown>:0 
    [MonoDroid]   at Xamarin.Forms.Application..ctor () [0x00000] in <filename unknown>:0 
    [MonoDroid]   at AppCreateTwice.App..ctor () [0x00000] in .../AppCreateTwice/AppCreateTwice/App.cs:8 
    [MonoDroid]   at AppCreateTwice.Android.MainActivity.OnCreate (Android.OS.Bundle bundle) [0x00011] in .../AppCreateTwice/Android/MainActivity.cs:36 

This has been tested with the newest version of XF (1.3.0.6275-pre1) on both a Moto G device and the Android player (Nexus 4), both running KitKat.

I have tried storing the created app in static variable an only creating it if, it hasn't been created before, but this will just fail in a different maner when you call LoadApplication. Also, it is not an option to not call LoadApplication, if it has already been called previously, as this will not setup the UI correctly. I assume it is not a bug, so my question is, what is the correct idiom for MainActivity.OnCreate with XF 1.3 and onwards? The current documentation is a bit lacking :-)

I have attached a sample project to to illustrate the problem, if anyone should find it useful.

Best Answers

Answers

  • nadjibnadjib DZMember ✭✭✭✭
    edited November 2014

    Yeah I was having trouble with this (in both WP and Android)

    Try something like

    LoadApplication(Xamarin.Forms.Forms.Application.Current ?? new App());

  • KasperOvergrdNielsenKasperOvergrdNielsen DKBeta, University ✭✭
    edited November 2014

    That seems a very reasonable suggestion, but all one gain is another exception:

        System.NullReferenceException: Object reference not set to an instance of an object
         at Xamarin.Forms.NavigationProxy.set_Inner (INavigation value) [0x00000] in <filename unknown>:0 
        at Xamarin.Forms.Platform.Android.Platform.SetPage (Xamarin.Forms.Page newRoot) [0x00000] in <filename unknown>:0 
         at Xamarin.Forms.Platform.Android.FormsApplicationActivity.InternalSetPage (Xamarin.Forms.Page page) [0x00000] in <filename unknown>:0 
         at Xamarin.Forms.Platform.Android.FormsApplicationActivity.SetMainPage () [0x00000] in <filename unknown>:0 
         at Xamarin.Forms.Platform.Android.FormsApplicationActivity.LoadApplication (Xamarin.Forms.Application app) [0x00000] in <filename unknown>:0 
         at AppCreateTwice.Android.MainActivity.OnCreate (Android.OS.Bundle bundle) [0x00022] in .../AppCreateTwice/Android/MainActivity.cs:36 
    

    In a way your suggestion is very similar to storing the instantiated App in a static variable and reusing it on subsequent calls to OnCreate, as I already tried. But thanks for alerting me to the Application.Current property. I wasn't aware of its existence.

    Perhaps I'm doing something wrong in the App class, even though it seems very benign:

    public class App : Application
    {
        public App ()
        {
            MainPage = new ContentPage { 
                Content = new Label {
                    Text = "Hello, Forms!",
                    VerticalOptions = LayoutOptions.CenterAndExpand,
                    HorizontalOptions = LayoutOptions.CenterAndExpand,
                }
            };
        }
    }
    

    I simply set the main page in the ctor.

  • PaulPCEPaulPCE USMember ✭✭

    I've just had this error too, and a Google search for the error turns up just this one page. Did you find out what the issue was?

  • KasperOvergrdNielsenKasperOvergrdNielsen DKBeta, University ✭✭

    @PaulPCE‌ Hi Paul. Not yet, unfortunately.

  • KasperOvergrdNielsenKasperOvergrdNielsen DKBeta, University ✭✭

    @DanielL Hi Daniel,

    The code in the reply you link to fails (it is almost identical to the example project I attached).

    Try the following:

    1. debug App,
    2. press back soft-key (repeatedly until you reach the Android home screen),
    3. press app switcher soft-key,
    4. choose app from list to have OnCreate called again,
    5. now LoadApplication(new App()) will throw exception

    Since we cannot avoid MainActivity.OnCreate from being called multiple times, and we cannot reuse the old App instance, nor are we allowed to create a new one, then how do we implement MainActivity.OnCreate?

  • PaulPCEPaulPCE USMember ✭✭

    Thanks @DanielL‌ for the help - My fault, I was still using SetPage() multiple times and hadn't fully read the release notes (or noticed that it was obsoleted). After reading up it all makes a lot more sense! I also found @MitchMilam‌'s blog post very useful (thanks Mitch!)

  • KasperOvergrdNielsenKasperOvergrdNielsen DKBeta, University ✭✭
    edited November 2014

    @DanielL‌ Hi Daniel. Thanks for the work around. Hijacking the back key press on the first page is a clever hack indeed. However, I agree with you that this looks like an internal XF bug.

  • GeoffArmstrongGeoffArmstrong CAMember ✭✭

    In FormsApplicationActivity the last line of OnCreate calls Application.ClearCurrent(). However, in our application, calling base.OnCreate(savedInstanceState) does not seem to clear Application.Current resulting in the error shown.

    I am not sure why this is.

  • @kaspero, I agree. The framework should push our app onto the back stack. We shouldn't need be hijacking the key press nor calling MoveTaskToBack(). It is, however, a workaround that works.

  • KasperOvergrdNielsenKasperOvergrdNielsen DKBeta, University ✭✭

    With Xamarin.Forms 1.3.1 - Pre release 1 (1.3.1.6294-pre1), the situation has improved - we no longer get the exceptions described above, that was experienced in 1.3.0.6275-pre1.

    However there is still an issue when the application exits, if you use Xamarin.Insights (1.8.2.105). It is not as deterministic, but it fairly easy to recreate with the following cycle:

    1. debug App,
    2. press back soft-key,
    3. press app switcher soft-key,
    4. choose app from list to have OnCreate called again,
    5. repeat from 2, until you get an exception.

    The stack trace is all internal to Xamarin:

        System.Diagnostics.Debugger. () in 
        System.Diagnostics.Debugger.Mono_UnhandledException (ex=) in 
        object.c54fed1f-c467-49a6-9a03-8549330a4061 (Parameters=) in 
        System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (Parameters=) in 
        Android.Runtime.JNIEnv.CallNonvirtualVoidMethod (Parameters=) in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.20-series/ba9bbbdd/source/monodroid/src/Mono.Android/src/Runtime/JNIEnv.g.cs:896
        Android.Content.ContextWrapper.UnregisterReceiver (Parameters=) in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.20-series/ba9bbbdd/source/monodroid/src/Mono.Android/platforms/android-21/src/generated/Android.Content.ContextWrapper.cs:3164
        Xamarin.InsightsCore.NetworkChangeReceiver.UnRegister (Parameters=) in 
        Xamarin.InsightsCore.HardwareDetector.Teardown (Parameters=) in 
        Xamarin.InsightsCore.PlatformServices.OnBackgroundManagerTerminating (Parameters=) in 
        Xamarin.InsightsCore.BackgroundManager.OnActivityDestroyed (Parameters=) in 
        Android.App.Application.IActivityLifecycleCallbacksInvoker.n_OnActivityDestroyed_Landroid_app_Activity_ (Parameters=) in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.20-series/ba9bbbdd/source/monodroid/src/Mono.Android/platforms/android-21/src/generated/Android.App.Application.cs:167
        object.c54fed1f-c467-49a6-9a03-8549330a4061 (Parameters=) in 
        Android.Runtime.JNIEnv.CallNonvirtualVoidMethod (Parameters=) in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.20-series/ba9bbbdd/source/monodroid/src/Mono.Android/src/Runtime/JNIEnv.g.cs:873
        Android.App.Activity.OnDestroy (Parameters=) in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.20-series/ba9bbbdd/source/monodroid/src/Mono.Android/platforms/android-21/src/generated/Android.App.Activity.cs:2831
        Xamarin.Forms.Platform.Android.FormsApplicationActivity.OnDestroy (Parameters=) in 
        Android.App.Activity.n_OnDestroy (Parameters=) in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.20-series/ba9bbbdd/source/monodroid/src/Mono.Android/platforms/android-21/src/generated/Android.App.Activity.cs:2816
        object.20bfdb1c-526c-4422-a02c-4e88fd1b9fa7 (Parameters=) in 
    

    I have tested this on a Nexus 5, running Android 4.4.4.

    Again, you can avoid this situation by using the same workaround @DanielL gave earlier in this thread, as this avoids the call to OnDestroy when pressing the soft back key.

  • KasperOvergrdNielsenKasperOvergrdNielsen DKBeta, University ✭✭
    edited January 2015

    @BrendanZagaeski‌, thanks for the follow-up. I can confirm that using ApplicationContext, as in:

    Insights.Initialize("...", ApplicationContext);
    

    instead of this, as in:

    Insights.Initialize("...", this);
    

    fixes the issue for me as well.

  • MarkWallisMarkWallis AUMember
    edited February 2015

    Thanks @DanielL, the hack worked for me :-)

    I was working around the problem by recreating the app and therefore all pages, not ideal but was fine...till I switched to iOS and realized that recreating the app from scratch there wasn't going to work there.

    Kind of important that life cycle management is the same or at least very similar x-platform. I know it must be very tricky so thanks Xamarin for the great work! But can we please fix so we don't need to do the dirty hack :-)

    Just to elaborate a little, the issue for me (and why I switched to recreating the App every time) was that everything looked fine when the user switched back to the app...but...Navigation.PushAsync failed. Probably there were other problems, this was the main problem for me. It did NOT throw an exception, it simply failed quietly. So I figured 'something' was wrong with the Page Navigation object, took quite some time to dig around, experiment and finally find @DanielL's great suggestion.

Sign In or Register to comment.