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.
Related
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.
The Situation
In the official documentation here: https://google.github.io/android-testing-support-library/docs/rules/index.html, it says:
"This rule provides functional testing of a single activity. The
activity under test will be launched before each test annotated with
#Test and before any method annotated with #Before. It will be
terminated after the test is completed and all methods annotated with
#After are finished. The Activity under Test can be accessed during
your test by calling ActivityTestRule#getActivity()."
Technically yes, the Activity is being terminated. But there doesn't seem to be any guarantee as to when this will happen. E.g. it won't necessarily happen before it's created again for the next test.
The Problem
In some of my tests, I need to rely on the fragments OnDestroy or OnDetach being called after each test, before the next test starts. I have listeners that need to be cleared and recreated.
If onDestroy from the previous test is called after OnResume in the current test, then the callback is cleared and the view doesn't get updated and the test fails.
If onDestroy from the previous test is not called at all, then the callback from the current test will be referring to the wrong instance. Again the view will not get updated and the test will fail.
The Question
Is this behaviour discussed in the situation by design or is it a bug? I'm so far unable to find this in the documentation.
What is best practice to handle this? I suspect other people have faced this problem.
Edit: I've now solved part 2. See workarounds section below. However if someone can answer part one by citing an official resource then I'd be happy to accept that answer. That's what I'm really asking here. The second part was just a bonus if someone had some ideas.
The Proof
If you would like to see this behaviour it will only take a few moments. Create a new project with an Activity like this:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onResume() {
super.onResume();
}
#Override
protected void onDestroy() {
super.onDestroy();
}
}
and a test class like this:
#RunWith(AndroidJUnit4.class)
#LargeTest
public class EspressoLifecycleTest {
#Rule
public ActivityTestRule<MainActivity> mActivityRule =
new ActivityTestRule<>(MainActivity.class);
#Test
public void test1() {
}
#Test
public void test2() {
}
#Test
public void test3() {
}
#Test
public void test4() {
}
}
Put breakpoints on the OnResume and OnDestroy methods and run the test suite in debug mode.
Do this a few times and notice that the order the Activity life cycle methods are called is not consistent. E.g. it might call OnResume twice in a row, and then call OnDestroy once, and then OnResume twice again, and then OnDestroy three times, or any other combination you can think of. Of course it always starts with at least one OnResume. Sometimes it doesn't even call OnDestroy if it's at the very end, but that's fine. What's not fine is that my tests are flaky because of this unpredicatable order.
I'm aware that this might be intentional and there could be a simple way to deal with it, I'm just not lucky enough to have found it. Please if you know what it is, post the answer here. I don't care how dumb my question might be in hindsight, I've spent a LOT of time on this problem. It's almost always something simple so I'm prepared to be embarrassed by the answer.
Workarounds
Using onPause over OnDestroy has the side effect of being called when I startActivityForResult, but without calling onResume again in the background fragment while in tablet mode. I'm exploring ways to make this work but no solution as yet.
Edit: onPause ended up with the same problem - which is partly why I was using onDetach in the first place. Ultimately, there are times when I don't want to detach the listeners until the fragment is destroyed.
This leads me to my next idea which worked! Hooray! Up until now I was creating a callback for the calling Activity, for the thing it was asking for, only if that specific callback didn't exist. It turns out this was a bad idea. I did that so I could limit the number of callbacks to the exact number required. The motivation was sound but the implementation required all this callback clearing. The solution is to recreate every callback when ever it's called from the fragment. Don't create it if it's null, always create it and replace whatever was there before. Now there's no need to clear them at all (AFAIK).
It's a bug: http://b.android.com/201513
I use fork work around it: https://github.com/shazam/fork
Noticed this issue before and the 'solution' I can think of is to override methods in ActivityTestRule: afterActivityFinished() or beforeActivityLaunched(). Basically you want to check and wait the listeners are cleared before next test execution.
IMO, this is a bug of ActivityTestRule.
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
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
}
When I write my activities on android, I have to override a lot of "lifecycle" methods, such as onCreate, onActivityResult:
class MyAcitivity extends Activity {
#Override
public void onCreate(...) {}
#Override
public void onStart(...) {}
#Override
public void onActivityResult(...) {}
#Overide
public void onBackPressed(...) {}
}
I don't like this, because I found my logical code are split to everywhere in my class. I do some operation in this method, but I have to handle the result in another method.
Is it the only way to design Activity like this? Is there any other solution can let me do the same without overriding methods from super classes?
Update
I do some operation in this method, but I have to handle the result in another method.
For example:
public void onCreate(...) {
startActivityForResult(new Intent(this, AnotherAcitity.class), INTENT_ANOTHER);
}
public void onActivityResult(...) {
if(requestCode == INTENT_ANOTHER) {
// do something
}
}
Update again
I know how to use these lifecycle methods, what I'm thinking is the "design". Is there any different way to design android (in theory) without "overriding lifecycle methods" to write activities. Does ios and win8 on mobiles use the same design as android? If I develop an ios or win8 application, do I have to override all kinds of lifecycle methods as I do on android?
You only need to override the methods you're using in your Activity. So if your activity simple displays a help page that has already been populated in the XML, you only have to override onCreate() and call setContentView().
In general, if your overriden method is like:
public void myOverridenMethod() {
super.myOverridenMethod();
}
That is to say, it contains nothing but a super call, you need not override it.
In the example you provided, you must override the appropriate lifecycle methods as the calling of these is beyond your control, unless you're willing to develop a custom ROM for your device(s).
EDIT
The Android lifecycle methods are called by the system at specific predefined points in your app's life.
You cannot design an Activity in a different way, as if you do Android has no idea which method does what. However, it knows exactly when to call which method in the Android lifecycle. By using your own methods instead of these, you have an app which Android cannot interact with.
Additionally, many lifecycle methods like onCreate() etc. help setup the initial bits of your app (like getting it a Context).
iOS and Windows Phone and BlackBerry have similar lifecycle methods, but they don't always have an exact Android equivalent, as all are different platforms and handle their apps differently.
This is just a generic framework pattern, framework doesn't depend on you, just notifies you, all your actions are optional for framework. It's called Inversion of Control.
This is just opposite to the direct style of programming where you decide everything about the Application, and give commands to framework.
Google developers designed Activity class, and Android only works through them. Android calls these methods whenever it pleases. Android does not care what you do in those methods, it just cares to notify you of the life cycle events.
Since everything is optional, you just have to fill in the places you are really interested in. An empty Activity runs just fine.
public class MyActivity extends Activity { }
If anything additional needs to be done, just add the code at correct place:
public class MyActivity extends Activity {
#Override
public void onCreate(...) {
//---whatever you want to do in this stage of life cycle----
}
}
You dont necessarily need to override all methods of life cycle of an activty. They all are for a specified purpose
onCreate() :
Called when the activity is first created. This is where you should do
all of your normal static set up: create views, bind data to lists,
etc. This method also provides you with a Bundle containing the
activity's previously frozen state, if there was one. Always followed
by onStart().
onRestart() :
Called after your activity has been stopped, prior to it being started
again. Always followed by onStart()
onStart() :
Called when the activity is becoming visible to the user. Followed by
onResume() if the activity comes to the foreground, or onStop() if it
becomes hidden.
onResume() :
Called when the activity will start interacting with the user. At this
point your activity is at the top of the activity stack, with user
input going to it. Always followed by onPause().
onPause ():
Called as part of the activity lifecycle when an activity is going
into the background,
but has not (yet) been killed. The counterpart to onResume().
When activity B is launched in front of activity A, this callback will be invoked on A.
B will not be created until A's onPause() returns, so be sure to not
do anything lengthy here.
onStop():
Called when you are no longer visible to the user. You will next
receive either onRestart(), onDestroy(), or nothing, depending on
later user activity.
Note that this method may never be called, in low memory situations
where the system does not have enough memory to keep your activity's
process running after its onPause() method is called.
onDestroy() :
The final call you receive before your activity is destroyed. This
can happen either because the activity is finishing (someone called
finish() on it, or because the system is temporarily destroying this
instance of the activity to save space. You can distinguish between
these two scenarios with the isFinishing() method.