graph TB
采集策略-->|控制触发|采集
采集-->自动采集-->全埋点监听touch-->|onGlobalLayout|hookDecorViewClick-->|mOnTouchListener|HookTouchListener-->trackEvent
采集-->手动采集-->trackEvent
上报策略-->|控制触发|上报
/** com/analysys/process/HeatMap.java
* 反射给View注册监听
*/
private void hookViewClick(View view) throws Exception {
ans-sdk/analysys_core/src/main/java/com/analysys/AutomaticAcquisition.java
@Override
public void onActivityCreated(final Activity activity, Bundle savedInstanceState) {
AnalysysConfig config = AgentProcess.getInstance().getConfig();
if (config.isAutoTrackClick()) {
AnalysysUtil.onActivityCreated(activity);
}
if (config.isAutoHeatMap()) {
initHeatMap(new WeakReference<>(activity));
}
}
private void initHeatMap(final WeakReference<Activity> wa) {
layoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (wa == null) {
return;
}
final Activity activity = wa.get();
if (activity != null) {
activity.getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
try {
HeatMap.getInstance().hookDecorViewClick(activity.getWindow().getDecorView());
} catch (Throwable ignore) {
ExceptionUtil.exceptionThrow(ignore);
}
}
});
}
}
};
}
@Override
public void onActivityResumed(Activity activity) {
if (AgentProcess.getInstance().getConfig().isAutoHeatMap()) {
checkLayoutListener(new WeakReference<>(activity), true);
}
}
private void checkLayoutListener(WeakReference<Activity> wr, boolean isResume) {
if (wr != null) {
Activity activity = wr.get();
if (layoutListener != null) {
View rootView = activity.findViewById(android.R.id.content);
if (isResume) {
rootView.getViewTreeObserver().addOnGlobalLayoutListener(layoutListener);
} else {
if (Build.VERSION.SDK_INT > 15) {
rootView.getViewTreeObserver().removeOnGlobalLayoutListener(layoutListener);
}
}
}
}
}
ans-sdk/analysys_core/src/main/java/com/analysys/process/HeatMap.java
/***
* 递归调用解析view
* @param decorView 根节点view
*/
public void hookDecorViewClick(View decorView) throws Exception {
if (decorView instanceof ViewGroup) {
hookViewClick(decorView);
int count = ((ViewGroup) decorView).getChildCount();
for (int i = 0; i < count; i++) {
if (((ViewGroup) decorView).getChildAt(i) instanceof ViewGroup) {
hookDecorViewClick(((ViewGroup) decorView).getChildAt(i));
} else {
hookViewClick(((ViewGroup) decorView).getChildAt(i));
}
}
} else {
hookViewClick(decorView);
}
}
/**
* 反射给View注册监听
*/
private void hookViewClick(View view) throws Exception {
int visibility = view.getVisibility();
if (visibility == 4 || visibility == 8) {
return;
}
if (!view.getGlobalVisibleRect(new Rect())) {
return;
}
Class viewClass = Class.forName("android.view.View");
Method getListenerInfoMethod = viewClass.getDeclaredMethod("getListenerInfo");
if (!getListenerInfoMethod.isAccessible()) {
getListenerInfoMethod.setAccessible(true);
}
Object listenerInfoObject = getListenerInfoMethod.invoke(view);
Class mListenerInfoClass = Class.forName("android.view.View$ListenerInfo");
Field mOnClickListenerField = mListenerInfoClass.getDeclaredField("mOnTouchListener");
// Log.d("sanbo", view.hashCode() + "-----" + HeatMap.HookTouchListener.class.getName() +
// " <-------> " + mOnClickListenerField.getType().getName());
mOnClickListenerField.setAccessible(true);
Object touchListenerObj = mOnClickListenerField.get(listenerInfoObject);
if (!(touchListenerObj instanceof HookTouchListener)) {
// printLog(view, touchListenerObj);
HookTouchListener touchListenerProxy =
new HookTouchListener((View.OnTouchListener) touchListenerObj);
mOnClickListenerField.set(listenerInfoObject, touchListenerProxy);
}
}
private class HookTouchListener implements View.OnTouchListener {
private View.OnTouchListener onTouchListener;
private HookTouchListener(View.OnTouchListener onTouchListener) {
this.onTouchListener = onTouchListener;
}
@Override
public boolean onTouch(final View v, final MotionEvent event) {
try {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// 黑白名单判断
if (isTackHeatMap(v)) {
setCoordinate(v, event);
}
} catch (Throwable ignore) {
ExceptionUtil.exceptionThrow(ignore);
}
}
return onTouchListener.onTouch(v, event);
return false;
}
}
private void setCoordinate(final View v, final MotionEvent event) {
if (isTouch(rawX, rawY)) {
AThreadPool.asyncLowPriorityExecutor(new Runnable() {
@Override
public void run() {
final String path = PathGeneral.getInstance().general(v);
boolean isAddPath = setPath(path);
if (isAddPath) {
rx = rawX;
ry = rawY;
setClickCoordinate();
setClickContent(v);
clickInfo.putAll(pageInfo);
AgentProcess.getInstance().pageTouchInfo(clickInfo,currentTime);
}
}
});
}
}
com/analysys/allgro/plugin/ASMProbeHelp.java
public void trackViewOnClick(final View v, final boolean hasTrackClickAnn) {
try {
final long currentTime = System.currentTimeMillis();
AThreadPool.asyncLowPriorityExecutor(new Runnable() {
@Override
public void run() {
try {
if (isInLimitTime(v)) {
return;
}
for (ASMHookAdapter observer : mObservers) {
observer.trackViewOnClick(v, hasTrackClickAnn,currentTime);
}
} catch (Throwable ignore) {
ExceptionUtil.exceptionThrow(ignore);
}
}
});
}catch (Throwable ignore){
ExceptionUtil.exceptionThrow(ignore);
}
}
com/analysys/allgro/plugin/ViewClickProbe.java
@Override
public void trackViewOnClick(View v, boolean hasTrackClickAnn,long currentTime) {
try {
Map<String, Object> viewInfo = new HashMap<>();
String[] viewTypeAndText = AllegroUtils.getViewTypeAndText(v);
viewInfo.put(Constants.ELEMENT_TYPE, viewTypeAndText[0]);
viewInfo.put(Constants.ELEMENT_CONTENT, viewTypeAndText[1]);
String path = PathGeneral.getInstance().general(v);
viewInfo.put(Constants.ELEMENT_PATH,path);
String idName = AllegroUtils.getViewIdResourceName(v);
if (!TextUtils.isEmpty(idName)) {
viewInfo.put(Constants.ELEMENT_ID, idName);
}
autoTrackClick(pageObj, viewInfo, hasTrackClickAnn,currentTime);
} catch (Throwable ignore) {
ExceptionUtil.exceptionThrow(ignore);
}
}
private void autoTrackClick(Object pageObj, Map<String, Object> elementInfo, boolean hasTrackClickAnn,long currentTime) throws Throwable {
if (pageObj != null) {
// 获取页面相关信息
elementInfo.putAll(AllegroUtils.getPageInfo(pageObj,true));
// if(elementInfo!=null&&elementInfo.containsKey(Constants.PARENT_URL)){
// 去除此字段
// elementInfo.remove(Constants.PARENT_URL);
// }
}
AgentProcess.getInstance().autoTrackViewClick(elementInfo,currentTime);
}
com/analysys/process/AgentProcess.java
/**
* Touch事件处理
*/
void pageTouchInfo(final Map<String, Object> screenDetail,final long currentTime) {
try {
Context context = AnalysysUtil.getContext();
Map<String, Object> screenInfo = CommonUtils.deepCopy(screenDetail);
if (context != null) {
JSONObject eventData = DataAssemble.getInstance(context).getEventData(
currentTime,Constants.API_APP_CLICK, Constants.APP_CLICK, null, screenInfo);
trackEvent(context, Constants.API_APP_CLICK, Constants.APP_CLICK, eventData);
}
} catch (Throwable ignore) {
ExceptionUtil.exceptionThrow(ignore);
}
}
/**
* 点击自动上报
*
* @param clickInfo 点击上报信息
*/
public void autoTrackViewClick(final Map<String, Object> clickInfo,final long currentTime) throws Throwable {
Context context = AnalysysUtil.getContext();
if (context != null) {
JSONObject eventData = DataAssemble.getInstance(context).getEventData(currentTime,Constants.API_USER_CLICK, Constants.USER_CLICK, null, clickInfo);
trackEvent(context, Constants.API_USER_CLICK, Constants.USER_CLICK, eventData);
}
}
/**
* 接口数据处理
*/
private void trackEvent(Context context, String apiName,
String eventName, JSONObject eventData) {
if (!CommonUtils.isEmpty(eventName) && checkoutEvent(eventData)) {
// 此处重置重传传次数,解决处于重传状态时,触发新事件重传次数不够三次
//SharedUtil.remove(mContext, Constants.SP_FAILURE_COUNT);
if (LogBean.getCode() == Constants.CODE_SUCCESS) {
LogPrompt.showLog(apiName, true);
}
EasytouchProcess.getInstance().setEventMessage(eventData.toString());
if (mEventObserver != null) {
mEventObserver.onEvent(eventName, eventData);
}
UploadManager.getInstance(context).sendManager(eventName, eventData);
}
}
com/analysys/network/UploadManager.java
/**
* 判断 发送数据
*/
public void sendManager(String type, JSONObject sendData) {
if (CommonUtils.isEmpty(sendData)) {
return;
}
dbCacheCheck();
TableAllInfo.getInstance(mContext).insert(sendData.toString(), type);
if (CommonUtils.isMainProcess(mContext)) {
BaseSendStatus sendStatus = PolicyManager.getPolicyType(mContext);
if (sendStatus.isSend(mContext)) {
sendUploadMessage();
}
} else {
LogPrompt.processFailed();
}
}
/**
* 发送实时消息
*/
private void sendUploadMessage() {
if (mHandler.hasMessages(delayUploadData)) {
mHandler.removeMessages(uploadData);
}
Message msg = Message.obtain();
msg.what = uploadData;
mHandler.sendMessage(msg);
}
/**
* 处理数据压缩,上传和返回值解析
*/
private class SendHandler extends Handler {
private SendHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
try {
String url = CommonUtils.getUrl(mContext);
if (!CommonUtils.isEmpty(url)) {
int what = msg.what;
if (what == uploadData || what == delayUploadData) {
uploadData(url);
} else if (what == updateTime) {
calibrationTime(url);
}
} else {
LogPrompt.showErrLog(LogPrompt.URL_ERR);
}
} catch (Throwable ignore) {
ExceptionUtil.exceptionThrow(ignore);
}
}
}
/**
* 数据上传
*/
private void uploadData(String url) throws IOException, JSONException {
if (NetworkUtils.isNetworkAvailable(mContext)) {
JSONArray eventArray = TableAllInfo.getInstance(mContext).select();
// 上传数据检查校验
eventArray = checkUploadData(eventArray);
if (!CommonUtils.isEmpty(eventArray)) {
LogPrompt.showSendMessage(url, eventArray);
encryptData(url, String.valueOf(eventArray));
} else {
TableAllInfo.getInstance(mContext).deleteData();
}
} else {
LogPrompt.networkErr();
}
}
/**
* 数据加密
*/
private void encryptData(String url, String value) throws IOException {
if (CommonUtils.isEmpty(spv)) {
spv = CommonUtils.getSpvInfo(mContext);
}
Map<String, String> headInfo = null;
String encryptData;
if (Constants.encryptType != 0) {
encryptData = encrypt(value, Constants.encryptType);
if (!TextUtils.isEmpty(encryptData)) {
headInfo = getHeadInfo();
if (!CommonUtils.isEmpty(headInfo)) {
LogPrompt.encryptLog(true);
} else {
encryptData = value;
}
} else {
encryptData = value;
}
} else {
encryptData = value;
}
String zipData = CommonUtils.messageZip(encryptData);
sendRequest(url, zipData, headInfo);
}
/**
* 发送数据
*/
private void sendRequest(String url, String dataInfo, Map<String, String> headInfo) {
try {
String returnInfo;
if (url.startsWith(Constants.HTTP)) {
returnInfo = RequestUtils.postRequest(url, dataInfo, spv, headInfo);
} else {
returnInfo = RequestUtils.postRequestHttps(mContext, url, dataInfo, spv, headInfo);
}
policyAnalysys(analysysStrategy(returnInfo));
} catch (Throwable ignore) {
ExceptionUtil.exceptionThrow(ignore);
}
}
https://docs.analysys.cn/integration/sdk/android/andoird-quick