Skip to content

Tracker Api 操作符

zhiHaoWu edited this page Nov 6, 2019 · 1 revision

简介

Tracker 是一个用户操作路径跟踪库。根据预先创建好的一系列路径,当用户的操作和设定一样时,通过回调的方式告知订阅者。 通过Tracker ,可以实现 指定路径无痕埋点。

观察者-订阅:

观察的意思是指不对原view 进行任何多余的操作。如 viewClick(R.id.button) 如果你没有对 R.id.button setOnClick 事件,那么你监听是无论如何也不会触发。

监听页面的跳转

Track.from(MainActivity.class).to(SecondActivity.class).subscribe(new OnSubscribe<Intent>() {
    @Override
    public void call(Intent intent) {
        Log.d(TAG, "A->B subscribe intent=" + intent + " t=" + Thread.currentThread());
        //UmengOnEvent.onEventFix("xxxx"); //调用友盟统计
    }
});

意思是指A->B( A 跳转(startActivity...)B 页面) 时,会调用OnSubscribe 回调,在回调中做具体埋点逻辑,参数intent 是跳转的intent,可以通过它获取某些数据。如不需要,可使用 OnEvent 无参的回调。
当然还可以继续 to Track.from(AActivity.class).to(BActivity.class).to(CActivity.class)... 表示A->B->C 的情况。
注:

  1. 而且必须是A->B 后B->C,如果A->B->D,D 页面中跳C,这时是不会触发的,因为第二个to(C),其实就指定了它的from 必须为B
  2. 如果用户操作为 A->B->D ,在D中,再返回到B,再由B->C ,那么也会触发事件,这里称 B->D(B跳D),再D<-B(D 返回到B)的操作称为中间操作,无论你的中间操作是什么,点击,还是跳转,都是无关的,因为最终还是做了B->C的操作,所以无论你中间做了什么,也还是认为符合你需要的,所以会触发回调。

监听页面 view被点击

Track.from(MainActivity.class).viewClick(R.id.tv_text).subscribe(new OnSubscribe<View>() {
    @Override
    public void call(View view) {
        Log.d(TAG, "A.viewClick(R.id.tv_text) v:" + view + " t=" + Thread.currentThread());
    }
});

表示的是MainActivity 中一个R.id.tv_text 这个view 被点击时触发。回调参数view 为被点击的view 。回调在子线程,可以获取属性,但不能操作UI. 注:因为同一id 可以在多个页面存在,所以这里是指必须为MainActivity 页面中的 R.id.tv_text 被点击。

View 系列包含下列方法

Track 方法 Aop 切入点对应方法
viewClick(viewid) view.setOnClickListener
viewVisibility(viewId) view.setVisiblity
viewLongClick(viewId) view.setOnLongClickListener

现在可以通过组合成这样的代码:

Track.from(AActivity.class).to(BActivity.class).viewClick(R.id.button).to(CActivity.class)
    .subscribe(new OnSubscribe<Intent>() {
        @Override
        public void call(Intent view) {
            Log.d(TAG,"A->B.c->C view:"+view+" t="+Thread.currentThread());
    }
});

表示A->B ,然后B 页面的 R.id.button 被点击,然后跳转到 C 页面。当然,可能是button 的点击事件中写的跳转,或者是先点击button ,然后其它操作导致跳转,无所谓,这两种都会触发。但一定是点击事件在跳转前,如果B页面的其它操作跳转,但是还没有点击过button ,此时也不会触发。因为事件是顺序性的。

from 表示事件的起源,一般为activity 。基于此activity 后面发生的一些操作。 当然,常用activity 生命周期也可以监听,实现方式是通过application.registerActivityLifecycleCallbacks.

Track 方法 对应activity 生命周期
activityOnCreated onCreate
activityOnStarted onStart
activityOnResumed onResume
activityOnPaused onPause
activityOnStoped onStop
activityOnDestroyed onDestroy
activityOnSaveInstanceState onSaveInstanceState

当然,fragment 系列生命周期也是可以监听的,使用Aop的方式

Track 方法 对应fragment生命周期
fragmentOnCreated(xxx.class) onCreate
fragmentOnStart(xxx.class) onStart
fragmentOnResumed(xxx.class) onResume
fragmentOnPaused(xxx.class) onPause
fragmentOnStoped(xxx.class) onStop
fragmentOnDestroyed(xxx.class) onDestroy
fragmentOnHiddenChanged(...) onHiddenChanged
fragmentSetUserVisibleHintd(..) setUserVisibleHint

你可能发现,fragment 的生命周期参数中需要填写具体fragment.class ,而activity 生命周期不需要。因为fragment 不能做为from ,它必须依附在某 FragmentActivity 上。 Track.from(TabFragmentActivity.class).fragmentOnCreate(MainFragment.class)... 所以这条代表TabFragmentActivity 中有一个fragment (MainFragment)执行了onCreate生命周期时触发。

注:fragment 必须是android.support.v4.app.Fragment,因为 android.app.Fragment 属于framework 层,无法用Aop切入。而常用的也是v4下的fragment

Dialog 支持方法如下: 支持所有dialog 的子类

Track 方法 对应Aop 方法
dialogShow(xxxDialog.class) dialog.show()
dialogDismiss(xxxDialog.class) dialog.dismiss()
dialogButtonClick(int buttonId) dialogInterface.Onclickener.onClick

其中 dialogDismiss 事件,只有当你主动调用 dialog.dismiss() 时才会触发。而点击区域外自动消失,是监听不到的。虽然库可以通过主动调用 dialog.setOnDismissListener 来监听,但是不符合观察者模式,所以放弃,不能对view做任何多余操作。dialogButtonClick 是dialog 的三个按钮点击事件,指POSITIVE,NEGATIVE,NEUTRAL。

PopupWindow支持方法如下: 支持所有popupWindow的子类

Track 方法 对应Aop 方法
popupWindowShow(xxxWindiw.class) popup.show()
dialogWindowDismiss(xxxWindiw.class) popup.dismiss()

其中 dialogWindowDismiss事件,只有当你主动调用 popup.dismiss() 时才会触发。原因同上

到目前为止,已经对一些常用控件,常见事件进行了监听,但有一些是我们在代码的逻辑操作,如接口回调,如数据的空与有,需要进行不同的操作,这时该怎么办呢?就算把所有的view事件添加进来,也肯定是有不全的,所以此时就需要一种万能事件:onMethodCall。

方法被调用时事件:onMethodCall

public Track<Object[]> onMethodCall(String methodName, Class... args)

方法参数说明 :方法名string,参数class,如有多个,填多个,无则不填。
这个操作符是什么意思呢,就是当某一个方法被调用时,进行回调。首先在方法上添加 @OnMethodCall 注解,(无论是你自定的方法,还是系统某接口回调的方法,只要你能在源码中添加这个注解就可以监听),监听的返回值Track<Object[]>就是方法被调时传进来的参数,如:

@OnMethodCall
private View setVisibili(TextView view) {
    if (view.getVisibility() == View.VISIBLE) {
        view.setVisibility(View.GONE);
    } else {
        view.setVisibility(View.VISIBLE);
    }
    return view;
}

监听:

Track.from(MainActivity.class).onMethodCall("setVisibili", TextView.class).subscribe(new OnSubscribe<Object[]>() {
    @Override
    public void call(Object[] objects) {
        Log.d(TAG, "MainActivity onMethodCall.setVisibili args:" + Arrays.toString(objects));
    }
});

注意:这里的 Track.from(MainActivity.class).onMethodCall("setVisibili", TextView.class) 不是说MainActivity 中的setVisibili 方法被调用,而是setVisibili 这个方法可以在任何地方被调用,那怕是SecondActivity中被调用。是一种和上下文无关的。如果需要精准对某类的某方法被调用时,用: Track.fromObject(MainActivity.class).onMethodCall("setVisibili", TextView.class).fromObject 表示MainActivity中,有一个方法setVisibili 被调用,等于这种情况 MainActivity.this.setVisibilimMainActivity.setVisibili,两种区别需要注意.

一次性事件订阅 disposable

Track.from(AActivity.class).to(BActivity.class).to(DActivity.class).viewClick(R.id.button)
    .disposable(new OnSubscribe<View>() {
        @Override
        public void call(View view) {
            Log.d(TAG, "A->B-D.click(button) =" + view);
        }
});

在 subscribe 中,当A->B->D后,多次点击R.id.button 时,回调是会多次触发的,而disposable 不同的时,当触发回调后,立即回到初始状态,当再点击button 时,是不会触发回调的,只有当A->B 这个起始事件重新发生时,路径才重新被点亮。适用于在终点事件不需要重复触发的场景.

过滤操作符 filter

filter 操作符对前一结点有效,当前一结点触发时,会去检查filter 条件,如果返回false,则还是不会点亮. 场景如:

Track.from(AActivity.class).fragmentOnHiddenChanged(MainFragment.class).filter(new IFilter<Fragment>() {
    @Override
    public boolean filter(Fragment fragment) {
        return !fragment.isHidden();
    }
});

监听方法

@Override
public void onHiddenChanged(boolean hidden) {
    super.onHiddenChanged(hidden);
}

在同一页面有多个fragment 时,onHiddenChanged 表示frg的是否可见,而可见与不可见都会触发,当你只需要可见时,可以加filter 过滤。

目前为止,所有的路径之间都是相互独立的,互不影响。但是有时候,路径间是有关系的

路径连接符 :join(Track trackB)

把路径B连接到当前路径的末尾,组成新的路径。

Track<Intent> tacker = Track.from(Activity.class).to(CActivity.class);
Track.fromObject(View.OnClickListener.class).onMethodCall("onClick", View.class)
        .subscribe(new OnSubscribe<Object[]>() {
            @Override
            public void call(Object[] objects) {
                Log.d(TAG, "OnClickListener.onClick :" + Arrays.toString(objects));
            }
        }).join(tacker).subscribe(new OnSubscribe<Intent>() {
            @Override
            public void call(Intent intent) {
                Log.d(TAG, ".onClick join A->C :" + intent);
            }
});

表示监听 onClickListener 对象中onClick 方法被触发后,然后再A->C事件。

路径互斥:mutexWith / mutex

mutexWith(Track... trakcB)
互斥是指两条或多条路径建立互斥关系后,当有一条完成全部点亮时,把其它互斥的路径置为默认状态。

场景如:同一Activity 中有多个fragment,多个frg 中有一个相同按钮id1,因为view和frg之间是无关系的(即通过view无法获取到frg)。view都属于Activity,如果需要知道是那个frg 中的id1点击,则需要互斥。
如需要知道是MainFragment 中的button6被点击:

Track<?> mainTrack = Track.from(TabFragmentActivity.class).fragmentSetUserVisibleHint(MainFragment.class).filter(frg -> frg.getUserVisibleHint())
    .mutexWith(Track.from(TabFragmentActivity.class).fragmentSetUserVisibleHint(MainFragment.class).filter(frg -> !frg.getUserVisibleHint()));

mainTrack.viewClick(R.id.button6)
        .subscribe(view -> Log.d(TAG, "main.click(6) :" + view));

mainTrack.viewClick(R.id.button8)
        .subscribe(view -> Log.d(TAG, "main.click(8) :" + view));

让MainFrgment 自己的可见与不可见互斥,这样当MainFrg 不可见时,路径被恢复至初始状态。其它frg 的button6 被点击,这样就不会触发mainTrack的回调。

整体使用上类似rxjava的操作习惯,具体使用见demo中Pointer.java。

Clone this wiki locally
You can’t perform that action at this time.