国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Android埋點系統(tǒng)設(shè)計

trigkit4 / 1022人閱讀

摘要:一埋點架構(gòu)設(shè)計埋點的核心邏輯抽象將生產(chǎn)的用戶數(shù)據(jù)組織發(fā)送給服務(wù)器。普通埋點定義頁面進入,頁面離開,頁面元素點擊,頁面元素曝光。無埋點進入退出都使用,如何區(qū)分增加了一個字段,用表示頁面進入退出。如何修改字節(jié)碼庫基礎(chǔ)使用。

一、埋點架構(gòu)設(shè)計

埋點的核心邏輯抽象:將“APP生產(chǎn)”“用戶數(shù)據(jù)”組織“發(fā)送給服務(wù)器”。

1.Producer是APP,生產(chǎn)各種用戶數(shù)據(jù)。
2.Consumer是埋點系統(tǒng)的數(shù)據(jù)上傳模塊,把各種用戶數(shù)據(jù)上傳給服務(wù)器。
3.MetaData是對用戶數(shù)據(jù)的抽象。
4.Queue是存儲用戶數(shù)據(jù)的隊列。

抽象邏輯分解為各個子模塊并拼裝,形成最終的架構(gòu)。

1.MetaData模塊:將用戶行為抽象為數(shù)據(jù)描述。
2.Producers模塊:生產(chǎn)用戶數(shù)據(jù),將用戶行為轉(zhuǎn)化為數(shù)據(jù)集。
3.Consumers模塊:消費用戶數(shù)據(jù),將用戶數(shù)據(jù)上傳給服務(wù)器。
4.Storage模塊:存儲用戶數(shù)據(jù),將用戶數(shù)據(jù)暫存在文件中。
5.Broker模塊:管理用戶數(shù)據(jù)。

二、MetaData模塊

關(guān)鍵是特征值的提取。
設(shè)備基礎(chǔ)屬性:
設(shè)備id:udid=12345678910
APP基礎(chǔ)屬性:
版本:v=8.5.0
渠道:c=xiaomi
用戶基礎(chǔ)屬性:
用戶id:ucid=12345678910
頁面元素描述:
屬于哪個APP:pid=bigc_app_xinfang
屬于哪個頁面:key=newhouse/homeindex
屬于哪個頁面元素:為頁面元素定義唯一的code
頁面與頁面關(guān)聯(lián)邏輯:
從哪個頁面進入當前頁面:f=newhouse/homeindex
頁面停留時間:stt=1000
用戶行為描述:
用戶基礎(chǔ)行為描述:evt=xxx,如APP啟動/退出,頁面進入/離開/滑動,頁面元素點擊/曝光,push到達/點擊
用戶擴展行為描述:action=json,如action={"project_name":"thyhwabktj", "xinfangapp_click":"10020"}
舉例:
{v=1.1.6, ts=1527067845806, ucid=null, ssid=b032222c-7105-4119-9bfe-a1aec5ba9285, pid=bigc_app_xinfang, key=newhouse/project, action={"sample_mark":"","project_name":"thyhwabktj"}, longitude=0.0, latitude=0.0, cid=110000, f=newhouse/homeindex?project_name=thyhwabktj&city_id=110000, stt=685, evt=2}

LJ由于歷史原因,有兩套MetaData
無埋點evt定義:app啟動/退出=5,頁面進入/退出=6,頁面滑動=8,頁面元素點擊=7,push到達/點擊=9。
普通埋點evt定義:頁面進入=1,3,頁面離開=2,頁面元素點擊=10186,頁面元素曝光=11316。
Q:無埋點進入/退出都使用6,如何區(qū)分?
A:增加了一個status字段,用status=0/1表示頁面進入/退出。
建議:統(tǒng)一dig埋點和無埋點的evt定義

三、Storage模塊

1.內(nèi)存緩存(List):每生產(chǎn)一條數(shù)據(jù)都會先進入內(nèi)存緩存
2.數(shù)據(jù)庫存儲(DataBase):提供對數(shù)據(jù)庫的操作接口

四、Broker模塊

1.接收生產(chǎn)者的數(shù)據(jù),并寫入數(shù)據(jù)庫(對外提供put方法)
Q:如何控制數(shù)據(jù)由內(nèi)存寫入數(shù)據(jù)庫時機?
1.1.內(nèi)存數(shù)據(jù)超過一定數(shù)量(如20條)時,缺點是應(yīng)用在后臺被殺,最多可能丟失20條數(shù)據(jù)
1.2.生命周期onPause時,缺點是寫操作相對比較頻繁
1.3.利用定時器,缺點是后臺定時任務(wù)可能不執(zhí)行,導(dǎo)致丟數(shù)據(jù)
LJ現(xiàn)行方案:1.1+1.3
建議使用:1.1+1.2
Q:多進程寫數(shù)據(jù)庫怎么辦?
A:多進程向sqlite插入數(shù)據(jù)不會有問題,只是插入順序是亂序的;如果要保證插入順序也一致,可以考慮啟動一個獨立進程操作sqlite,其它進程與sqlite所在進程進行通信。

2.讀取數(shù)據(jù)庫中的數(shù)據(jù),并供給消費者消費(對外提供aquire/release方法)
Q:如何控制數(shù)據(jù)提供給消費者消費的時機?
A:生命周期onPause時,缺點是消費相對比較頻繁

五、Consumers模塊

申請(aquire)數(shù)據(jù),消費(upload)數(shù)據(jù),釋放(release)數(shù)據(jù)
建議:LJ目前的代碼可以參考此設(shè)計進行代碼優(yōu)化

六、Producers模塊

普通埋點數(shù)據(jù)生產(chǎn)
Producers模塊對外提供各種封裝好的add方法供開發(fā)者調(diào)用,如addClickEvent(), addPageEnterEvent(), addPageLeaveEvent()等。

無埋點數(shù)據(jù)生產(chǎn)
Producers模塊自動生產(chǎn)各種埋點數(shù)據(jù),如app啟動/退出,頁面進入/退出/滑動,頁面元素點擊,push到達/點擊等。

七、LJ現(xiàn)有埋點庫

由于歷史原因,LJ總共有2個埋點庫:
dig庫:LJ-APP&BK-APP均在使用,用于對無埋點無法處理的特殊數(shù)據(jù)進行補充
無埋點庫:LJ-APP用的老版本(pid無法自定義,主工程和插件共用主工程的pid),BK-APP用的新版本(pid可以自定義,主工程和插件工程可以分別定義自己的pid)
建議:兩庫合并

八、無埋點實現(xiàn)原理

無埋點的核心是,如何通過代碼自動搜集想要的信息:
1.設(shè)備、APP、用戶等基礎(chǔ)屬性,直接通過api獲取
2.Activity進入/離開等生命周期相關(guān)屬性,直接通過LifeCycleCallback監(jiān)聽獲取
3.Activity的唯一標記如pageId等屬性,直接通過注解獲取
4.UI元素點擊/滑動等行為屬性,需要通過hook代碼才能實現(xiàn)

如何確定UI元素的唯一性:
方案1:為需要統(tǒng)計的元素定義唯一的code,寫入contentDescription,然后讀取這個屬性
方案2:利用ViewTree中的ViewPath唯一確定一個UI元素

核心代碼:

public static ViewPath getPath(View view) {
    do {
      //1.構(gòu)造ViewPath中于view對應(yīng)的節(jié)點:ViewType[index]
      ViewType = view.getClass().getSimpleName();
      index = view在兄弟節(jié)點中的index;
      ViewPath節(jié)點 = ViewType[index];
    } while ((view = view.getParent()) instanceof View);//2.將view指向上一級的節(jié)點
  }

結(jié)果示例:

DecorView/LinearLayout[0]/FrameLayout[0]/ActionBarOverlayLayout[0]/ContentFrameLayout[0]/FrameLayout[0]/LinearLayout[0]/ViewPager[0]/ButtonFragment[0]/AppCompatButton[0]

ViewPath可讀性問題:
可以建立一個Mapping文件,將ViewPath和描述Describe對應(yīng)起來,具體實現(xiàn):
寫一個工具,當我們在手機上點擊一個按鈕的時候彈出彈窗,輸入Describe描述文字,最終生成一個ViewPath<->Describe的Mappting文件。

九、注解基礎(chǔ)知識

1.元數(shù)據(jù)metadata與注解annotation
Java中總共有4種類型:類Class、接口Interface、枚舉Enum、元數(shù)據(jù)@interface(就是注解)。
元數(shù)據(jù):是添加到包、類、方法、屬性上的額外信息,對其進行描述,如@Override。
元注解:是最基本的注解:@Target、@Retention、@Documented、@Inherited
@Target取值:PACKAGE、TYPE、FIELD、METHOD
@Retention取值:SOURCE、CLASS、RUNTIME
注解的作用:編譯時可獲取到注解信息動態(tài)生成代碼,運行時科獲取到注解信息做特殊處理。

2.運行時注解
在運行時通過反射對注解進行處理,比較消耗資源,性能較差。

定義:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface MethodInfo {
?
????String author() default "xiaoming";
?
????String date();
?
????int version() default 1;
}

使用:

public class App {
?
????@MethodInfo(
????????author = “xiaoming@gmail.com”,
????????date = "2018/05/10",
????????version = 2)
????public String getAppName() {
????????return "trinea";
????}
}

解析:

Class cls = Class.forName("com.lianjia.test.annotation.App");
for (Method method : cls.getMethods()) {
    MethodInfo methodInfo = method.getAnnotation(MethodInfo.class);
    System.out.println(“method author: ” + methodInfo.author());
}    

3.編譯時注解
在編譯時通過Java Annotation Process技術(shù)對注解進行處理,因為不使用反射,所以性能較好

模擬ButterKnife定義:

@Retention(CLASS) 
@Target(FIELD)
public @interface InjectView {
??int value();
}

模擬ButterKnife調(diào)用:

@InjectView(R.id.user) 
EditText username;

模擬ButterKnife處理:

@SupportedAnnotationTypes({"com.lianjia.InjectView "})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class MyProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set annotations, RoundEnvironment roundEnv) {
        for (TypeElement typeElement : annotations) {    // 遍歷annotations獲取annotation類型
            for (Element element : roundEnv.getElementsAnnotatedWith(typeElement)) {    // 使用roundEnv.getElementsAnnotatedWith獲取所有被某一類型注解標注的元素,依次遍歷
                // 在元素上調(diào)用接口獲取注解值
                int annoValue = element.getAnnotation(TestAnnotation.class).value();
                String annoWhat = element.getAnnotation(TestAnnotation.class).what();

                System.out.println("value = " + annoValue);
                System.out.println("what = " + annoWhat);

                // 向當前環(huán)境輸出warning信息
                processingEnv.getMessager().printMessage(Kind.WARNING, "value = " + annoValue + ", what = " + annoWhat, element);
            }
        }
        return false;
    }
}
十、Hook代碼實現(xiàn)搜集用戶點擊數(shù)據(jù)

1.Android編譯運行全流程

2.在onClick(View view)方法中加入埋點邏輯
2.1.編寫埋點邏輯:由埋點sdk(LianjiaAnalyticsSdk)完成
2.2.將埋點邏輯插入onClick(View view)方法中:由埋點插件(LianjiaAnalyticsPlugin)完成

3.埋點插件編寫
1.插件編寫流程?plugin編寫。
2.如何侵入編譯流程?transform庫基礎(chǔ)使用。
4.如何修改字節(jié)碼?Javassist庫基礎(chǔ)使用。

4.核心代碼

編寫埋點邏輯:

public class AnalyticsEventsBridge {


  /**
   * Hook onClick(View view)方法,并調(diào)用此方法
   */
  public static void onViewClick(@Nullable View view) {
    // 獲取view的唯一標記等相關(guān)信息
    // 生成一條埋點日志并寫入
  }
}

編寫插件:
1.插件項目目錄結(jié)構(gòu)

2.build.gradle修改

apply plugin: "groovy"

dependencies {
  compile gradleApi()

  compile "com.android.tools.build:gradle:2.3.3"
  compile "org.javassist:javassist:3.21.0-GA"
}

apply from: "./gradle-mvn-push.gradle"
apply plugin: "maven-publish"

publishing {
  publications {
    mavenJava(MavenPublication) {
      groupId PROJ_GROUP
      artifactId PROJ_ARTIFACTID
      version PROJ_VERSION
      from components.java
    }
  }
}

3.插件執(zhí)行入口,相當于Main函數(shù)

class AnalyticsPlugin implements Plugin {

  @Override
  void apply(Project project) {
    InjectAndJarMergingTransform transform = new InjectAndJarMergingTransform()
    android.registerTransform(transform)
  }
}

transform侵入編譯流程:

public class InjectAndJarMergingTransform extends Transform {
  @Override public void transform(@NonNull TransformInvocation invocation)
      throws TransformException, IOException {
    println("LianjiaJarMergingTransform, begin");
    //這里可以獲取到文件的輸入/輸出信息,并對其做相應(yīng)的更改,核心抽象為1個方法
    processClass(inputStream, outputStream);
    println("LianjiaJarMergingTransform, end");
  }
}

Javassist修改字節(jié)碼:

  private void processClass(InputStream inputStream, OutputStream outputStream) throws IOException {
    final ClassPool classPool = AndroidClassPool.getClassPool()
    final CtClass clazz = classPool.makeClass(inputStream)
    final CtMethod ctMethod
    try {
      ctMethod = ctClass.getMethod(targetMethodName, targetMethodDescriptor);
    } catch (NotFoundException e) {
      xxxxxxxx
    }
    //通過過濾器,找到android.view.View$OnClickListener的onClick(View view)方法,略
    //Hook調(diào)用AnalyticsEventsBridge.onViewClick(view)方法
 ctMethod.insertBefore("""com.lianjia.sdk.analytics.gradle.AnalyticsEventsBridge.onViewClick($1);""")
    //其中$0=this, $1=$args[0]表示方法的第一個參數(shù)
}

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/69519.html

相關(guān)文章

  • Android 性能監(jiān)控系列一(原理篇)

    摘要:全稱應(yīng)用性能管理監(jiān)控后面我會通過一系列的文章來介紹的原理框架設(shè)計與實現(xiàn)等等。在應(yīng)用構(gòu)建期間,通過修改字節(jié)碼的方式來進行字節(jié)碼插樁就是實現(xiàn)自動化的方案之一。 showImg(https://segmentfault.com/img/bVbbRX6?w=1995&h=1273); 歡迎關(guān)注微信公眾號:BaronTalk,獲取更多精彩好文! 一. 前言 性能問題是導(dǎo)致 App 用戶流失的罪魁...

    yacheng 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<