神兵利器—Android方法耗時統計插件Mirror(上)

1 前言

1.1 發生背景

有一天,Boss跑過來說,下次迭代我們要做蜂鳥團隊App性能調優。對于一個大型成熟的App應用,在業務穩定后,往往會更加關注性能相關的表現。那么,Android App的性能調優該從什么地方入手呢?在進行性能調優、減少應用卡頓過程中,找出問題——耗時嚴重的代碼,是一個不可或缺且非常重要的步驟,才能有的放矢對癥下藥。如何發現應用中的耗時任務甚至是耗時函數呢,如果想依靠開發人員通過review代碼來找出問題多少有點不太現實,要是可以在日志中或者報表中羅列出每個方法的執行時間,絕對是一個高效便捷的功能,對耗時方法一目了然。所以,Mirror工具就應運而生。Mirror取自“魔鏡”,意為“魔鏡!魔鏡!照出所有妖魔鬼怪!”

1.2 傳統方式

在此之前,要統計一個函數的執行時間,可能我們大多數同學都是這么做的:在方法調用的前后,手動編寫耗時統計代碼,如下:

long start = SystemClock.elapsedRealtime();

 // 目標方法
doingSomeThing();

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

如果統計一兩個方法的執行時間,完全可以應付的過來。但是如果方法比較多,怎么辦,不可能在每個方法前后都寫這樣冗余的代碼吧。一旦接入Mirror,就可以輕而易舉地幫我們完成冗余繁瑣的工作。Mirror是一個Android Studio Gradle插件,在編譯時,通過AOP字節碼插入的方式對每一個方法插入方法耗時統計代碼。

1.3 對比Hugo

此處,可能有同學會問:Hugo工具也可以做到方法耗時的統計,為什么要用Mirror呢?在解答之前,先簡單介紹一下Hugo工具。Hugo是國外大佬JakeWharton開發的,通過注解聲明的方式,統計函數的執行時間。還是以上面的例子來講,導入依賴后,直接在doingSomeThing方法上添加@DebugLog注解即可,如下:

@DebugLog
private void doingSomeThing() {
    ......
}

所謂成也蕭何,敗也蕭何!通過注解聲明的方式,統計一兩個方法耗時倒也方便,要是統計所有方法耗時就無法勝任,不可能注解滿天飛吧。再者,注解聲明要是多了,代碼編譯的效率也就降低。因此,Hugo工具只適合有針對性地統計少數方法的耗時。

2 Gradle插件

在開發實現Mirror工具中,涉及到Gradle插件開發和Aop字節碼插入,我們將以上下兩篇博客的形式來講解,本篇重點是Gradle插件開發。

Mirror是在編譯時借助于Gradle 插件,利用Aop字節碼插入技術,從而幫助我們可以自動地往每個方法插入方法耗時統計的代碼。不同于Eclipse,Android Studio開發工具為我們提供了Gradle Plugin方式,可以自定義Task在編譯時期完成我們制定好的任務。目前,我們經常用的ButterKnife、GreenDao工具都用到了Gradle插件。自定義Gradle插件,是Android開發人員不可缺少的一項技能,顯得特別基礎重要,是時候要學習一波了。

基于Mirror為例子,帶領大家自定義Gradle插件

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倉庫里??梢赃x擇打包到本地,或者是遠程服務器中。在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技術。要是有什么不對的地方,請多多指正!最后,非常感謝大家對本篇博客的關注!

參考文獻

https://github.com/JakeWharton/hugo
https://github.com/JakeWharton/butterknife
http://greenrobot.org/greendao/
https://docs.gradle.org/current/userguide/groovy_plugin.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容