Android to determine when the app is open or closed technology research

Source: Internet
Author: User

There are only two things that can make a team united, fearful, or loyal. ---"Speed and passion 7"

Original link: http://engineering.meetme.com/2015/04/android-determine-when-app-is-opened-or-closed/

Problems that exist

Inevitably, Android development will encounter the need to check when the app enters the foreground and when it is closed by the user. Strangely enough, it is not easy to achieve this goal. It's not hard to check the app's first launch, but it's not as easy to tell when it's reopened and closed.

This article will describe a technique for judging the app to open, reopen, and close.

Let's get started.

The key to judging an app's opening and closing is to determine if its activities is being displayed in the foreground. Let's start with a simple example, an app that has only one activity, and doesn't support horizontal mode. To determine whether the app is open or close, you only need to check the activity's OnStart and OnStop methods:

@Overrideprotected void OnStart () {    super.onstart ();    The application has been opened!} @Overrideprotected void OnStop () {    super.onstop ();    The application has been closed!}



The problem with the above example is that the method fails when it is necessary to support horizontal mode. When we rotate the device, the activity will be rebuilt and the OnStart method will be called again, and the app will be incorrectly judged to be opened for the second time.

In order to handle the device rotation, we need to add a verification step. When activity exits, a timer is used to determine if the activity of the app has been launched in a short time, and if not, the user has actually exited the app, and if the activity is restarted, the user still stays in the app.

This check also applies to apps that have multiple activities, because jumping from one activity to another in the app can be handled in this way.

Using this technique I have created a management class, and all of the activities will be notified of this management class when it is visible and not visible. This management class handles the above verification steps for each activity, thus avoiding false detection. It also provides a publish subscription (viewer) mode, which can be used by any module that is interested in launching and closing the app.

The use of this management class is divided into three steps:

1) add it to your project

Import Android.app.activity;import android.os.handler;import Android.os.looper;import Android.os.Message;import Android.support.annotation.nonnull;import Android.text.format.dateutils;import Java.lang.ref.Reference;import Java.lang.ref.weakreference;import Java.util.hashset;import java.util.set;/** * This class was responsible for tracking All currently open activities. * By doing so this class can detect when the application was in the foreground * and when it was running in the background. */public class Appforegroundstatemanager {private static final String TAG = AppForegroundStateManager.class.getSimpleN    Ame ();    private static final int message_notify_listeners = 1; public static final Long App_closed_validation_time_in_ms = * Dateutils.second_in_millis;    Seconds private reference<activity> mforegroundactivity;    Private set<onappforegroundstatechangelistener> mlisteners = new hashset<> (); Private Appforegroundstate mappforegroundstate = appforEgroundstate.not_in_foreground;    Private Notifylistenershandler Mhandler; Make this class A thread safe singleton private static class Singletonholder {public static final Appforegro    Undstatemanager INSTANCE = new Appforegroundstatemanager ();    } public static Appforegroundstatemanager getinstance () {return singletonholder.instance; } private Appforegroundstatemanager () {//Create the handler on the main thread mhandler = new Notifylis    Tenershandler (Looper.getmainlooper ()); } public enum Appforegroundstate {in_foreground, not_in_foreground} public interface Onappforegro Undstatechangelistener {/** Called when the foreground state of the app changes */public void Onappforegro    Undstatechange (Appforegroundstate newstate);        }/** an activity should call the when it becomes visible */public void onactivityvisible (activity activity) { if (mforegroundactivity! = null) MforegroundactiviTy.clear ();        mforegroundactivity = new weakreference<> (activity);    Determineappforegroundstate (); }/** an activity should call the if it is no longer visible */public void onactivitynotvisible (activity activit         Y) {/* * The foreground activity may has been replaced with a new foreground activity in our app.         * So only clear the foregroundactivity if the new activity matches the foreground activity.            */if (mforegroundactivity! = null) {Activity ref = Mforegroundactivity.get (); if (activity = = ref) {//This was the activity that was going away, clear the reference Mfore                Groundactivity.clear ();            Mforegroundactivity = null;    }} determineappforegroundstate (); }/** Use-determine if this app was in the foreground */public Boolean Isappinforeground () {return MAPPFO   Regroundstate = = Appforegroundstate.in_foreground; }/** * Determine the current state, update the tracking global, and notify subscribers if the State has C     Hanged.  */private void Determineappforegroundstate () {/* Get the current state */appforegroundstate oldstate =        Mappforegroundstate; /* Determine what is the new state should is */FINAL Boolean isinforeground = mforegroundactivity! = NULL &&        Mforegroundactivity.get ()! = NULL; Mappforegroundstate = Isinforeground?        AppForegroundState.IN_FOREGROUND:AppForegroundState.NOT_IN_FOREGROUND; /* If the new state is different and the old State, the Notify subscribers of the state, change * * if (Mappforegroun        Dstate! = oldstate) {validatethennotifylisteners ();     }}/** * Add a listener to being notified of app foreground state change events. * * @param listener */public void AddListener (@NonNull Onappforegroundstatechangelistener listener) {m Listeners.add (listener);    }/** * Remove a listener from being notified of the app foreground state change events. * * @param listener */public void RemoveListener (Onappforegroundstatechangelistener listener) {Mlisten    Ers.remove (listener); }/** Notify all listeners the app foreground state has changed */private void notifylisteners (appforegroundstate n        Ewstate) {android.util.log.i (TAG, "notifying subscribers that app just entered state:" + newstate);         for (Onappforegroundstatechangelistener listener:mlisteners) {listener.onappforegroundstatechange (newState); }}/** * This method would notify subscribes that the foreground state have changed when and if Appropria     Te. * <br><br> * We don't want to just notify listeners right away when the app enters of leaves the Foregrou nd. When changing orientations or opening and * Closing the app quickly we briefly pass through a not_in_foreground state That's must be IGNored. To accomplish this a delayed message would be is * Sent when we detect a change. We'll not notify this a foreground change happened until the delay time have been reached.     If a second * foreground change was detected during the delay period then the notification would be canceled. */private void Validatethennotifylisteners () {//If the app has a pending notifications then throw out the E Vent as the state change have failed validation if (Mhandler.hasmessages (message_notify_listeners)) {and            ROID.UTIL.LOG.V (TAG, "Validation failed:throwing out app foreground state change Notification");        Mhandler.removemessages (message_notify_listeners); } else {if (mappforegroundstate = = Appforegroundstate.in_foreground) {//If the app entered th E foreground then notify listeners right away;            There is no validation time for this mhandler.sendemptymessage (message_notify_listeners); } else {                We need to validate that the app entered the background. A delay is used-allow-for-time when the application went into the//background and we do not want to co                NSider the app being backgrounded such as in apps purchasing flow and full screen ads.            Mhandler.sendemptymessagedelayed (Message_notify_listeners, App_closed_validation_time_in_ms); }}} Private class Notifylistenershandler extends Handler {private Notifylistenershandler (Looper loo        Per) {super (Looper);                } @Override public void Handlemessage (Message inputmessage) {switch (inputmessage.what) {  The decoding is do case message_notify_listeners:/* NOTIFY subscribers of The state Change */ANDROID.UTIL.LOG.V (TAG, "App just changed foreground state to:" + mappforegrounds                    Tate); Notifylisteners (mappforegroundstate);                   Break            Default:super.handleMessage (InputMessage); }        }    }}



2) Activities need to send notifications when visibility changes

All activities in the app should add the following code to notify the management class when visibility changes. The best way to achieve this is to add this code to the baseactivity of the project.

@Overrideprotected void OnStart () {    super.onstart ();    Appforegroundstatemanager.getinstance (). onactivityvisible (this);} @Overrideprotected void OnStop () {    appforegroundstatemanager.getinstance (). onactivitynotvisible (this);    Super.onstop ();}



3) Subscribe to the app's foreground visibility Change event

Subscribing to the app foreground visibility Change event in the module of interest, the OnCreate function of the application class is a good place to ensure that every time your app starts and closes, you'll be notified.

public class MyApplication extends application {    @Override public    void OnCreate () {        super.oncreate ();        Appforegroundstatemanager.getinstance (). AddListener (this);    }    @Override public    void Onappforegroundstatechange (Appforegroundstatemanager.appforegroundstate newstate) {        if (AppForegroundStateManager.AppForegroundState.IN_FOREGROUND = = newstate) {            //App just entered the FOREGROUND. Do something here!        } else {            //App just entered the background. Do something Here!        }}}    



Further thinking

There are some details that need to be discussed further, and some of the points discussed below can be fine-tuned for specific applications.

Calibration time

Checking the timer check to see if the app really gets into the background how much time interval is appropriate? The above code is set to 30 seconds for the following reasons.

When your app is running, there may be third-party activities that cover the full screen, and some common examples are Google in-app purchases and the Facebook sign-in registration page. In these cases your app will be forced into the background and the foreground is used to display these third-party pages. It's obviously wrong to think of this as a user leaving your app. The 30-second timeout setting is used to avoid this situation. For example, when a user completes an in-app purchase within 30 seconds, most of the users can do it, and the user does not suddenly leave the app.

If this is not the case for your app, I recommend that you set your calibration time to 4 seconds, which is appropriate for low-provisioning devices when the screen spins the time interval to recreate the activity.

CPU hibernation

A possible problem is when the user closes the app or the app is still in the foreground when the user locks the screen, and the CPU may not wait for the timer to detect and hibernate. In order to ensure that the timer can normally detect the user exiting the app in this case, we need to hold wakelock to prevent the CPU from hibernating until the app shutdown event is confirmed. This is not a problem in practice compared to the use of wakelock.

Determine how the app is started

Now that we know how to detect when the app starts and shuts down, we don't know how the app started. is the user clicking on the notification bar message? Or click a link? Or do they start directly from the desktop icon or recent use?

Trace startup mechanism

First we need to know where to check how the app is started. Based on the previous example, we can print out when the app starts and how it starts.

public class MyApplication extends application {public final String TAG = MyApplication.class.getSimpleName ();    public enum Launchmechanism {DIRECT, NOTIFICATION, URL;    } private Launchmechanism mlaunchmechanism = Launchmechanism.direct;    public void Setlaunchmechanism (Launchmechanism launchmechanism) {mlaunchmechanism = launchmechanism;        } @Override public void OnCreate () {super.oncreate ();    Appforegroundstatemanager.getinstance (). AddListener (this); } @Override public void Onappforegroundstatechange (Appforegroundstatemanager.appforegroundstate newstate) {i F (AppForegroundStateManager.AppForegroundState.IN_FOREGROUND.equals (newstate)) {//APP just entered the Foreg            Round.        LOG.I (TAG, "App Just entered the Foreground with launch mechanism of:" + mlaunchmechanism); } else {//App just entered the background.         Set Our launch mode back to the default of direct.   Mlaunchmechanism = Launchmechanism.direct; }    }}



Setting the startup mechanism

Now we can print the mechanism when the app starts, but we don't have to set it up. So the next step is to write it down when the user launches the app via a link or notification. If not set in either way, the user is launched by clicking on the app icon.

Track Link Click events

To keep track of the user clicking the link to open the app, you need to find the place where the link is handled in the code and add the following code to track the startup mechanism. Make sure that the code is called before the activity's OnStart () function. Where to add the following code depends on your app architecture.

Getapplication (). Setlaunchmechanism (Launchmechanism.url);



Tracking Notification Events

Unfortunately the tracking notification click requires more tricks, and when the notification is displayed, clicking on it will open a pendingintent that was previously bound, and the trick here is to add an identity to all pendingintents of the notification indicating that it was issued by the notification.

For example, when creating pendingintent for notifications, add the following code for each intent:

public static final String extra_handling_notification = "notification.extra_handling_notification";//Put a EXTRA so we Know when an activity launches if it was a from a Notificationintent.putextra (Extra_handling_notification, true);



What we need to do in this step is to check this identity in each activity (unified in baseactivity). When this identity is recognized, it is initiated from the notification, and the startup mechanism can be set to pass notification. This step should be handled in oncreate so that it is set up before the app starts to the foreground (it will trigger the startup mechanism to print).

@Overridepublic void OnCreate (Bundle savedinstancestate) {    super.oncreate (savedinstancestate);    Intent Intent = Getintent ();    if (intent! = null && Intent.getextras ()! = null) {        //Detect If the activity is launched by the user ClickIn G on a notification        if (Intent.getextras (). Getboolean (Extra_handling_notification, False)) {            //Notify the Activity was opened by the user clicking on a notification.            Getapplication (). Setlaunchmechanism (Launchmechanism.notification);}}    



This article is nearing the end, and here you should have mastered how to detect when the app started and shut down, and how it started.

Android to determine when the app is open or closed technology research

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.