Android魔镜:方法耗时统计插件Mirror-基础篇

晓锋,曾在PPTV工做,饿了么资深Android工程师,专一于Android单元测试、架构设计、性能优化、以及最新技术分享,我的博客:michaelzhonghtml

注:本篇是《Android魔镜:方法耗时统计插件Mirror》系列博客的第一篇,后面咱们会持续更新,欢迎关注!java

1 前言

1.1 发生背景

有一天,Boss跑过来讲,下次迭代咱们要作蜂鸟团队App性能调优。对于一个大型成熟的App应用,在业务稳定后,每每会更加关注性能相关的表现。那么,Android App的性能调优该从什么地方入手呢?在进行性能调优、减小应用卡顿过程当中,找出问题——耗时严重的代码,是一个不可或缺且很是重要的步骤,才能有的放矢对症下药。如何发现应用中的耗时任务甚至是耗时函数呢,若是想依靠开发人员经过review代码来找出问题多少有点不太现实,要是能够在日志中或者报表中罗列出每一个方法的执行时间,绝对是一个高效便捷的功能,对耗时方法一目了然。因此,Mirror工具就应运而生。Mirror取自“魔镜”,意为“魔镜!魔镜!照出全部妖魔鬼怪!”android

1.2 传统方式

在此以前,要统计一个函数的执行时间,可能咱们大多数同窗都是这么作的:在方法调用的先后,手动编写耗时统计代码,以下:git

long start = SystemClock.elapsedRealtime();

 // 目标方法
doingSomeThing();

Log.d(TAG, "doingSomeThing()[" + (SystemClock.elapsedRealtime() - start) + "ms]");
复制代码

若是统计一两个方法的执行时间,彻底能够应付的过来。可是若是方法比较多,怎么办,不可能在每一个方法先后都写这样冗余的代码吧。一旦接入Mirror,就能够垂手可得地帮咱们完成冗余繁琐的工做。Mirror是一个Android Studio Gradle插件,在编译时,经过AOP字节码插入的方式对每个方法插入方法耗时统计代码。github

1.3 对比Hugo

此处,可能有同窗会问:Hugo工具也能够作到方法耗时的统计,为何要用Mirror呢?在解答以前,先简单介绍一下Hugo工具。Hugo是国外大佬JakeWharton开发的,经过注解声明的方式,统计函数的执行时间。仍是以上面的例子来说,导入依赖后,直接在doingSomeThing方法上添加@DebugLog注解便可,以下:api

@DebugLog
private void doingSomeThing() {
    ......
}
复制代码

所谓成也萧何,败也萧何!经过注解声明的方式,统计一两个方法耗时倒也方便,要是统计全部方法耗时就没法胜任,不可能注解满天飞吧。再者,注解声明要是多了,代码编译的效率也就下降。所以,Hugo工具只适合有针对性地统计少数方法的耗时。性能优化

2 Gradle插件

在开发实现Mirror工具中,涉及到Gradle插件开发和Aop字节码插入,咱们将以上下两篇博客的形式来说解,本篇重点是Gradle插件开发。服务器

Mirror是在编译时借助于Gradle 插件,利用Aop字节码插入技术,从而帮助咱们能够自动地往每一个方法插入方法耗时统计的代码。不一样于Eclipse,Android Studio开发工具为咱们提供了Gradle Plugin方式,能够自定义Task在编译时期完成咱们制定好的任务。目前,咱们常常用的ButterKnifeGreenDao工具都用到了Gradle插件。自定义Gradle插件,是Android开发人员不可缺乏的一项技能,显得特别基础重要,是时候要学习一波了。架构

基于Mirror为例子,带领你们自定义Gradle插件app

2.1 新建Project

如图新建一个MirrorDemo工程,若是是在原有的Project上开发,这一步就能够跳过。

Mirror-Demo-Project.png

2.2 新建Module

在Project里新建一个Module,这里取名为"plugin",如图。这个Module用于开发Gradle插件,一样Module里面并无Gradle Plugin给你选,可是咱们只是须要一个“容器”来容纳咱们写的插件。所以,你能够随便选择一个Module类型(如Phone、Tablet Module、Android Library),由于在下一步咱们是将里面的大部份内容删除,因此选择哪一个类型的Module不重要。

Mirror-Demo-Module.png

2.3 删除其余配置

将刚才新建的Module中把内容删除,只保留build.gradle文件和src/main目录。

Mirror-Demo-Delelte.png

2.4 新建groovy目录

因为Gradle是基于groovy语言,所以咱们开发的Gradle插件至关于一个groovy项目。因此,须要在main目录下新建groovy目录。

Mirror-Demo-Groovy.png

2.5 配置build.gradle

配置Module编译环境,删除build.gradle原有配置,导入Plugin的依赖配置:

apply plugin: 'groovy'
  
dependencies {
    compile gradleApi() //gradle sdk compile localGroovy() //groovy sdk compile 'com.android.tools.build:gradle:2.3.0' } repositories {
    jcenter()
}
复制代码
2.6 配置Maven

为了方便管理和引用,就要把插件打包发布到Maven仓库里。能够选择打包到本地,或者是远程服务器中。在build.gradle添加以下配置:

apply plugin: 'maven'

def mirror_version = "1.0.0"

uploadArchives {
    repositories.mavenDeployer {
        repository(url: uri('../repo'))
        pom.groupId = 'me.ele'
        pom.artifactId = 'mirror-plugin'
        pom.version = "$mirror_version"
    }
}
复制代码
2.7 建立MirrorPlugin

groovy是基于Java,所以接下来建立groovy的过程跟建立java很相似。在groovy新建包名,如:me.ele.mirror,而后在该包下新建groovy文件,经过new->file->MirrorPlugin.groovy来新建名为MirrorPlugin的groovy文件。

package me.ele.mirror

import org.gradle.api.Plugin
import org.gradle.api.Project

public class MirrorPlugin implements Plugin<Project> {

    void apply(Project project) {
        System.out.println("========================");
        System.out.println("Hello MirrorPlugin!");
        System.out.println("========================");
    }
}
复制代码
2.8 建立properties

在main目录下创建\resources\META-INF\gradle-plugins\me.ele.mirror.plugin.properties文件,如图:

Mirror-Demo-Properties.png

这里须要注意的两点就是:

  1. build.gradle配置文件里要引入的插件名是me.ele.mirror.plugin,即properties文件名,不然就会找不到插件;
  2. implementation-class配置的是继承于Plugin的入口类,即me.ele.mirror.MirrorPlugin,没有.groovy后缀名。
2.9 发布到本地Maven

pluginmodule中,点击Tasks目录下的uploadArchives发布依赖到repo仓库中,如图:

Mirror-Demo-Upload.png

2.10 使用本地仓库

在project 的build.gradle中buildscript中增长本地仓库地址,以下:

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {

    repositories {
        google()
        // 添加本地仓库目录
        maven {
            url uri('./repo') } jcenter() } dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
        // 导入Mirror插件依赖
        classpath 'me.ele:mirror-plugin:1.0.0'
    }
}

allprojects {
    repositories {
        google()
        maven {
            url uri('./repo') } jcenter() } } task clean(type: Delete) {
    delete rootProject.buildDir
}
复制代码

而后在app的build.gradle中增长plugin

// 在编译时期,应用Mirror插件
apply plugin: 'me.ele.mirror.plugin'
复制代码
2.11 build项目

build项目后,能够在Gradle Console窗口中看到输出内容:

Mirror-Demo-Build.png

经过以上步骤,咱们已经实现了自定义Gradle插件。虽然只是一个Demo,可是在看到控制台下打印出自定义的log,心中仍是有很大的成就感,毕竟咱们接触到了一个新姿式。

3 小结

本篇博客主要是带领你们一步步手动自定义一个Gradle插件,是实现Mirror工具的基础,下一篇博客将主要讲Mirror工具中涉及的Aop技术。要是有什么不对的地方,请多多指正!最后,很是感谢你们对本篇博客的关注!

参考文献




阅读博客还不过瘾?

欢迎你们扫二维码经过添加群助手,加入交流群,讨论和博客有关的技术问题,还能够和博主有更多互动

博客转载、线下活动及合做等问题请邮件至 shadowfly_zyl@hotmail.com 进行沟通

相关文章
相关标签/搜索