Android埋點方案的簡單實現(xiàn)-AOP之AspectJ

個人博客

http://www.milovetingting.cn

Android埋點方案的簡單實現(xiàn)-AOP之AspectJ

AOP的定義

AOP為Aspect Oriented Programming的縮寫奋构,意為:面向切面編程悬垃,通過預(yù)編譯方式和運行期間動態(tài)代理實現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)颠猴。

以上關(guān)于AOP的定義引用自百度百科萧落。

AOP的運用場景

日志記錄、性能統(tǒng)計闲延、權(quán)限控制姆涩、埋點等

AOP的具體實現(xiàn)方案有很多菠齿,這里選用AspectJ來簡單實現(xiàn)

  1. 監(jiān)聽View的點擊、頁面打開熙宇、關(guān)閉
  2. 為方法添加開始鳖擒、結(jié)束的日志
  3. 統(tǒng)計方法運行時間

AspectJ的使用

AspectJ的引入

這里引用AspectJX,AspectJX是基于AspectJ的一個AOP框架

新建Android工程烫止,在項目根目錄下的build.gradle文件中添加依賴

dependencies {
        //...
        classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.8'
        //...
    }

新建Module蒋荚,類型選擇Android Library,在新建的library的build.gradle文件中馆蠕,添加相應(yīng)的依賴

apply plugin: 'android-aspectjx'

在app的build.gradle文件中增加對剛才新建的library的引用及AspectJ的依賴

apply plugin: 'android-aspectjx'

dependencies {
    //...
    implementation project(':library')
}

監(jiān)聽View的點擊期升、頁面打開惊奇、關(guān)閉

在library中新建回調(diào)接口TrackCallBack

public interface TrackCallBack {

    /**
     * 當(dāng)View被點擊
     *
     * @param pageName
     * @param viewIdName
     */
    void onClick(String pageName, String viewIdName);

    /**
     * 當(dāng)頁面打開時
     *
     * @param pageName
     */
    void onPageOpen(String pageName);

    /**
     * 當(dāng)頁面關(guān)閉時
     *
     * @param pageName
     */
    void onPageClose(String pageName);

}

在library中新建切入點TrackPoint

public class TrackPoint {

    private static TrackCallBack mTrackCallBack;

    private TrackPoint() {

    }

    /**
     * 初始化
     * @param trackCallBack
     */
    public static void init(TrackCallBack trackCallBack) {
        mTrackCallBack = trackCallBack;
    }

    static void onClick(String pageName, String viewIdName) {
        if (mTrackCallBack == null) {
            return;
        }
        mTrackCallBack.onClick(pageName, viewIdName);
    }

    static void onPageOpen(String pageName) {
        if (mTrackCallBack == null) {
            return;
        }
        mTrackCallBack.onPageOpen(pageName);
    }

    static void onPageClose(String pageName) {
        if (mTrackCallBack == null) {
            return;
        }
        mTrackCallBack.onPageClose(pageName);
    }

}

在library中新建切面TraceAspect

@Aspect
public class TraceAspect {

    private static final String TAG = TraceAspect.class.getSimpleName();

    @Pointcut("execution(* onClick(..))")
    public void onClickPointcut() {

    }

    @Pointcut("execution(* android.app.Activity+.onCreate(..))")
    public void activityOnCreatePointcut() {

    }

    @Pointcut("execution(* android.app.Activity+.onDestroy(..))")
    public void activityDestroyPointcut() {

    }

    @Around("onClickPointcut()")
    public void onClick(ProceedingJoinPoint joinPoint) throws Throwable {
        Object target = joinPoint.getTarget();
        String className = "";
        if (target != null) {
            className = target.getClass().getName();
        }
        Object[] args = joinPoint.getArgs();
        if (args.length > 0 && args[0] instanceof View) {
            View view = (View) args[0];
            String entryName = view.getResources().getResourceEntryName(view.getId());
            TrackPoint.onClick(className, entryName);
        }
        joinPoint.proceed();
    }

    @Around("activityOnCreatePointcut()")
    public void pageOpen(ProceedingJoinPoint joinPoint) throws Throwable {
        Object target = joinPoint.getTarget();
        String className = target.getClass().getName();
        TrackPoint.onPageOpen(className);
        joinPoint.proceed();
    }

    @Around("activityDestroyPointcut()")
    public void pageClose(ProceedingJoinPoint joinPoint) throws Throwable {
        Object target = joinPoint.getTarget();
        String className = target.getClass().getName();
        TrackPoint.onPageClose(className);
        joinPoint.proceed();
    }

}

在app模塊新建Application,在onCreate中執(zhí)行初始化:

public class App extends Application {

    private static final String TAG = TraceAspect.class.getSimpleName();

    @Override
    public void onCreate() {
        super.onCreate();
        TrackPoint.init(new TrackCallBack() {
            @Override
            public void onClick(String pageName, String viewIdName) {
                Log.d(TAG, "onClick:" + pageName + "-" + viewIdName);
                //執(zhí)行相應(yīng)的業(yè)務(wù)
            }

            @Override
            public void onPageOpen(String pageName) {
                Log.d(TAG, "onPageOpen:" + pageName);
                //執(zhí)行相應(yīng)的業(yè)務(wù)
            }

            @Override
            public void onPageClose(String pageName) {
                Log.d(TAG, "onPageClose:" + pageName);
                //執(zhí)行相應(yīng)的業(yè)務(wù)
            }
        });
    }
}

新增的Application需要在AndroidManifest中引用才會生效播赁。

運行App后颂郎,點擊打開另一個Activity,然后依次退出Activity容为,輸出日志如下:

2020-01-13 16:50:17.373 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: onPageOpen:com.wangyz.aspectjdemo.MainActivity
2020-01-13 16:50:19.243 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: onClick:com.wangyz.aspectjdemo.MainActivity-btn_open
2020-01-13 16:50:19.298 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: onPageOpen:com.wangyz.aspectjdemo.SecondActivity
2020-01-13 16:50:21.392 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: onPageClose:com.wangyz.aspectjdemo.SecondActivity
2020-01-13 16:50:22.320 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: onPageClose:com.wangyz.aspectjdemo.MainActivity

為方法添加開始乓序、結(jié)束的日志

在library中增加注解AddLog

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AddLog {
}

在TraceAspect增加以下代碼

@Pointcut("execution(@com.wangyz.library.AddLog * *(..))")
    public void addLogPointcut() {

    }

@Around("addLogPointcut()")
    public void addLog(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        AddLog addLog = signature.getMethod().getAnnotation(AddLog.class);
        if (addLog != null) {
            Object target = joinPoint.getTarget();
            String className = "";
            if (target != null) {
                className = target.getClass().getName();
            }
            Log.d(TAG, "start execute:" + className + "-" + signature.getMethod().getName());
            joinPoint.proceed();
            Log.d(TAG, "end execute:" + className + "-" + signature.getMethod().getName());
        } else {
            joinPoint.proceed();
        }
    }

在MainActivity的onCreate上增加AddLog注解

@AddLog
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //...
    }

運行App后,輸出日志如下:

2020-01-13 16:50:17.373 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: start execute:com.wangyz.aspectjdemo.MainActivity-onCreate
2020-01-13 16:50:17.392 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: end execute:com.wangyz.aspectjdemo.MainActivity-onCreate

統(tǒng)計方法運行時間

在library中增加注解ExecTime

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExecTime {
}

在TraceAspect增加以下代碼

@Pointcut("execution(@com.wangyz.library.ExecTime * *(..))")
    public void execTimePointcut() {

    }

@Around("execTimePointcut()")
    public void execTime(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        ExecTime execTime = signature.getMethod().getAnnotation(ExecTime.class);
        if (execTime != null) {
            long start = System.currentTimeMillis();
            joinPoint.proceed();
            long end = System.currentTimeMillis();
            Object target = joinPoint.getTarget();
            String className = "";
            if (target != null) {
                className = target.getClass().getName();
            }
            Log.d(TAG,
                    "execute time:" + className + "-" + signature.getMethod().getName() + " : " + (end - start) + "ms");
        } else {
            joinPoint.proceed();
        }
    }

在onClick方法上增加ExecTime注解

@ExecTime
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_open:
                Intent intent = new Intent(this, SecondActivity.class);
                startActivity(intent);
                break;
            default:
                break;
        }
    }

運行App后坎背,輸出日志如下:

2020-01-13 16:50:19.272 16610-16610/com.wangyz.aspectjdemo D/TraceAspect: execute time:com.wangyz.aspectjdemo.MainActivity-onClick : 28ms

源碼地址:https://github.com/milovetingting/Samples/tree/master/AspectJDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末替劈,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子沼瘫,更是在濱河造成了極大的恐慌抬纸,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耿戚,死亡現(xiàn)場離奇詭異湿故,居然都是意外死亡,警方通過查閱死者的電腦和手機膜蛔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門坛猪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人皂股,你說我怎么就攤上這事墅茉。” “怎么了呜呐?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵就斤,是天一觀的道長。 經(jīng)常有香客問我蘑辑,道長椅寺,這世上最難降的妖魔是什么牌柄? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任粉私,我火速辦了婚禮肘习,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘副砍。我一直安慰自己衔肢,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布豁翎。 她就那樣靜靜地躺著角骤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪心剥。 梳的紋絲不亂的頭發(fā)上邦尊,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天硼控,我揣著相機與錄音,去河邊找鬼胳赌。 笑死牢撼,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的疑苫。 我是一名探鬼主播熏版,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼捍掺!你這毒婦竟也來了撼短?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤挺勿,失蹤者是張志新(化名)和其女友劉穎曲横,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體不瓶,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡禾嫉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蚊丐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片熙参。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖麦备,靈堂內(nèi)的尸體忽然破棺而出孽椰,到底是詐尸還是另有隱情,我是刑警寧澤凛篙,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布黍匾,位于F島的核電站,受9級特大地震影響呛梆,放射性物質(zhì)發(fā)生泄漏锐涯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一削彬、第九天 我趴在偏房一處隱蔽的房頂上張望全庸。 院中可真熱鬧秀仲,春花似錦融痛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至保礼,卻和暖如春沛励,著一層夾襖步出監(jiān)牢的瞬間责语,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工目派, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留坤候,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓企蹭,卻偏偏與公主長得像白筹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子谅摄,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容