» » Android Vitals - Why did my process start?

Android Vitals - Why did my process start?

If we post a message and no activities are created at the time it is executed, then we will understand that this is not a cold start, even if the activity eventually starts after 20 seconds.

class MyApp : Application() {

  override fun onCreate() {
    super.onCreate()

    var firstActivityCreated = false

    registerActivityLifecycleCallbacks(object :
        ActivityLifecycleCallbacks {

      override fun onActivityCreated(
          activity: Activity,
          savedInstanceState: Bundle?
      ) {
        if (firstActivityCreated) {
          return
        }
        firstActivityCreated = true
      }
    })
    Handler().post {
      if (firstActivityCreated) {
        // TODO рапортуем о холодном запуске
      }
    }
  }
}

This approach assumes that we must wait for the activity to launch or for this message to complete before we know if the application was cold starting. Sometimes it would be useful to know it right inside Application.onCreate(). For example, we may need asynchronous resource preloading to optimize cold start:

class MyApp : Application() {

  override fun onCreate() {
    super.onCreate()

    if (isColdStart()) {
      preloadDataForUiAsync()
    }
  }
}

The importance of the process

While there is no Android API yet to find out why a process was running, we still have a way to find out why a process is still running: 

RunningAppProcessInfo.importance , which we can read from ActivityManager.getMyMemoryState() . According to the documentation “Processes and Application Lifecycle” :

To determine which processes should be terminated when out of memory, Android places each process in a “hierarchy of importance” based on the components they run and their states. [...] The system will base its decision on how to classify a process on the most important level found among all the components active in the process at that moment.

When a process starts, we can check its importance. If its importance is rated as IMPORTANCE_FOREGROUND , then it is a cold start:

class MyApp : Application() {

  override fun onCreate() {
    super.onCreate()

    if (isForegroundProcess()) {
      preloadDataForUiAsync()
    }
  }

  private fun isForegroundProcess(): Boolean {
    val processInfo = ActivityManager.RunningAppProcessInfo()
    ActivityManager.getMyMemoryState(processInfo)
    return processInfo.importance == IMPORTANCE_FOREGROUND
  }
}

Data validation

I have implemented both cold start detection approaches in a test application and got identical results in all cases. I then added the discovery code to the production app with enough installs to provide meaningful results. Here are my (anonymized) results:

 

This pie chart only shows app launches that had an activity created prior to the first post.

  • 0.2% correspond to other values.

Let's look at this data from a different angle: how often was an activity created prior to the first post for each importance value?

 

  • When the launch importance is 100, an activity is always created before the first post. And when an activity is created before the first message, 97.4% of the time the importance is 100.

  • When the launch importance is 400, the activity is almost never created until the first post. “Almost never” is not “never” - there are still enough cases when the activity is created before the first post. In 2.4% of cases, the importance is 400.

The most likely explanation for the 2.4% with a importance of 400 is that these were not cold starts, however, the system received a request to start the activity almost immediately after the process started, right on startup Application.onCreate(), but before we had a chance to add our first fast.

Correction: I saved the importance in the log after the first post and compared it with the importance at the start of the application. My data showed that in 74% of app launches, when an activity was created prior to the first post, and the launch process importance was not initially set to 100, after the first post, the importance value of this process changed to 100. I think this confirms the theory that that the system decided to launch the activity after the application had already started to run.

Conclusion

Now, based on this information and the findings from the previous post, we can pinpoint a cold start. For cold start:

  • The first activity is created before the first post is dequeued.

  • The first activity is created with a zero bundle.

 Here is the updated code:

class MyApp : Application() {

  override fun onCreate() {
    super.onCreate()

    if (isForegroundProcess()) {
      var firstPostEnqueued = true
      Handler().post {
        firstPostEnqueued = false
      }
      registerActivityLifecycleCallbacks(object :
          ActivityLifecycleCallbacks {

        override fun onActivityCreated(
            activity: Activity,
            savedInstanceState: Bundle?
        ) {
          unregisterActivityLifecycleCallbacks(this)
          if (firstPostEnqueued && savedInstanceState == null) {
            // TODO рапортуем о холодном запуске
          }
        }
      })
    }
  }

  private fun isForegroundProcess(): Boolean {
    val processInfo = ActivityManager.RunningAppProcessInfo()
    ActivityManager.getMyMemoryState(processInfo)
    return processInfo.importance == IMPORTANCE_FOREGROUND
  }
}

Found this interesting? Leave us a comment!

Related Articles

Add Your Comment

reload, if the code cannot be seen

All comments will be moderated before being published.