Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xamarin.Forms: Forms.Context is obsolete

The new obsolete warning in Xamarin.Forms 2.5 really puzzled me. What context should I be using in Dependency Services, for example, to call GetSystemService()?

Should I store in a static field the context of activity the xamarin forms were initialized against?

Should I override the android Application class and use its Context?

Should I call GetSystemService at activity create and save it somewhere?

like image 556
Anton Duzenko Avatar asked Nov 17 '17 15:11

Anton Duzenko


2 Answers

I was having the same issue with several Dependency Services

The simplest solution

In a lot of cases for Single Activity Applications

Xamarin.Forms.Forms.Context

Can be replaced with

Android.App.Application.Context

The Background in more detail

Android.App.Application.Context returns the global Application Context of the current process tied to the lifecycle of the Application, as apposed to an Activity context.

A typical example of using the Application context is for starting an Activity e.g.

Android.App.Application.Context.StartActivity(myIntent);

The general rule of thumb is to use the current Activity Context, unless you need to save a reference to a context from an object that lives beyond your Activity. In which case use the Application context

Why did Forms.Context go obsolete?

Xmarin.Forms 2.5 introduced a new "Forms embedding" feature, which can embed Forms pages into Xamarin.iOS / Xamarin.Android apps. However, since Xamarin.Android apps can use multiple Activities, seemingly there was a danger of Xamarin.Android users calling Forms.Context and in turn getting a reference to the MainActivity, which has the potential cause problems.

The work around

Inside a Renderer you now get a reference to the view’s context which is passed into the constructor.

With any other class you are faced with the issue of how to get the Activity Context. In a single Activity application (in most cases) the Application.Context will work just fine.

However to get the current Activity Context in a Multiple Activity Application you will need to hold a reference to it. The easiest and most reliable way to do this is via a class that implements the Application.IActivityLifecycleCallbacks Interface.

The main idea is to keep a reference of the Context when an Activity is created, started, or resumed.

[Application]
public partial class MainApplication : Application, Application.IActivityLifecycleCallbacks
{
   internal static Context ActivityContext { get; private set; }

   public MainApplication(IntPtr handle, JniHandleOwnership transfer) : base(handle, transfer) { }

   public override void OnCreate()
   {
      base.OnCreate();
      RegisterActivityLifecycleCallbacks(this);
   }    

   public override void OnTerminate()
   {
      base.OnTerminate();
      UnregisterActivityLifecycleCallbacks(this);
   }  

   public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
   {
      ActivityContext = activity;
   }   

   public void OnActivityResumed(Activity activity)
   {
      ActivityContext = activity;
   }

   public void OnActivityStarted(Activity activity)
   {
      ActivityContext = activity;
   }

   public void OnActivityDestroyed(Activity activity) { }  
   public void OnActivityPaused(Activity activity) { }
   public void OnActivitySaveInstanceState(Activity activity, Bundle outState) { }
   public void OnActivityStopped(Activity activity) { }
}

With the above approach, single Activity Applications and multiple Activity Applications can now always have access to the Current/Local Activity Context. e.g instead of relying on the global context

Android.App.Application.Context 

// or previously

Xamarin.Forms.Forms.Context

Can now be replaced with

MainApplication.ActivityContext

Example call in a Dependency Service

if (MainApplication.ActivityContext!= null)
{
    versionNumber = MainApplication.ActivityContext
                       .PackageManager
                       .GetPackageInfo(MainApplication.ActivityContext.PackageName, 0)
                       .VersionName;
}

Additional Resources

Android.App.Application.IActivityLifecycleCallbacks

registerActivityLifecycleCallbacks

like image 93
TheGeneral Avatar answered Nov 07 '22 11:11

TheGeneral


In the latest scaffold of a new Xamarin Forms solution the CrossActivityPlugin (https://github.com/jamesmontemagno/CurrentActivityPlugin) is referenced in the Android project. So you can use

CrossCurrentActivity.Current.Activity.StartActivity(myIntent)

like image 45
Guido Neele Avatar answered Nov 07 '22 11:11

Guido Neele