轉載請說明.java
一款應用的啓動次數,無疑是一項重要的APM的檢測指標.但Android的啓動次數要考慮到一個重要的因素.那就是從後臺切換回前臺的時候.這算一次新的啓動嗎?app
友盟和NewRelic做爲國內外2家這項數據檢測的領頭羊,採用的方法是相似的.可是實現手段徹底不同. this
友盟須要用戶本身在代碼中,用戶手工寫代碼嵌入Activity生命週期的onPause()和onResume()方法中,執行先後臺切換監視的邏輯.spa
newRelic的用戶徹底無需用戶關心這個.它會在編譯時期自動在Activity的生命週期的onStop()和onStart()方法中嵌碼.線程
App先後臺切換的超時時間也不同.友盟是30s,newRelic是5s.code
經過ASM技術.在用戶打包apk的時候動態嵌碼.這裏的實現原理之後再講.blog
用戶若是使用了newRelic的sdk後,會發如今系統的Activity的子類中onCreate()和onStop(),onStart()方法中都嵌碼了. onCreate()方法的嵌碼和Activity的交互Trace有關.暫不討論.生命週期
sdk初始化的時候會執行下面的邏輯get
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory("AppStateMon")); this.activitySnoozeTimeInMilliseconds = snoozeTimeInMilliseconds; executor.scheduleAtFixedRate(this, initialDelay, period, timeUnit); //sdk初始化的時候開始執行,以固定頻率執行看門狗邏輯.檢測app是否進入後臺.(默認5s檢測一次,打盹超過5s認爲進入後臺)
public void run() { synchronized (this.foregroundLock) { if ((this.foregrounded) && (getSnoozeTime() >= this.activitySnoozeTimeInMilliseconds)) { //若是app如今在前臺但打盹時間大於規定時間.認爲應用已經進入後臺. notifyApplicationInBackground(); this.foregrounded = false; } } }
private long getSnoozeTime() //打盹時間 { synchronized (this.foregroundLock) { synchronized (this.snoozeLock) { if (this.snoozeStartTime == 0L) return 0L; return (System.currentTimeMillis() - this.snoozeStartTime); } } }
嵌入在用戶的Activity的代碼中.onStop()方法被嵌入了ApplicationStateMonitor.activityStopped(), onStart()方法嵌入了ApplicationStateMonitor.activityStarted().it
因此Activity執行生命週期的2個方法時.會順帶執行如下2個方法.
public void activityStopped() { synchronized (this.foregroundLock) { synchronized (this.snoozeLock) { this.count -= 1L; if (this.count == 0L) //最關鍵的地方.若是應用進入後臺,打盹時間纔開始記錄 this.snoozeStartTime = System.currentTimeMillis(); } } } public void activityStarted() { synchronized (this.foregroundLock) { synchronized (this.snoozeLock) { this.count += 1L; if (this.count == 1L) { this.snoozeStartTime = 0L; } } if (!(this.foregrounded)) { //若是app原來沒在前臺,則通知SDK已經進入前臺.數據收集工做再次執行. log.verbose("Application appears to be in the foreground"); notifyApplicationInForeground(); this.foregrounded = true; } } }
上面邏輯走的通的一個重要緣由是;一個ActivityA切換到另外一個ActivityB,而後而後走的生命週期流程以下:
A.onStart()---->B.onCreate()-->B.onStart()--->A.onStop()-------(按home鍵進入後臺)>B.onStop()----->B.onDestroty()
count=1 ------------------->count=2----- ------>count=1---------------------------->count=0--->打盹時間的初始值開始肯定爲當前時間
2個Activity的生命週期是重疊的.因此Activity切換的時候不會認爲app在打盹. 一個Activity的界面展現出來是在生命週期的onResume()中.而用戶的界面徹底看不到才執行Activity的onStop()方法.
而Activity生命週期的方法都是在主線程執行的.能夠認爲如下流程是線性的.因此A切換到B的時候 是先運行A的onCreate()->OnStart()->OnResume()以後纔會運行B的OnStop()方法.