Xamarin.Forms - OnStop(), OnExit(), OnClose() - xamarin.forms

I'm developing an app and I need to know when the app gets Stopped, Closed, Exited, whatever interrupts it, in order to stop some services such as WebSocket. How can I get 'access' to those events?
Thanks!

I have tested the following in a small example. (Tested it on UWP and works, the OnSleep() is called, when i close the App). The OnSleep() Method which can be overridden in the App.xaml.cs is the Method you are looking for.
The Xamarin Application LifeCycle offers some methods for your needs.
OnStart - Called when the application starts.
OnSleep - Called each time the application goes to the background.
OnResume - Called when the application is resumed, after being sent to the background.
Note that there is no method for application termination. Under normal
circumstances (ie. not a crash) application termination will happen
from the OnSleep state, without any additional notifications to your
code.
Example:
using System;
using System.Diagnostics;
using Xamarin.Forms;
namespace App1
{
public partial class App : Application
{
public App()
{
InitializeComponent();
if (Device.RuntimePlatform == Device.iOS)
MainPage = new MainPage();
else
MainPage = new NavigationPage(new MainPage());
}
protected override void OnStart() {
Debug.WriteLine("OnStart");
}
protected override void OnSleep() {
Debug.WriteLine("OnSleep");
}
protected override void OnResume() {
Debug.WriteLine("OnResume");
}
}
}
Update
According to this you have to catch unhandled exceptions in the native code. That makes it a lil complicated to shutdown your services.
Example:
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity {
protected override void OnCreate(Bundle bundle) {
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
private void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs) {
//crashed by exception
}
}
Further Reading on Unhandled Exceptions: here

Related

Detaching Connectivity Changes Xamarin Forms

I am using Xamarin.Essentials to handle connectivity changes, it all works on android
However I have noticed that in iOS it didnt work, I debugged and noticed that
sliding down the screen in iOS Physical device
Put the phone in Plane Mode and remove wifi
Sleep event fired and removed connectivity
//App.Xaml
protected override void OnSleep()
{
Connectivity.ConnectivityChanged -= OnConnectivityChanged;
}
protected override void OnStart()
{
Connectivity.ConnectivityChanged += OnConnectivityChanged;
}
protected override void OnResume()
{
Connectivity.ConnectivityChanged += OnConnectivityChanged;
}
if I comment out
Connectivity.ConnectivityChanged -= OnConnectivityChanged;
it all works.
Am I missing the obvious?
WHere I am supposed to detach the connectivity?
This is because the Forms lifecycle is somewhat different from the declarative lifecycle approach of native platform, and you can do this directly in the native ios lifecycle.
in your ios project AppDelegate.cs :
public override void OnActivated(UIApplication application)
{
Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged;
}
public override void DidEnterBackground(UIApplication uiApplication)
{
Connectivity.ConnectivityChanged -= Connectivity_ConnectivityChanged;
}
private void Connectivity_ConnectivityChanged(object sender, ConnectivityChangedEventArgs e)
{
//do some thing
}
You could refer to the lefecycle.
Note:
on iOS 13 (and later),you need write them to SceneDelegate as well.

OnAppearing not called in prism xamarin forms

I want a process to be called each time I navigated to my view to refresh a list.
I am using Xamarin Forms and prism framework.
I made my ViewModel derivate from ContentPage but the following method is never called :
protected override void OnAppearing()
{
//Do things
}
How am I supposed to do to get the event? Is it better to use OnNavigateTo?
Through the document:
There are times in your application where you may want to invoke code
in your ViewModel based on when the Page Appears or Disappears without
Navigation specific consideration. For these times you can utilize the
IPageLifecycleAware interface to properly respond to the Appearing and
Disappearing events from the Page.
public class ViewAViewModel : IPageLifecycleAware
{
public void OnAppearing()
{
Console.WriteLine("We are appearing");
}
public void OnDisappearing()
{
Console.WriteLine("We are disappearing");
}
}
I found a solution to make my code work, I add to do this in my code behind from the page:
protected override void OnAppearing()
{
(BindingContext as IPageLifecycleAware)?.OnAppearing();
}
Still a mystery why I need to add this and it is not in the sample.
at the time of this post, the sample does it differently. It uses Behaviors to achieve expectation
It has a PageLifeCycleAwareBehavior class
private void OnAppearing(object sender, EventArgs e)
{
MvvmHelpers.InvokeViewAndViewModelAction<IPageLifecycleAware>(AssociatedObject, aware => aware.OnAppearing());
}
private void OnDisappearing(object sender, EventArgs e)
{
MvvmHelpers.InvokeViewAndViewModelAction<IPageLifecycleAware>(AssociatedObject, aware => aware.OnDisappearing());
}
You can see the full implementation here
You can also call Initialize is a good alternative
Add ini to your class
public class HelloViewModel : BindableBase, IInitialize
then add the following method
public void Initialize(INavigationParameters parameters)
{
// Do stuff
}

How to instantiate a BroadcastReceiver on just one page of my PCL project?

I am developing a xamarin cross platform application.
I need to get an activation code by SMS automatically on my login confirmation page. This answer works if I intanciate SmsReceiver in my MainActivity, but how can I instantiate the SmsReceiver class only on my login confirmation page?
SMS activation will only be used once on one page, I do not want it to be active for the lifetime of my app. It would be a waste.
I want my app to read incoming messages only when the login confirmation page is active. How can I do this?
If you don't want your BroadcastReceiver from being always "listening" you just need to mark it as Enabled=false in the declaration, this will prevent the BrodcastReceiver from being statically-registered in the Manifest.xml.
[BroadcastReceiver(Enabled = false, Label = "SMS Receiver")]
[IntentFilter(new string[] { "android.provider.Telephony.SMS_RECEIVED", Intent.CategoryDefault })]
public class SmsReceiver : BroadcastReceiver
{
// Code omitted for simplicity
....
}
Later on the Activity you want to be listening you will need to call
RegisterReceiver()
Passing the instance of the BroadcastReceiver and the IntentFilter.
something like:
[Activity(Label = "MainActivity", MainLauncher = true, Icon = "#mipmap/icon")]
public class MainActivity: Activity
{
private SmsReceiver _smsReceiver;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
_smsReceiver = new SmsReceiver()
}
protected override OnResume()
{
base.OnResume();
RegisterReceiver(_smsReceiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED"));
}
protected override OnPause()
{
UnregisterReceiver(_smsReceiver);
base.OnPause();
}
}
As you can see I am also unregistering when I don't need/want to be listening anymore.
This is done with the method:
UnregisterReceiver();
Hope this helps.

What is the proper way to register view with viewmodels in Prism.DryIoc

I'm using the Prism Template Pack for Visual Studio for Mac to generate a new project (tried both shared and pcl) then updating to 7.0.0.340-ci. Is registration of the views to view models done by convention?
When I try to run this app it throws this exception: Objective-C exception thrown. Name: NSInternalInconsistencyException Reason: Application windows are expected to have a root view controller at the end of application launch.
Here is the code for the main app.
public partial class App : PrismApplication
{
public App(IPlatformInitializer initializer = null) : base(initializer) { }
protected override void OnInitialized()
{
InitializeComponent();
NavigationService.NavigateAsync("MainPage?title=Hello%20from%20Xamarin.Forms");
}
protected override void RegisterTypes(Prism.Ioc.IContainerRegistry containerRegistry)
{
Prism.Mvvm.ViewModelLocationProvider.Register<MainPage,MainPageViewModel>();
}
//protected override void RegisterTypes()
//{
// Container.RegisterTypeForNavigation<MainPage>();
//}
}
I had to comment out the bottom lines and redo the override due to incompatible signatures.
Where did the Container.RegisterTypeForNavigation go or what is it's replacement?
I also tried it without any code in the RegisterTypes method.
In a debug session exploring the NavigationService properties says MainPage is null.
The RegisterTypes method should look like this.
In prism forms 7.x pages used for navigation must be registered via RegisterForNavigation<>() or RegisterForNavigation().
protected override void RegisterTypes(Prism.Ioc.IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<MainPage,MainPageViewModel>();
}

Can I access session state from an HTTPModule?

I could really do with updating a user's session variables from within my HTTPModule, but from what I can see, it isn't possible.
UPDATE: My code is currently running inside the OnBeginRequest () event handler.
UPDATE: Following advice received so far, I tried adding this to the Init () routine in my HTTPModule:
AddHandler context.PreRequestHandlerExecute, AddressOf OnPreRequestHandlerExecute
But in my OnPreRequestHandlerExecute routine, the session state is still unavailable!
Thanks, and apologies if I'm missing something!
Found this over on the ASP.NET forums:
using System;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.Diagnostics;
// This code demonstrates how to make session state available in HttpModule,
// regardless of requested resource.
// author: Tomasz Jastrzebski
public class MyHttpModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.PostAcquireRequestState += new EventHandler(Application_PostAcquireRequestState);
application.PostMapRequestHandler += new EventHandler(Application_PostMapRequestHandler);
}
void Application_PostMapRequestHandler(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
if (app.Context.Handler is IReadOnlySessionState || app.Context.Handler is IRequiresSessionState) {
// no need to replace the current handler
return;
}
// swap the current handler
app.Context.Handler = new MyHttpHandler(app.Context.Handler);
}
void Application_PostAcquireRequestState(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
MyHttpHandler resourceHttpHandler = HttpContext.Current.Handler as MyHttpHandler;
if (resourceHttpHandler != null) {
// set the original handler back
HttpContext.Current.Handler = resourceHttpHandler.OriginalHandler;
}
// -> at this point session state should be available
Debug.Assert(app.Session != null, "it did not work :(");
}
public void Dispose()
{
}
// a temp handler used to force the SessionStateModule to load session state
public class MyHttpHandler : IHttpHandler, IRequiresSessionState
{
internal readonly IHttpHandler OriginalHandler;
public MyHttpHandler(IHttpHandler originalHandler)
{
OriginalHandler = originalHandler;
}
public void ProcessRequest(HttpContext context)
{
// do not worry, ProcessRequest() will not be called, but let's be safe
throw new InvalidOperationException("MyHttpHandler cannot process requests.");
}
public bool IsReusable
{
// IsReusable must be set to false since class has a member!
get { return false; }
}
}
}
HttpContext.Current.Session should Just Work, assuming your HTTP Module isn't handling any pipeline events that occur prior to the session state being initialized...
EDIT, after clarification in comments: when handling the BeginRequest event, the Session object will indeed still be null/Nothing, as it hasn't been initialized by the ASP.NET runtime yet. To work around this, move your handling code to an event that occurs after PostAcquireRequestState -- I like PreRequestHandlerExecute for that myself, as all low-level work is pretty much done at this stage, but you still pre-empt any normal processing.
Accessing the HttpContext.Current.Session in a IHttpModule can be done in the PreRequestHandlerExecute handler.
PreRequestHandlerExecute: "Occurs just before ASP.NET starts executing an event handler (for example, a page or an XML Web service)." This means that before an 'aspx' page is served this event gets executed. The 'session state' is available so you can knock yourself out.
Example:
public class SessionModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += BeginTransaction;
context.EndRequest += CommitAndCloseSession;
context.PreRequestHandlerExecute += PreRequestHandlerExecute;
}
public void Dispose() { }
public void PreRequestHandlerExecute(object sender, EventArgs e)
{
var context = ((HttpApplication)sender).Context;
context.Session["some_sesion"] = new SomeObject();
}
...
}
If you're writing a normal, basic HttpModule in a managed application that you want to apply to asp.net requests through pages or handlers, you just have to make sure you're using an event in the lifecycle after session creation. PreRequestHandlerExecute instead of Begin_Request is usually where I go. mdb has it right in his edit.
The longer code snippet originally listed as answering the question works, but is complicated and broader than the initial question. It will handle the case when the content is coming from something that doesn't have an ASP.net handler available where you can implement the IRequiresSessionState interface, thus triggering the session mechanism to make it available. (Like a static gif file on disk). It's basically setting a dummy handler that then just implements that interface to make the session available.
If you just want the session for your code, just pick the right event to handle in your module.
Since .NET 4.0 there is no need for this hack with IHttpHandler to load Session state (like one in most upvoted answer). There is a method HttpContext.SetSessionStateBehavior to define needed session behaviour.
If Session is needed on all requests set runAllManagedModulesForAllRequests to true in web.config HttpModule declaration, but be aware that there is a significant performance cost running all modules for all requests, so be sure to use preCondition="managedHandler" if you don't need Session for all requests.
For future readers here is a complete example:
web.config declaration - invoking HttpModule for all requests:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="ModuleWithSessionAccess" type="HttpModuleWithSessionAccess.ModuleWithSessionAccess, HttpModuleWithSessionAccess"/>
</modules>
</system.webServer>
web.config declaration - invoking HttpModule only for managed requests:
<system.webServer>
<modules>
<add name="ModuleWithSessionAccess" type="HttpModuleWithSessionAccess.ModuleWithSessionAccess, HttpModuleWithSessionAccess" preCondition="managedHandler"/>
</modules>
</system.webServer>
IHttpModule implementation:
namespace HttpModuleWithSessionAccess
{
public class ModuleWithSessionAccess : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += Context_BeginRequest;
context.PreRequestHandlerExecute += Context_PreRequestHandlerExecute;
}
private void Context_BeginRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
app.Context.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
}
private void Context_PreRequestHandlerExecute(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
if (app.Context.Session != null)
{
app.Context.Session["Random"] = $"Random value: {new Random().Next()}";
}
}
public void Dispose()
{
}
}
}
Try it: in class MyHttpModule declare:
private HttpApplication contextapp;
Then:
public void Init(HttpApplication application)
{
//Must be after AcquireRequestState - the session exist after RequestState
application.PostAcquireRequestState += new EventHandler(MyNewEvent);
this.contextapp=application;
}
And so, in another method (the event) in the same class:
public void MyNewEvent(object sender, EventArgs e)
{
//A example...
if(contextoapp.Context.Session != null)
{
this.contextapp.Context.Session.Timeout=30;
System.Diagnostics.Debug.WriteLine("Timeout changed");
}
}

Resources