Rabbit is a framework I am currently working on to improve the efficiency and quality of App development. It is generally positioned as an APM framework.
APM is an essential function for calculating application cold startup time and page rendering time. This function is implemented in Rabbit based on the implementation of Android automatic page speed measurement in Meituan. The following function points have been achieved:
Application.onCreate
Time-consuming statistics- Example Collect statistics about the cold start time
Activity.onCreate
Time-consuming statisticsActivity
For the first timeinflate
Time-consuming statisticsActivity
First render time- Monitor page network request time
The specific statistical timing is shown below:
The final output looks like this:
Apply start speed measurement
Page start speed measurement
Network time measurement
Method of use
The core idea of the whole speed measurement component is to use Gradle plug-in to dynamically inject monitoring code during application compilation. Build. Gradle: build.gradle: build.gradle: build.gradle
apply plugin: 'rabbit-tracer-transform'
Copy the code
To support network monitoring, an interceptor needs to be inserted during OkHttpClient initialization (currently only OkHttp network monitoring is supported):
OkHttpClient.Builder().addInterceptor(Rabbit.getApiTracerInterceptor())
Copy the code
We’ll consider making Interceptor’s initialization AOP later.
No additional initialization code is required for the Rabbit speed detection function. Here’s a quick overview of how it works:
Use onCreate to collect time statistics
Implementation idea:
- When compiling the application
Application. AttachBaseContext ()
andEnd Application. The onCreate ()
Method to insert time statistics code. - The SDK collects the speed data and presents it.
For bytecode insertion at compile time, this article will not do a detailed implementation analysis. For specific implementation, you can refer to the Rabbit source code. The final result of the insertion is as follows:
public class CustomApplication extends Application {
protected void attachBaseContext(Context base) {
AppStartTracer.recordApplicationCreateStart();
super.attachBaseContext(base);
}
public void onCreate() { super.onCreate(); Rabbit.init(this); AppStartTracer.recordApplicationCreateEnd(); }}Copy the code
Page rendering time statistics
When is the page rendered complete?
When the page is rendered, we can listen for activity.contentView drawing by listening for viewGroup.dispatchDraw ().
Manually add a layer of custom parent View to the View set by activity.setContentView (), which is used to calculate the completion time of drawing
public class ActivitySpeedMonitor extends FrameLayout {
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
RabbitTracerEventNotifier.eventNotifier.activityDrawFinish(getContext(), System.currentTimeMillis());
}
public static void wrapperViewOnActivityCreateEnd(Activity activity) {
FrameLayout contentView = activity.findViewById(android.R.id.content);
ViewGroup contentViewParent = (ViewGroup) contentView.getParent();
if(contentView ! = null && contentViewParent ! = null) { ActivitySpeedMonitor newParent = new ActivitySpeedMonitor(contentView.getContext());if(contentView.getLayoutParams() ! = null) { newParent.setLayoutParams(contentView.getLayoutParams()); } contentViewParent.removeView(contentView); newParent.addView(contentView); contentViewParent.addView(newParent); }}}Copy the code
Above ActivitySpeedMonitor. WrapperViewOnActivityCreateEnd () code will be inserted in the Activity at compile time. The onCreate method:
public class TransformTestActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { ActivitySpeedMonitor.activityCreateStart(this); super.onCreate(savedInstanceState); this.setContentView(2131296286); ActivitySpeedMonitor.wrapperViewOnActivityCreateEnd(this); }}Copy the code
Activity Collects the first inflate time statistics
We know that the viewgroup.dispatchDraw () method is called when the ViewTree changes and normally dispatchDraw() is called the first time with the following code:
setContentView(R.layout.activity_transform_test);
Copy the code
Therefore, Rabbit treats the completion time of the Activity’s first dispatchDraw() method as the point at which the Activity’s first Inflate ends.
In fact, this length of time can represent the complexity of the Activity’s layout.
The first rendering time of the Activity
The end point of this time statistics is: the page initiates a network request to get the data and completes the page rendering
For example, if your application home page has 3 interfaces, the data of these 3 interfaces constitute the UI of the entire home page, and the rendering time of the home page is 3 interfaces to complete the request, and the data rendering is completed.
Rabbit_speed_monitor. json rabbit_speed_monitor.json file in the assest folder.
{
"home_activity": "MainActivity"."page_list": [{"page": "MainActivity"."api": [
"xxx/api/getHomePageRecPosts"."xxx/api/getAvailablePreRegistrations"."xxxx/api/appHome"]}... ] }Copy the code
Home_activity Collects statistics on the cold startup time of applications.
Page_list Configures the page whose rendering time needs to be counted.
Rabbit records the render time when all specified interfaces are complete and the viewgroup.dispatchDraw () method is complete:
RabbitAppSpeedMonitor.java
fun activityDrawFinish(activity: Any, drawFinishTime: Long) {
val apiStatus = pageApiStatusInfo[currentPageName]
if(apiStatus ! = null) {if(apiStatus allApiRequestFinish ()) {/ / all the request has been completed pageSpeedCanRecord =false. / / statistics only once pageSpeedInfo fullDrawFinishTime = drawFinishTime RabbitDbStorageManager. Save (pageSpeedInfo)}}}Copy the code
How do you count interface completion?
Network request time monitoring
Also use RabbitAppSpeedInterceptor, monitoring network takes time but here it’s not that we really understand the request for network time consuming, with time around: network request takes ~ application processing time consuming, concrete implementation core code is as follows:
class RabbitAppSpeedInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val startTime = System.currentTimeMillis()
val request = chain.request()
val requestUrl = request.url().url().toString()
val response = chain.proceed(request)
if(! RabbitTracer.monitorRequest(requestUrl))returnThe response / / no need to monitor the request val costTime = System. CurrentTimeMillis () - startTime RabbitTracer. MarkRequestFinish (requestUrl, costTime)return response
}
}
Copy the code
App cold startup time statistics
Combining the above narrative, Rabbit definition App cold start time for HomeActivity rendering is finished – Application. AttachBaseContext () at the beginning.
HomeActivity can be configured using rabbit_speed_monitor.json:
{
"home_activity": "MainActivity"."page_list": [...]. }Copy the code
conclusion
The implementation principle of the application velocimetry component is not very complicated, but it still involves many points. For the implementation logic, see: Rabbit
The timing of the count currently used in Rabbit may not be the most appropriate. If you know of a better time, please contact us.
For details about how to implement the Rabbit function, see “Anatomy of Rabbit implementation”
Finally, please follow my wechat account: