Xamarin.Forms: Forms.Context is obsolete - xamarin.forms

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?

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

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)

Related

Is it possible to delay-load PRISM / Xamarin Forms components that aren't immediately needed?

I have the following AppDelegate which takes quite some time to load:
Syncfusion.ListView.XForms.iOS.SfListViewRenderer.Init();
new Syncfusion.SfNumericUpDown.XForms.iOS.SfNumericUpDownRenderer();
Syncfusion.SfCarousel.XForms.iOS.SfCarouselRenderer.Init();
Syncfusion.XForms.iOS.Buttons.SfSegmentedControlRenderer.Init();
Syncfusion.XForms.iOS.Buttons.SfCheckBoxRenderer.Init();
new Syncfusion.XForms.iOS.ComboBox.SfComboBoxRenderer();
//Syncfusion.XForms.iOS.TabView.SfTabViewRenderer.Init();
new Syncfusion.SfRotator.XForms.iOS.SfRotatorRenderer();
new Syncfusion.SfRating.XForms.iOS.SfRatingRenderer();
new Syncfusion.SfBusyIndicator.XForms.iOS.SfBusyIndicatorRenderer();
What options should I consider when I know some of these components aren't needed for the main screen, but for subscreens?
I am using PRISM, and it appears that every tab is pre-loaded immediately before allowing display or interaction with the end user. What can I do to delay the pre-rendering that the Prism TabView does prior to showing the interface?
Should I use Lazy<T>? What is the right approach?
Should I move these components to another initialization section?
There are a number of ways you could ultimately achieve this, and it all depends on what your real goals are.
If your goal is to ensure that you get to a Xamarin.Forms Page as fast as possible so that you have some sort of activity indicator, that in essence says to the user, "it's ok I haven't frozen, we're just doing some stuff to get ready for you", then you might try creating a "SpashScreen" page where you do additional loading. The setup might look something like the following:
public partial class AppDelegate : FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App(new iOSInitializer()));
return base.FinishedLaunching(app, options);
}
}
}
public class iOSInitializer : IPlatformInitializer, IPlatformFinalizer
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterInstance<IPlatformFinalizer>(this);
}
public void Finalize()
{
new Syncfusion.SfNumericUpDown.XForms.iOS.SfNumericUpDownRenderer();
Syncfusion.SfCarousel.XForms.iOS.SfCarouselRenderer.Init();
Syncfusion.XForms.iOS.Buttons.SfSegmentedControlRenderer.Init();
Syncfusion.XForms.iOS.Buttons.SfCheckBoxRenderer.Init();
}
}
public class App : PrismApplication
{
protected override async void OnInitialized()
{
await NavigationService.NavigateAsync("SplashScreen");
}
}
public class SplashScreenViewModel : INavigationAware
{
private IPlatformFinalizer _platformFinalizer { get; }
private INavigationService _navigationService { get; }
public SplashScreenViewModel(INavigationService navigationService, IPlatformFinalizer platformFinalizer)
{
_navigationService = navigationService;
_platformFinalizer = platformFinalizer;
}
public async void OnNavigatedTo(INavigationParameters navigationParameters)
{
_platformFinalizer.Finalize();
await _navigationService.NavigateAsync("/MainPage");
}
}
If you're working with Modules you could take a similar approach though any Modules that would initialize at Startup would still be making that call to Init the renderers before you've set a Page to navigate to. That said, working with Modules does give you a number of benefits here as you only ever would have to initialize things that the app actually requires at that point.
All of that said I'd be surprised if you see much in the way of gain as these Init calls are typically empty methods only designed to prevent the Linker from linking them out... if you aren't linking or have a linker file you could simply instruct the Linker to leave your Syncfusion and other libraries alone.

How to prevent static variable became null after application holds for long time

I have developed and released one application in market long ago. Now some some users pointed crashes when holding application for long time. Now I identified the reason for the crash, that is I am using a class with static variable and methods to store data (getters and setters). Now I want to replace the static way with any other ways.From my study I got the following suggestions:
shared preferences: I have to store more than 40 variable (strings, int and json arrays and objects), So I think using shared preferences is not a good idea.
SQLite: more than 40 fields are there and I don't need to keep more than one value at a time.I am getting values for fields from different activities. I mean name from one activity , age from another activity, etc So using SQLite also not a good Idea I think.
Application classes: Now I am thinking about using application classes to store these data. Will it loss the data like static variable after hold the app for long time?
Now I replace the static variable with application class . Please let me know that application data also became null after long time?
It may useful to somebody.
Even though I didn't get a solution for my problem, I got the reason for why shouldn't we use application objects to hold the data. Please check the below link
Don't use application object to store data
Normally if you have to keep something in case your Activity gets destroyed you save all these things in onSaveInstanceState and restore them in onCreate or in onRestoreInstanceState
public class MyActivity extends Activity {
int myVariable;
final String ARG_MY_VAR="myvar";
public void onCreate(Bundle savedState) {
if(savedState != null {
myVariable = savedState.getInt(ARG_MY_VAR);
} else {
myVariable = someDefaultValue;
}
public void onSaveInstanceState(Bundle outState) {
outState.putInt(ARG_MY_VAR, myVariable);
super.onSaveInstanceState(outState);
}
}
Here if Android OS destroys your Activity onSaveInstanceState will be called and your important variable will be saved. Then when the user returns to your app again Android OS restores the activity and your variable will be correctly initialized
This does not happen when you call finish() yourself though, it happens only when Android destroys your activity for some reasons (which is quite likely to happen anytime while your app is in background).
First you should overwrite the onSaveInstanceState and onRestoreInstanceState methods in you activity:
#Override
protected void onSaveInstanceState (Bundle outState){
outState.putString("myVariable", myVariable);
// Store all you data inside the bundle
}
#Override
protected void onRestoreInstanceState (Bundle savedInstanceState){
if(savedInstanceState != null){
myVariable = savedInstanceState.getString("myVariable");
// Restore all the variables
}
}
May be try use static variable inside Application space?
public class YourApplication extends Application
{
private static ApplicationBubblick singleton;
public String str;
public static YourApplication getInstance()
{
return singleton;
}
}
And use variable via:
YourApplication.getInstance().str = ...; // set variable
... = YourApplication.getInstance().str; // get variable
This variable will be live until your app will start and stop all services or activities of your app. This is not work when your app crash.

Entity Framework Context dealing stale data

I'm using Unity to inject a context and using the following lifetime manager...
public class HttpContextLifetimeManager<T> : LifetimeManager, IDisposable
{
#region IDisposable Members
public void Dispose()
{
RemoveValue();
}
#endregion
public override object GetValue()
{
object value = HttpContext.Current.Items[typeof (T).AssemblyQualifiedName];
return value;
}
public override void RemoveValue()
{
HttpContext.Current.Items.Remove(typeof (T).AssemblyQualifiedName);
}
public override void SetValue(object newValue)
{
HttpContext.Current.Items[typeof (T).AssemblyQualifiedName]
= newValue;
}
}
First request to page one: Shows values.
First web request to page two:Updates values.
Second web request to page one: Shows old values.
Second web request to page two: Shows new values.
I have to restart the VS development server to get page one to show the new values.
So how can a context a) live between page requests and b) be specific to a page?
This had nothing to do with EF. The generated UI was out of sync with the entity and the entity was throwing validation errors which weren't being reported in the UI. It would have helped if the scaffolding templates had generated a validation summary which didn't only show model level errors.

Windsor composite lifestyle for asp.net process

I have an asp.net process which also consumes messages from a servicebus (MassTransit). For webrequests my database session is resolved with a PerWebRequest lifestyle.
But when the process consumes a message from MassTransit I need the database session to have another lifestyle, as no HttpContext is available.
I have made this:
public class PerRequestLifeStyleManager : ILifestyleManager
{
readonly PerWebRequestLifestyleManager perWebRequestLifestyleManager;
readonly PerThreadLifestyleManager perThreadLifestyleManager;
public PerRequestLifeStyleManager()
{
perWebRequestLifestyleManager = new PerWebRequestLifestyleManager();
perThreadLifestyleManager = new PerThreadLifestyleManager();
}
public void Init(IComponentActivator componentActivator, IKernel kernel, ComponentModel model)
{
perWebRequestLifestyleManager.Init(componentActivator, kernel, model);
perThreadLifestyleManager.Init(componentActivator, kernel, model);
}
public object Resolve(CreationContext context)
{
return GetManager().Resolve(context);
}
public bool Release(object instance)
{
return GetManager().Release(instance);
}
public void Dispose()
{
GetManager().Dispose();
}
ILifestyleManager GetManager()
{
if (HttpContext.Current != null)
{
return perWebRequestLifestyleManager;
}
return perThreadLifestyleManager;
}
}
Can anyone tell me, if this is the right way to go? And if it isn't, what is?
Thanks.
EDIT: I have just updated the question with some code that seems to work (before it was untested). I still am eager to know if this - seen from a Windsor perspective - is safe and sound.
Try using one of the hybrid lifestyles.
By using the Castle Windsor extension, you should just be able to have your ISession as a dependency on the constructor of the consumer class. That way, the container will manage the lifecycle of the ISession, and dispose of it once the consumer is disposed by MT.
If you need even more control, you can look at how the WindsorConsumerFactory is implemented to wrap the resolution and release of the consumer class instance around the delivery of the message to the consumer.
If you need to inject something beyond that, you can also use an interceptor:
Unit of work when using MassTransit

Can I create custom global methods in my Android Application class?

I currently have an app that has many activities and needs to have a way of maintaining state between these activities.
I use the Application class to do this, declaring my global variables and using getters and setters to interact with my activities.
I was hoping to place a few custom methods in there, so that when I want to do a common task like, for instance, display an error message, I can declare the method in my application class and call it from any activity that uses it
EscarApplication application = (EscarApplication) this.getApplication();
EscarApplication being the name of my application class above.
I have tried to include this method in my application class:
public void showError(String title, String message) {
Log.i("Application level",message);
this.alertDialog.setTitle(title);
alertDialog.setMessage(message);
alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int which) {
return;
}
});
alertDialog.show();
}
In the hope that I can call this method from activity without having to redeclare it, but when I call it using something like below I get an null pointer exception:
Visit.this.application.showError("Update error", "An error has occurred while trying to communicate with the server");
Visit being the name of my current activity above.
Should this work, or can I only use getters and setters to change global vars in an Application Class.
Stack Trace:
java.lang.RuntimeException: Unable to start activity ComponentInfo{escar.beedge/escar.beedge.HomeScreen}: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2401)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2417)
at android.app.ActivityThread.access$2100(ActivityThread.java:116)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1794)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4203)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549)
at dalvik.system.NativeStart.main(Native Method)
ERROR/AndroidRuntime(375): Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
at android.view.ViewRoot.setView(ViewRoot.java:460)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:177)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
at android.app.Dialog.show(Dialog.java:238)
at escar.beedge.EscarApplication.showError(EscarApplication.java:98)
at escar.beedge.HomeScreen.onCreate(HomeScreen.java:30)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2364)
The dialog is declared as such in the application class:
AlertDialog alertDialog;
Created in that same class:
alertDialog = new AlertDialog.Builder(this).create();
and the method to call it in that class is as follows:
public void showError(String title, String message) {
alertDialog.setTitle(title);
alertDialog.setMessage(message);
alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int which) {
return;
}
});
alertDialog.show();
}
And finally, it is called from an activity like so:
EscarApplication application;
application = (EscarApplication) this.getApplication();
application.showError("test", "display this message");
If you need to maintain state between activities, then use a service. Anything else is a hack
Someone correct me if Im wrong, but an Application class wont be able to execute view related objects as they need to be bound to a view which needs to be bound to an activity.
In that sense, you could use your Application class to implement a static method that customises the dialog
public static void setDialog(String title, String message,AlertDialog alertDialog){
alertDialog.setTitle(title);
alertDialog.setMessage(message);
alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int which) {
return;
}
});
}
but you would have to create the dialog and call the show method on the activities themselves (actually maybe even the button to be set in the dialog would need to be created on the activity)
Another option could be to implement a class that extends the AlertDialog class and whose button is pre-set to the behavior you want.
You could use the Singleton pattern.
I'm looking to achieve something similar to you.
I haven't found an official answer, but it looks like you shouldn't be using the application context for Toast and Dialogs. Instead, try using the context of an Activity like so :
// From inside your activity
Dialog dialog = new Dialog(this);
instead of this:
// From inside your Application instance
Dialog dialog = new Dialog(getApplicationContext());
Read this :
Android: ProgressDialog.show() crashes with getApplicationContext

Resources