通過aspectj對Android資料統計的簡單實現
功能需求
一個專案實現之後,我們並不知道使用者對某個部分的使用頻率是對少,為了更好的來對專案各個功能的使用統計,我們需要做一些資料埋點的功能,也就是每當使用者點選按鈕的時候,都對這次點選進行儲存處理,然後再之後統一上傳到伺服器,進行資料分析。
實現思路
條件
假如,當前有兩個方法進行資料埋點:登入和註冊。
功能表的資料結構如下:功能id、操作次數、操作人id。
public class FunctionsTable { //功能Id private long functionId; //操作次數 private int operateCounts; //操作使用者Id private long operatorId; public long getFunctionId() { return functionId; } public void setFunctionId(long functionId) { this.functionId = functionId; } public int getOperateCounts() { return operateCounts; } public void setOperateCounts(int operateCounts) { this.operateCounts = operateCounts; } public long getOperatorId() { return operatorId; } public void setOperatorId(long operatorId) { this.operatorId = operatorId; } }
下載我們需要做的就是,每次使用者點登陸和註冊的時候,把資料庫中的operateCount欄位每次+1。
分析
首先,最直接的實現方法也就是封裝一個儲存資料的資料庫靜態操作方法,然後將每次需要操作的地方傳入functionId,來進行操作。
這種方法看似可行,但是實際操作的時候會發現很可能需要修改原來的程式碼結構,同時,一旦function的數量到達一定量以後,這時候產品告訴我們,需求要改,還需要加入額外的操作,這時候一旦運氣不好,比如增加一個方法引數,所有呼叫方法地方都得修改一遍。這時候就比較麻煩了。
有沒有什麼更好的方法?肯定是有的。首先分析上述方案,它是一個單一重複的呼叫過程,唯一的區別就是傳入的functionId,這是一個單一卻又重複
具體實現
首先我們選用aop一個常用的類庫:aspectj。因此這裡我們通過位元組碼插樁的方式,修改編譯之後的class來進行程式碼的自動生成,這樣就不會對我們敲程式碼的邏輯產生任何影響。
程式碼原邏輯
非常簡單,就是兩個點選事件,模擬一下登陸註冊的點選。
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btnLogin = findViewById(R.id.btn_login); Button btnRegister = findViewById(R.id.btn_register); btnLogin.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.e("Statistics", "登陸"); } }); btnRegister.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.e("Statistics", "註冊"); } }); } }
aspectJ位元組碼插樁的實現
1.新建一個列舉類來定義需要埋點的功能:登陸、註冊
public enum Function {
LOGIN(1, "登陸"),
REGISTER(2, "註冊");
int functionId;
String functionName;
Function(int functionId, String functionName) {
this.functionId = functionId;
this.functionName = functionName;
}
public String getFunctionName() {
return functionName;
}
}
2.新建一個註解類來標記需要統計點選次數的方法:
使用的時候只需要將需要統計的方法加上註解即可
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Statistics {
Function function();
}
3.Aspectj的簡單實現:
@Aspect
public class StatisticsInstrumentation {
public static final String TAG = "Statistics";
@Around("execution(@com.noob.databurialpoint.Statistics * *(..)) && @annotation(statistics)")
public void aroundJoinPoint(ProceedingJoinPoint joinPoint, Statistics statistics) throws Throwable {
calculate(statistics);
joinPoint.proceed();//執行原方法
}
private void calculate(Statistics statistics){
if(statistics != null){
Log.e(TAG, "對" + statistics.function().getFunctionName() + "進行統計");
// select * from FunctionsTable where operatorId=statistics.getFunctionId()
//if(size > 0){
// int counts = operateCounts ++
// update FunctionsTable set operateCounts = counts
// }else {
// insert into FunctionsTable values (xxx, statistics.getFunctionId(), 1)
// }
}
}
}
程式碼解釋:
@Around("execution(@com.noob.databurialpoint.Statistics * *(..)) && @annotation(statistics)")
- com.noob.databurialpoint.Statistics是Statistics註解的具體包名
- @annotation(statistics) 代表執行方法中傳入註解引數,才能再aroundJoinPoint方法裡獲取這個註解物件
- calculate是點選統計的虛擬碼
註解使用
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnLogin = findViewById(R.id.btn_login);
Button btnRegister = findViewById(R.id.btn_register);
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
@Statistics(function = Function.LOGIN)
public void onClick(View v) {
Log.e("Statistics", "登陸");
}
});
btnRegister.setOnClickListener(new View.OnClickListener() {
@Override
@Statistics(function = Function.REGISTER)
public void onClick(View v) {
Log.e("Statistics", "註冊");
}
});
}
}
測試結果:
相信大家也都看到了,我們根本沒有對之前的方法進行修改,唯一的區別就是在呼叫方法上新增一了一個
@Statistics註解,如果我們需要修改邏輯,也只需要修改一次aspectJ的實現類StatisticsInstrumentation即可,這樣就開發的時候就非常方便。
究其原因是因為aspectj修改了MainActivity.class類,修改後編譯生成的class程式碼如下:
不再只是一個簡單的log列印,而是回去呼叫我們額外寫的StatisticsInstrumentation中的方法.這就是aspectj的作用。
總結
aspectj是一個很好的aop框架,此處只是aspectj的一個簡單使用示例,關於更深入的用法這裡就不再介紹,大家可以去網上尋找相關程式碼。