diff --git a/src/CONST.js b/src/CONST.js
index 779a6edd1a6f..0d0e151adfc8 100644
--- a/src/CONST.js
+++ b/src/CONST.js
@@ -78,6 +78,11 @@ const CONST = {
         TIMEZONE: 'timeZone',
     },
     DEFAULT_TIME_ZONE: {automatic: true, selected: 'America/Los_Angeles'},
+    APP_STATE: {
+        ACTIVE: 'active',
+        BACKGROUND: 'background',
+        INACTIVE: 'inactive',
+    },
 
     // at least 8 characters, 1 capital letter, 1 lowercase number, 1 number
     PASSWORD_COMPLEXITY_REGEX_STRING: '^(?=.*[A-Z])(?=.*[0-9])(?=.*[a-z]).{8,}$',
diff --git a/src/libs/AppStateMonitor/index.js b/src/libs/AppStateMonitor/index.js
new file mode 100644
index 000000000000..63f2f448ac35
--- /dev/null
+++ b/src/libs/AppStateMonitor/index.js
@@ -0,0 +1,35 @@
+import {AppState} from 'react-native';
+import CONST from '../../CONST';
+import shouldReportActivity from './shouldReportActivity';
+
+let appState = CONST.APP_STATE.ACTIVE;
+
+/**
+ * Listener that will only fire the callback when the user has become active.
+ *
+ * @param {Function} callback
+ * @returns {Function} to unsubscribe
+ */
+function addBecameActiveListener(callback) {
+    /**
+     * @param {String} state
+     */
+    function appStateChangeCallback(state) {
+        if (
+            shouldReportActivity
+            && (appState === CONST.APP_STATE.INACTIVE || appState === CONST.APP_STATE.BACKGROUND)
+            && state === CONST.APP_STATE.ACTIVE
+        ) {
+            callback();
+        }
+        appState = state;
+    }
+    AppState.addEventListener('change', appStateChangeCallback);
+    return () => {
+        AppState.removeEventListener('change', appStateChangeCallback);
+    };
+}
+
+export default {
+    addBecameActiveListener,
+};
diff --git a/src/libs/AppStateMonitor/shouldReportActivity/index.js b/src/libs/AppStateMonitor/shouldReportActivity/index.js
new file mode 100644
index 000000000000..05939ab7eb8d
--- /dev/null
+++ b/src/libs/AppStateMonitor/shouldReportActivity/index.js
@@ -0,0 +1,4 @@
+// We only need to report when the app becomes active on native since web maintains most of it's network functions while
+// in the "background" and the concept is not quite the same on mobile. We avoid setting this to true for web since
+// the event would fire much more frequently than it does on native causing performance issues.
+export default false;
diff --git a/src/libs/AppStateMonitor/shouldReportActivity/index.native.js b/src/libs/AppStateMonitor/shouldReportActivity/index.native.js
new file mode 100644
index 000000000000..ff3177babdde
--- /dev/null
+++ b/src/libs/AppStateMonitor/shouldReportActivity/index.native.js
@@ -0,0 +1 @@
+export default true;
diff --git a/src/libs/NetworkConnection.js b/src/libs/NetworkConnection.js
index d37559dcb5cf..f98512acf8b8 100644
--- a/src/libs/NetworkConnection.js
+++ b/src/libs/NetworkConnection.js
@@ -1,16 +1,16 @@
 import _ from 'underscore';
-import {AppState} from 'react-native';
 import Onyx from 'react-native-onyx';
 import NetInfo from './NetInfo';
 import ONYXKEYS from '../ONYXKEYS';
+import SleepTimer from './SleepTimer';
+import AppStateMonitor from './AppStateMonitor';
 
 // NetInfo.addEventListener() returns a function used to unsubscribe the
 // listener so we must create a reference to it and call it in stopListeningForReconnect()
 let unsubscribeFromNetInfo;
-let sleepTimer;
-let lastTime;
+let unsubscribeFromSleepTimer;
+let unsubscribeFromAppState;
 let isOffline = false;
-let listeningForAppStateChanges = false;
 let logInfo = () => {};
 
 // Holds all of the callbacks that need to be triggered when the network reconnects
@@ -42,10 +42,6 @@ function setOfflineStatus(isCurrentlyOffline) {
     isOffline = isCurrentlyOffline;
 }
 
-function logAppStateChange(state) {
-    logInfo('[NetworkConnection] AppState change event', true, {state});
-}
-
 /**
  * Set up the event listener for NetInfo to tell whether the user has
  * internet connectivity or not. This is more reliable than the Pusher
@@ -54,10 +50,9 @@ function logAppStateChange(state) {
 function listenForReconnect() {
     logInfo('[NetworkConnection] listenForReconnect called', true);
 
-    if (!listeningForAppStateChanges) {
-        AppState.addEventListener('change', logAppStateChange);
-        listeningForAppStateChanges = true;
-    }
+    unsubscribeFromAppState = AppStateMonitor.addBecameActiveListener(() => {
+        triggerReconnectionCallbacks('app became active');
+    });
 
     // Subscribe to the state change event via NetInfo so we can update
     // whether a user has internet connectivity or not.
@@ -68,18 +63,11 @@ function listenForReconnect() {
 
     // When a device is put to sleep, NetInfo is not always able to detect
     // when connectivity has been lost. As a failsafe we will capture the time
-    // every two seconds and if the last time recorded is greater than 4 seconds
+    // every two seconds and if the last time recorded goes past a threshold
     // we know that the computer has been asleep.
-    lastTime = (new Date()).getTime();
-    clearInterval(sleepTimer);
-    sleepTimer = setInterval(() => {
-        const currentTime = (new Date()).getTime();
-        const isSkewed = currentTime > (lastTime + 4000);
-        if (isSkewed) {
-            triggerReconnectionCallbacks('sleep timer clock skewed');
-        }
-        lastTime = currentTime;
-    }, 2000);
+    unsubscribeFromSleepTimer = SleepTimer.addClockSkewListener(() => (
+        triggerReconnectionCallbacks('timer clock skewed')
+    ));
 }
 
 /**
@@ -87,15 +75,17 @@ function listenForReconnect() {
  */
 function stopListeningForReconnect() {
     logInfo('[NetworkConnection] stopListeningForReconnect called', true);
-    clearInterval(sleepTimer);
-    sleepTimer = null;
     if (unsubscribeFromNetInfo) {
         unsubscribeFromNetInfo();
         unsubscribeFromNetInfo = undefined;
     }
-    if (listeningForAppStateChanges) {
-        AppState.removeEventListener('change', logAppStateChange);
-        listeningForAppStateChanges = false;
+    if (unsubscribeFromSleepTimer) {
+        unsubscribeFromSleepTimer();
+        unsubscribeFromSleepTimer = undefined;
+    }
+    if (unsubscribeFromAppState) {
+        unsubscribeFromAppState();
+        unsubscribeFromAppState = undefined;
     }
 }
 
diff --git a/src/libs/SleepTimer/index.js b/src/libs/SleepTimer/index.js
new file mode 100644
index 000000000000..75c722304e2f
--- /dev/null
+++ b/src/libs/SleepTimer/index.js
@@ -0,0 +1,37 @@
+/**
+ * The Timer module is used on web/desktop to detect when a computer has gone to sleep. We don't use this on native
+ * mobile since it does not work reliably and fires at inappropriate times.
+ */
+let sleepTimer;
+let lastTime;
+
+/**
+ * Adds a listener for detecting when laptop screens have closed or desktop computers put to sleep. Not reliable on
+ * native platforms.
+ *
+ * @param {Function} onClockSkewCallback function to call when the
+ * @returns {Fuction} that when called clears the timer
+ */
+function addClockSkewListener(onClockSkewCallback) {
+    clearInterval(sleepTimer);
+    sleepTimer = setInterval(() => {
+        const currentTime = (new Date()).getTime();
+        const isSkewed = currentTime > (lastTime + 8000);
+        lastTime = currentTime;
+
+        if (!isSkewed) {
+            return;
+        }
+
+        onClockSkewCallback();
+    }, 2000);
+
+    return () => {
+        clearInterval(sleepTimer);
+        sleepTimer = null;
+    };
+}
+
+export default {
+    addClockSkewListener,
+};
diff --git a/src/libs/SleepTimer/index.native.js b/src/libs/SleepTimer/index.native.js
new file mode 100644
index 000000000000..96dcb1988f45
--- /dev/null
+++ b/src/libs/SleepTimer/index.native.js
@@ -0,0 +1,8 @@
+/**
+ * We don't want the clock skew listener to run on native as it only helps us on desktop/web when a laptop is closed
+ * and reopened. This method of detecting timing variance to see if we are inactive doesn't work well on native mobile
+ * platforms. These platforms should use AppState instead to determine whether they must catch up on missing data.
+ */
+export default {
+    addClockSkewListener: () => () => {},
+};
