APM之Android应用的启动次数(1)

 转载请说明.

  一款应用的启动次数,无疑是一项重要的APM的检测指标.但Android的启动次数要考虑到一个重要的因素.那就是从后台切换回前台的时候.这算一次新的启动吗?

  友盟和NewRelic作为国内外2家这项数据检测的领头羊,采用的方法是类似的.但是实现手段完全不一样. 

 友盟和newRelic的实现方法:

  友盟需要用户自己在代码中,用户手工写代码嵌入Activity生命周期的onPause()和onResume()方法中,执行前后台切换监视的逻辑.

  newRelic的用户完全无需用户关心这个.它会在编译时期自动在Activity的生命周期的onStop()和onStart()方法中嵌码.

  App前后台切换的超时时间也不一样.友盟是30s,newRelic是5s.

 newRelic的实现原理:

  通过ASM技术.在用户打包apk的时候动态嵌码.这里的实现原理以后再讲.

  用户如果使用了newRelic的sdk后,会发现在系统的Activity的子类中onCreate()和onStop(),onStart()方法中都嵌码了. onCreate()方法的嵌码和Activity的交互Trace有关.暂不讨论.

sdk初始化的时候会执行下面的逻辑

  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().

  所以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()方法.

  

 

posted @ 2015-04-24 18:12  laiqurufeng  阅读(947)  评论(0编辑  收藏  举报