Which LifeCycleEvents to use in .NET MAUI on Android? - android

I have a View (with ViewModel, both Singleton) that reads and writes to a text file.
I would like it to be opened and closed in a controlled manner and am trying to work out where this should happen in the Maui app lifecycle.
In normal operation I assume that I should open it in OnAppearing and close in OnDisappearing.
If however the app is moved into the background and killed, OnDisappearing for the View does not fire.
It does fire OnPause, so I could close the file here and reopen it OnResume but would rather not use these for simplicity (although I will if recommended).
My questions are:
Is there any event that fires when the app is killed by the user (i.e. put in background and swiped away).
If the app is killed, will my text file be tidily flushed and closed?
Does anyone have any recommendations for better ways to do this. I am new to MAUI and may be missing something fundamental.

" Is there any event that fires when the app is killed by the user?"
No. Your app code does not get a chance to run at that time.
As you've discovered, there is a bit of a disconnect between App lifecycle (e.g Deactivated or OnPaused) and Page lifecycle (Appearing/Disappearing).
This is fundamental to Android itself, whether usng Maui or not. On Android, the only event that is guaranteed to happen before an app disappears is OnPaused; Maui app's Deactivated event runs when Android activity's OnPaused is called.
Unfortunately after OnPaused, its possible that your app will not get to run any other code later. You'll have to design everythng with that in mind.
Regardless of what page or popup your app is showing, or what it is in the middle of, your "app Deactivated" code is responsible for saving whatever information needs to be persisted.
Below follows one conceptual way to handle the situation.
"If the app is killed, will my text file be tidily flushed and closed?"
No. Unfortunately, there is no standard mechanism that does this for you.
The burden is on you to keep track of what "clean-up" is needed, depending on what user has done / is doing in app.
Consider defining public class BasePage : ContentPage class, that has some methods that manage this. Have your pages inherit from BasePage.
An example:
// In App.xaml.cs:
protected override void OnDeactivated()
{
CurrentPage?.SaveData();
...
}
private static BasePage CurrentPage;
public static void PageAppearing(BasePage page)
{
CurrentPage = page;
}
public static void PageDisappearing(BasePage page)
{
// Null CurrentPage, but skip that if has already changed to a different page.
// (Fixes a bug on some XForms implementations; haven't tested whether this is still an issue in Maui.)
if (ReferenceEquals(CurrentPage, page)
CurrentPage = null;
}
public class BasePage : ContentPage
{
// NO "InitializeContext". No XAML. Each subclass does that.
public BasePage(){ }
protected override void OnAppearing()
{
base.OnAppearing();
App.PageAppearing(this);
}
protected override void OnDisappearing()
{
App.PageDisappearing(this);
base.OnDisappearing();
}
public virtual void SaveData() {}
}
public partial class MyTextEditPage : BasePage
{
public override void SaveData()
{
... code to save text file being edited ...
}
}
The first thing I do in Deactivated/OnPaused is set a flag: DidPause = true; (refers to declaration private static bool DidPause;). I check that flag in Activated/OnResume, to know that app is continuing after going into background (but was not killed).
Next, have some mechanism to save data that "may have changed" (aka "dirty flag" is set on them), but have not been saved to local files. Save them. (If there is changed information that has not yet been successfully sent to a server, that is more complex. I save locally first; what to do after that is beyond scope of this answer.)
Third, pause or shutdown anything that doesn't need to run while app is in background.
In OnResume, if (DidPause) { DidPause = false; ... } code to restore anything that got shutdown or paused.
In this case, whatever page was showing should still be showing. Data saving was just a precaution, in case the app never comes back.
else { ... } (DidPause not set): The app is starting for first time, or after being killed.

Related

Xamarin Cross Platform: UI is reset after sleep and resume

My application sometimes resets the UI into the initial UI state just like after the first initialization.
The event is occurred when I use another application while the problematic application is sent to background / is sleeping. Then when I change back to the problematic application, it displays the initial state of UI (The initial state of the main page).
I suppose I need to save its state before I minimize the application on OnSleep() event and restore it back on OnResume() event.
How can I do this?
I have solved the problem by using global variable to store whether the application is still running or not.
public MainPage()
{
InitializeComponent();
if(Config.Config._RunningGetLocationThread == true)
{
lbl_Title.Text = "Tracker is running...";
btn_StartTracking.Text = "Stop Tracking!";
}
else
{
btn_StartTracking.Text = "Start Tracking!";
lbl_Title.Text = "Tracker is not running";
}
}
Config.Config._RunningGetLocationThread == true is the global variable and set to true when the application is first started.
I think there should be a better way to save the state of the UI elements. If anyone know the best way to save state UI elements, please share here.

App closing event in Android

Is there any way to know your application is running ?
I want to run a piece of code when Android app is just closed. Any suggestion will be appreciated.
Just to answer my own question now after so much time. When user close the app, the process is terminated with no notice. onDestroy is not guaranteed to be called. only when you explicitly call finish().
I suggest you to make a custom application class and note store the visibility of application wether it is running in background or not.obviously if you don't close the application like this
How to close Android application?
have a look at this so that you don't close it from background and perform the visibility check like this.
public class MyApplication extends Application {
public static boolean isActivityVisible() {
return activityVisible;
}
public static void activityResumed() {
activityVisible = true;
}
public static void activityPaused() {
activityVisible = false;
}
private static boolean activityVisible;
}
and this is how you register you application class to the manifest file.
<application
android:name="your.app.package.MyApplication"
android:icon="#drawable/icon"
android:label="#string/app_name" >
and override these two methods like this.
#Override
protected void onResume() {
super.onResume();
MyApplication.activityResumed();
}
#Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}
now check this status and perform what you like if it is running in background.You can take help of Booleans to check if the application is not closed by other reasons.
In general, there's no such thing as closing applications in Android: the user just stops using the app. It's up to the programmer to make sure that the user does not mention process creation and termination.
Please note that Android may kill the application process when it lacks memory and restart the application later.
For example, one of old office-like apps had the following bug: the user wanted to insert a photo, the office application invoked the Camera app, and Android killed the office app. The office app was not ready for a restart and lost all document changes (which was the bug). Apparently, the buggy app ignored the bundle passed to onCreate().
So the process life cycle and the application life cycle are different things. The process restart is visible to the application: the static variables get reset to their initial values (most likely, null). So it is possible to have a non-null bundle and null static data structures.
One example of executing a piece of code when the process dies may be found below:
Android camera locked after force close .
The problem solved in that post was that Android by itself does not close the camera when the process dies. I cannot tell from your post whether or not your problem is similar to this one.
If you uses, in your Activity, an object derivated from the class: ViewModel, you can capture the event: onCleared(); which is called always, after onDestroy().
See: https://developer.android.com/topic/libraries/architecture/viewmodel

Handle application states (starting/stopping) not activity states

I'm working on my 1st Android app and wondering how to handle activation/deactivation/starting/stopping globally, not on Activity level.
This great article shows states transition for Activities:
http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle
Is there something similar for Application states?
For example at iOS and Windows Phone app there is clear app states separated from activities (views, controllers, whatever).
I'm asking because I want to perform certain operations only once per app loading/exiting not with every activity starting/stopping
The answer is There is Simply No Direct method to do this
rather than in Application Class you can catch these events
#Override
public void onLowMemory()
{
super.onLowMemory();
}
#Override
public void onTerminate()
{
super.onTerminate();
}
So you will have to handle it in all the Activities you will be having
the following methods
onResume()
onStart()
onRestart()
onPause()
onDestroy()
You will have to implement in all Activity to handle for all application
A suggesstion
You can have some Variable in Application class to save application state
say create a variable like
public static boolean isPaused;
and set it from all activity on state change
The question you're asking is applicable for iOS and Windows but not really for Android.
Android doesn't really have a concept of an application as an object, although there's an Application class. Instead, an app is a loose collection of Activities. There are many good reasons for this state of affairs; for example, it supports fast app switching and easy interaction between Activities of different apps.
The best way to coordinate your "app" so that one Activity doesn't try to do something that's already been done is to use SharedPreferences to store app state. Nearly every other way of doing it is less preferred. Even if the system kills off your entire app, SharedPreferences will maintain the current state. The Application object won't.
Also, Android is based on pausing and resuming. An Activity or activities are created, pause, and resume. They may be destroyed, but that's an extreme case. A corollary to this is that apps should not have an exit button; there's no need for one. I sometimes see apps that have one, but what they're really trying to do is shut down a background Service or process. The best way to do that is to have an affordance that says "Sleep" or similar.
Have all activities inherit from the same hierarchy and put whatever you want in OnCreate, OnPause, OnResume, OnStop, OnDestroy and call the super where applicable.
Example
Parent
IamTheParentActivity : Activity
protected void onCreate()
{
setApplicationState(ApplicationState.Running);
}
protected void onPause()
{
setApplicationState(ApplicationState.Paused);
}
private void setApplicationState(Enum ApplicationState)
{
//Some Application Level Variable
Application.State = ApplicationState
}
Children
IamTheChild : IamTheParentActivity
protected void override onCreate()
{
base.OnCreate;
do other stuff
}

Is there a global Start() and Stop() API in Android?

So, I have an Android application. I have.. let's say a Menu > About button on multiple screens or something. I want to keep track of how often a user will push it and when they exit the application, I want to send that count to a remote server. When they start the application again, I need to reset that count to 0.
So far, I imagine some simple static class
public static class ButtonCounter
{
static int Count;
public static void Start()
{
Count=0;
}
public static void Increment()
{
Count++;
}
public static void Stop()
{
//send to server
}
}
Now, the hard part is that I somehow need to globally insert the Start and Stop methods into each activity's OnStart and OnStop method... And even then it'd still require something else so that Stop isn't called when you advance from one screen of the application to the next.
Basically, somehow, I want for Stop to only be called when it is leaving the code I own (ie, obscuring the activity, and moving to an activity not within this application).. and I want Start to only be called when it is entering the code I own AND leaving foreign (not my application) code.
I know my wish is probably not possible with how Android's life cycle for everything works.. but can someone nudge me in what would be the right direction on how to do this most effectively?
(also, I tagged Xamarin, but it should equally apply to standard Java code as well)
If you want to keep track of certain features that the user does throughout using an application, and not have to develop them yourself there are options available for you. I am not precisely sure from the question if this is the case. But there are certain APIs available for use for you, where you can just put a couple lines of code in at various places to keep track of stuff like this for you.
One such solution would be to use a service such as this one here: Flurry. With a service such as this, the work is pretty much done for you for. If you are only interested in developing one yourself, feel free to ignore this response. When using this one myself, it was pretty simple. I just initialize a Flurry variable and then tell it that I want a click here or a function call there to be tracked.
My suggestion is to create a custom base class that extends Activity and have all of your activities extend that base class.
public class MyMonitoredActivity:Activity{
....
protected override void OnStart ()
{
base.OnStart();
ButtonCounter.Start();
}
protected override void OnStop ()
{
base.OnStop();
ButtonCounter.Stop();
}
}
You could then add any sort of boolean checks or what not like #JosephChilberry suggested.

Is any way to detect when any activity of my app are displaying and when all activities are closing?

I.e I would like to know when user interact with my application and when not.
I have tried do it using ActivityManager.getRecentTasks(). I have checked root activity at a top task to detect interact user with my application or not.
I have forced to check it in separated thread each second or two.
This way is bad for me. There is another way to detect when any activity of my app are opening or closed?
Have a look at the lifecycle of an Activity.
There are callback methods (onStart, onResume, onPause, onDestroy, ...) that are invoked by the system whenever your activity is created, becomes active or inactive etc.
You might create your own application class (just inherit from android.app.Application) and do your tracking there. The application will be around as long as your app is running.
For example you could put a flag or a counter there and set it from the activities' callbacks. A simple example for that could be:
public void onResume() {
super.onResume();
((MyApplication)getApplication()).active = true;
}
public void onDestroy() {
super.onDestroy();
((MyApplication)getApplication()).destroyed += 1;
}

Categories

Resources