1、页面跳转路径信息采集
getApplication().registerActivityLifecycleCallbacks(new SampleActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
super.onActivityCreated(activity, savedInstanceState);
AppActivityManager.getAppManager().addActivity(activity);
if (activity instanceof BaseActivity) {
BaseActivity baseActivity = (BaseActivity) activity;
baseActivity.builder.page_loading_time(System.currentTimeMillis())
.page_path(activity.getClass().getName())
.page_title_name(baseActivity.mQMUITopBar.getTitle().toString());
}
}
@Override
public void onActivityDestroyed(Activity activity) {
super.onActivityDestroyed(activity);
AppActivityManager.getAppManager().removeActivity(activity);
if (activity instanceof BaseActivity) {
BaseActivity baseActivity = (BaseActivity) activity;
baseActivity.builder.page_finish_time(System.currentTimeMillis());
EventHelper.addEvent(baseActivity.builder.build());
}
}
});
1
getApplication().registerActivityLifecycleCallbacks(new SampleActivityLifecycleCallbacks() {
2
3
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
4
super.onActivityCreated(activity, savedInstanceState);
5
AppActivityManager.getAppManager().addActivity(activity);
6
if (activity instanceof BaseActivity) {
7
BaseActivity baseActivity = (BaseActivity) activity;
8
baseActivity.builder.page_loading_time(System.currentTimeMillis())
9
.page_path(activity.getClass().getName())
10
.page_title_name(baseActivity.mQMUITopBar.getTitle().toString());
11
}
12
}
13
14
15
public void onActivityDestroyed(Activity activity) {
16
super.onActivityDestroyed(activity);
17
AppActivityManager.getAppManager().removeActivity(activity);
18
if (activity instanceof BaseActivity) {
19
BaseActivity baseActivity = (BaseActivity) activity;
20
baseActivity.builder.page_finish_time(System.currentTimeMillis());
21
EventHelper.addEvent(baseActivity.builder.build());
22
}
23
}
24
});
2、应用进入前后台采集
和上面类似
getApplication().registerActivityLifecycleCallbacks(new SampleActivityLifecycleCallbacks() {
private int mFinalCount;
private PageBackFrontEventBean.Builder builder;
@Override
public void onActivityStarted(Activity activity) {
super.onActivityStarted(activity);
mFinalCount++;//如果mFinalCount ==1,说明是从后台到前台
if (mFinalCount == 1){
builder = new PageBackFrontEventBean.Builder()
.page_front_time(System.currentTimeMillis())
.page_sessionid("page_sessionid" + System.currentTimeMillis())
.page_path(activity.getClass().getName());
}
}
@Override
public void onActivityStopped(Activity activity) {
super.onActivityStopped(activity);
mFinalCount--;
if (mFinalCount == 0 && builder != null){//如果mFinalCount ==0,说明是前台到后台
builder.page_back_time(System.currentTimeMillis());
EventHelper.addEvent(builder.build());
}
}
}
1
getApplication().registerActivityLifecycleCallbacks(new SampleActivityLifecycleCallbacks() {
2
private int mFinalCount;
3
private PageBackFrontEventBean.Builder builder;
4
5
6
public void onActivityStarted(Activity activity) {
7
super.onActivityStarted(activity);
8
mFinalCount++;//如果mFinalCount ==1,说明是从后台到前台
9
if (mFinalCount == 1){
10
builder = new PageBackFrontEventBean.Builder()
11
.page_front_time(System.currentTimeMillis())
12
.page_sessionid("page_sessionid" + System.currentTimeMillis())
13
.page_path(activity.getClass().getName());
14
}
15
}
16
17
18
public void onActivityStopped(Activity activity) {
19
super.onActivityStopped(activity);
20
mFinalCount--;
21
if (mFinalCount == 0 && builder != null){//如果mFinalCount ==0,说明是前台到后台
22
builder.page_back_time(System.currentTimeMillis());
23
EventHelper.addEvent(builder.build());
24
}
25
}
26
}
3、应用接口请求采集
CherryCodeAPIManager.setNetReqEventCallback(new NetReqEventCallback() {
private SparseLongArray requestTimeArray = new SparseLongArray();
@Override
public void onReqNetStart(String url, String params) {
CherryCodeLogUtil.i("bqt", "【onReqNetStart】" + (CherryServiceConfig.getCherryIp() + url) + " " + params);
if (requestTimeArray.get((url + params).hashCode()) != 0L) {
requestTimeArray.put((url + params).hashCode(), System.currentTimeMillis());
}
}
@Override
public void onReqNetSucess(String url, String params, String response) {
CherryCodeLogUtil.i("bqt", "【onReqNetSucess】"
+ (CherryServiceConfig.getCherryIp() + url) + " " + params + " " + response);
if (requestTimeArray.get((url + params).hashCode()) != 0L) {
RequestEventBean bean = new RequestEventBean.Builder()
.request_id("request_id" + System.currentTimeMillis())
.request_start_time(requestTimeArray.get((url + params).hashCode()))
.request_end_time(System.currentTimeMillis())
.request_url(CherryServiceConfig.getCherryIp() + url)
.request_params(params)
.request_resoponse_data("")
.request_status("0")/*请求的返回状态(0成功,1失败)*/
.build();
EventHelper.addEvent(bean);
requestTimeArray.delete((url + params).hashCode());
}
}
@Override
public void onReqNetFail(String url, String params, String response) {
CherryCodeLogUtil.i("bqt", "【onReqNetFail】"
+ (CherryServiceConfig.getCherryIp() + url) + " " + params + " " + response);
if (requestTimeArray.get((url + params).hashCode()) != 0L) {
RequestEventBean bean = new RequestEventBean.Builder()
.request_id("request_id" + System.currentTimeMillis())
.request_start_time(requestTimeArray.get((url + params).hashCode()))
.request_end_time(System.currentTimeMillis())
.request_url(CherryServiceConfig.getCherryIp() + url)
.request_params(params)
.request_resoponse_data("")
.request_errorinfo(response)
.request_status("1")/*请求的返回状态(0成功,1失败)*/
.build();
EventHelper.addEvent(bean);
requestTimeArray.delete((url + params).hashCode());
}
}
});
1
CherryCodeAPIManager.setNetReqEventCallback(new NetReqEventCallback() {
2
private SparseLongArray requestTimeArray = new SparseLongArray();
3
4
5
public void onReqNetStart(String url, String params) {
6
CherryCodeLogUtil.i("bqt", "【onReqNetStart】" + (CherryServiceConfig.getCherryIp() + url) + " " + params);
7
if (requestTimeArray.get((url + params).hashCode()) != 0L) {
8
requestTimeArray.put((url + params).hashCode(), System.currentTimeMillis());
9
}
10
}
11
12
13
public void onReqNetSucess(String url, String params, String response) {
14
CherryCodeLogUtil.i("bqt", "【onReqNetSucess】"
15
+ (CherryServiceConfig.getCherryIp() + url) + " " + params + " " + response);
16
if (requestTimeArray.get((url + params).hashCode()) != 0L) {
17
RequestEventBean bean = new RequestEventBean.Builder()
18
.request_id("request_id" + System.currentTimeMillis())
19
.request_start_time(requestTimeArray.get((url + params).hashCode()))
20
.request_end_time(System.currentTimeMillis())
21
.request_url(CherryServiceConfig.getCherryIp() + url)
22
.request_params(params)
23
.request_resoponse_data("")
24
.request_status("0")/*请求的返回状态(0成功,1失败)*/
25
.build();
26
EventHelper.addEvent(bean);
27
requestTimeArray.delete((url + params).hashCode());
28
}
29
}
30
31
32
public void onReqNetFail(String url, String params, String response) {
33
CherryCodeLogUtil.i("bqt", "【onReqNetFail】"
34
+ (CherryServiceConfig.getCherryIp() + url) + " " + params + " " + response);
35
if (requestTimeArray.get((url + params).hashCode()) != 0L) {
36
RequestEventBean bean = new RequestEventBean.Builder()
37
.request_id("request_id" + System.currentTimeMillis())
38
.request_start_time(requestTimeArray.get((url + params).hashCode()))
39
.request_end_time(System.currentTimeMillis())
40
.request_url(CherryServiceConfig.getCherryIp() + url)
41
.request_params(params)
42
.request_resoponse_data("")
43
.request_errorinfo(response)
44
.request_status("1")/*请求的返回状态(0成功,1失败)*/
45
.build();
46
EventHelper.addEvent(bean);
47
requestTimeArray.delete((url + params).hashCode());
48
}
49
}
50
});
4、app异常采集
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
ExceptionEventBean bean=new ExceptionEventBean.Builder()
.abnormal_time(System.currentTimeMillis())
.abnormal_id("abnormal_id"+System.currentTimeMillis())
.abnormal_conrtent(e.getMessage())
.abnormal_thread( t.getName())
.abnormal_type(e.getClass().getSimpleName())
.build();
EventHelper.addEvent(bean);
});
1
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
2
ExceptionEventBean bean=new ExceptionEventBean.Builder()
3
.abnormal_time(System.currentTimeMillis())
4
.abnormal_id("abnormal_id"+System.currentTimeMillis())
5
.abnormal_conrtent(e.getMessage())
6
.abnormal_thread( t.getName())
7
.abnormal_type(e.getClass().getSimpleName())
8
.build();
9
EventHelper.addEvent(bean);
10
});
5、点击事件采集
**
* 拦截所有View或其子类的【onClick方法】以及通过ButterKnife的注解添加的点击事件
*/
@Aspect
public class OnClickAspect {
@Pointcut("execution(* android.view.View.On*Listener.on*Click(..)) ")//定义匹配范围:执行指定方法时拦截
public void onClick() {//匹配View.OnClickListener中的onClick方法和View.OnLongClickListener中的OnLongClickListener方法
}
@Pointcut("execution(* *.on*ItemClick(..)) ")//如果有多个匹配范围,可以定义多个,多个规则之间通过 || 或 && 控制
public void onItemClick() {//匹配任意名字以on开头以ItemClick结尾的方法
}
@Pointcut("execution(@butterknife.OnClick * *(..))")//匹配通过butterknife的OnClick注解添加的点击事件
public void butterKnifeOnClick() {
}
@Around("onClick() || onItemClick() || butterKnifeOnClick()")//@Around 拦截方法,这个注解可以同时拦截方法的执行前后
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
long beginTime = SystemClock.currentThreadTimeMillis();
printJoinPointInfo(joinPoint);
if (joinPoint.getSignature() instanceof MethodSignature) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//要根据Pointcut匹配的类型强转
printMethodSignatureInfo(signature);
printArgs(joinPoint);
printParameterInfo(joinPoint);
}
Object result = joinPoint.proceed();
Log.i("bqt", "【@Around】返回值=" + ObjToStringUtils.toString(result)// null
+ " 方法执行耗时=" + (SystemClock.currentThreadTimeMillis() - beginTime));//2
return result;
}
//必须是静态方法
private static void printJoinPointInfo(ProceedingJoinPoint joinPoint) {
Log.i("bqt", "【@Around】MethodSignature"
+ "\nKind=" + joinPoint.getKind()//method-execution
+ "\nArgs=" + ObjToStringUtils.toString(joinPoint.getArgs())//[android.widget.TextView{d090a1d V.ED..C.. ...P.... 0,0-1440,210}]
+ "\nSignature=" + ObjToStringUtils.toString(joinPoint.getSignature())//void com.bqt.aop.MainActivity.1.onClick(View)
+ "\nSourceLocation=" + ObjToStringUtils.toString(joinPoint.getSourceLocation())//MainActivity.java:25
+ "\nStaticPart=" + ObjToStringUtils.toString(joinPoint.getStaticPart())//execution(void com.bqt.aop.MainActivity.1.onClick(View))
+ "\nTarget=" + ObjToStringUtils.toString(joinPoint.getTarget())//com.bqt.aop.MainActivity$1@d5c5492
+ "\nThis=" + ObjToStringUtils.toString(joinPoint.getThis()));//com.bqt.aop.MainActivity$1@d5c5492
}
private static void printMethodSignatureInfo(MethodSignature signature) {
//下面通过MethodSignature的方式获取方法的详细信息,也基本都可以通过Method对象获取
Log.i("bqt", "【@Around】MethodSignature"
+ "\n方法=" + ObjToStringUtils.toString(signature.getMethod())// public void com.bqt.aop.MainActivity$1.onClick(android.view.View)
+ "\n方法名=" + signature.getName()// onClick
+ "\n返回值类型=" + ObjToStringUtils.toString(signature.getReturnType())// void
+ "\n声明类型=" + ObjToStringUtils.toString(signature.getDeclaringType())// class com.bqt.aop.MainActivity$1
+ "\n声明类型名=" + signature.getDeclaringTypeName()// com.bqt.aop.MainActivity$1
+ "\n异常类型=" + ObjToStringUtils.toString(signature.getExceptionTypes())// []
+ "\n修饰符=" + signature.getModifiers()// 1,对应为 public static final int PUBLIC = 0x00000001
+ "\n参数名=" + ObjToStringUtils.toString(signature.getParameterNames())// ["v"]
+ "\n参数类型=" + ObjToStringUtils.toString(signature.getParameterTypes()));// [class android.view.View]
}
private static void printArgs(ProceedingJoinPoint joinPoint) {
String[] parameterNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();//获取参数名列表
Object[] parameterValues = joinPoint.getArgs();//获取参数值列表
StringBuilder builder = new StringBuilder("");
for (int i = 0; i < parameterValues.length; i++) {
builder.append("\n")
.append(parameterNames[i])
.append("=")//拼接参数名
.append(ObjToStringUtils.toString(parameterValues[i]));//拼接参数值
}
Log.i("bqt", "【@Around】参数列表" + builder.toString());//v=android.widget.TextView{d090a1d V.ED..C.. ...P.... 0,0-1440,210}
}
private static void printParameterInfo(ProceedingJoinPoint joinPoint) {
Object[] parameterValues = joinPoint.getArgs();//获取参数值列表
for (Object obj : parameterValues) {
if (obj instanceof TextView) {
TextView textView = (TextView) obj;
Log.i("bqt", "【@Around】TextView的信息"
+ " 文字=" + textView.getText()
+ " 所属界面=" + textView.getContext().getClass().getSimpleName()
+ " ID=" + textView.getId()
+ " 父页面名称=" + textView.getParent().getClass().getSimpleName()
);
}
}
}
}
1
**
2
* 拦截所有View或其子类的【onClick方法】以及通过ButterKnife的注解添加的点击事件
3
*/
4
5
public class OnClickAspect {
6
("execution(* android.view.View.On*Listener.on*Click(..)) ")//定义匹配范围:执行指定方法时拦截
7
public void onClick() {//匹配View.OnClickListener中的onClick方法和View.OnLongClickListener中的OnLongClickListener方法
8
}
9
10
("execution(* *.on*ItemClick(..)) ")//如果有多个匹配范围,可以定义多个,多个规则之间通过 || 或 && 控制
11
public void onItemClick() {//匹配任意名字以on开头以ItemClick结尾的方法
12
}
13
14
("execution(@butterknife.OnClick * *(..))")//匹配通过butterknife的OnClick注解添加的点击事件
15
public void butterKnifeOnClick() {
16
}
17
18
("onClick() || onItemClick() || butterKnifeOnClick()")//@Around 拦截方法,这个注解可以同时拦截方法的执行前后
19
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
20
long beginTime = SystemClock.currentThreadTimeMillis();
21
printJoinPointInfo(joinPoint);
22
23
if (joinPoint.getSignature() instanceof MethodSignature) {
24
MethodSignature signature = (MethodSignature) joinPoint.getSignature();//要根据Pointcut匹配的类型强转
25
printMethodSignatureInfo(signature);
26
printArgs(joinPoint);
27
printParameterInfo(joinPoint);
28
}
29
30
Object result = joinPoint.proceed();
31
Log.i("bqt", "【@Around】返回值=" + ObjToStringUtils.toString(result)// null
32
+ " 方法执行耗时=" + (SystemClock.currentThreadTimeMillis() - beginTime));//2
33
return result;
34
}
35
36
//必须是静态方法
37
private static void printJoinPointInfo(ProceedingJoinPoint joinPoint) {
38
Log.i("bqt", "【@Around】MethodSignature"
39
+ "\nKind=" + joinPoint.getKind()//method-execution
40
+ "\nArgs=" + ObjToStringUtils.toString(joinPoint.getArgs())//[android.widget.TextView{d090a1d V.ED..C.. ...P.... 0,0-1440,210}]
41
+ "\nSignature=" + ObjToStringUtils.toString(joinPoint.getSignature())//void com.bqt.aop.MainActivity.1.onClick(View)
42
+ "\nSourceLocation=" + ObjToStringUtils.toString(joinPoint.getSourceLocation())//MainActivity.java:25
43
+ "\nStaticPart=" + ObjToStringUtils.toString(joinPoint.getStaticPart())//execution(void com.bqt.aop.MainActivity.1.onClick(View))
44
+ "\nTarget=" + ObjToStringUtils.toString(joinPoint.getTarget())//com.bqt.aop.MainActivity$1@d5c5492
45
+ "\nThis=" + ObjToStringUtils.toString(joinPoint.getThis()));//com.bqt.aop.MainActivity$1@d5c5492
46
}
47
48
private static void printMethodSignatureInfo(MethodSignature signature) {
49
//下面通过MethodSignature的方式获取方法的详细信息,也基本都可以通过Method对象获取
50
Log.i("bqt", "【@Around】MethodSignature"
51
+ "\n方法=" + ObjToStringUtils.toString(signature.getMethod())// public void com.bqt.aop.MainActivity$1.onClick(android.view.View)
52
+ "\n方法名=" + signature.getName()// onClick
53
+ "\n返回值类型=" + ObjToStringUtils.toString(signature.getReturnType())// void
54
+ "\n声明类型=" + ObjToStringUtils.toString(signature.getDeclaringType())// class com.bqt.aop.MainActivity$1
55
+ "\n声明类型名=" + signature.getDeclaringTypeName()// com.bqt.aop.MainActivity$1
56
+ "\n异常类型=" + ObjToStringUtils.toString(signature.getExceptionTypes())// []
57
+ "\n修饰符=" + signature.getModifiers()// 1,对应为 public static final int PUBLIC = 0x00000001
58
+ "\n参数名=" + ObjToStringUtils.toString(signature.getParameterNames())// ["v"]
59
+ "\n参数类型=" + ObjToStringUtils.toString(signature.getParameterTypes()));// [class android.view.View]
60
}
61
62
private static void printArgs(ProceedingJoinPoint joinPoint) {
63
String[] parameterNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();//获取参数名列表
64
Object[] parameterValues = joinPoint.getArgs();//获取参数值列表
65
66
StringBuilder builder = new StringBuilder("");
67
for (int i = 0; i < parameterValues.length; i++) {
68
builder.append("\n")
69
.append(parameterNames[i])
70
.append("=")//拼接参数名
71
.append(ObjToStringUtils.toString(parameterValues[i]));//拼接参数值
72
}
73
Log.i("bqt", "【@Around】参数列表" + builder.toString());//v=android.widget.TextView{d090a1d V.ED..C.. ...P.... 0,0-1440,210}
74
}
75
76
private static void printParameterInfo(ProceedingJoinPoint joinPoint) {
77
Object[] parameterValues = joinPoint.getArgs();//获取参数值列表
78
for (Object obj : parameterValues) {
79
if (obj instanceof TextView) {
80
TextView textView = (TextView) obj;
81
Log.i("bqt", "【@Around】TextView的信息"
82
+ " 文字=" + textView.getText()
83
+ " 所属界面=" + textView.getContext().getClass().getSimpleName()
84
+ " ID=" + textView.getId()
85
+ " 父页面名称=" + textView.getParent().getClass().getSimpleName()
86
);
87
}
88
}
89
}
90
}
6、网页加载事件采集
两种方式
/**
* 拦截所有的【onPageStarted方法】
*/
@Aspect
public class WebViewAspect {
@Pointcut("execution(* *.onPageStarted(..)) ")//定义匹配范围:执行指定方法时拦截
public void onPageStarted() {
}
@Around("onPageStarted()")//@Around 拦截方法,这个注解可以同时拦截方法的执行前后
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
CherryCodeLogUtil.i("bqt", "【网页加载事件】" + joinPoint.getSignature().getClass().getSimpleName());
if (joinPoint.getSignature() instanceof MethodSignature) {
addEvent(joinPoint);
}
return joinPoint.proceed();
}
private static void addEvent(ProceedingJoinPoint joinPoint) {
Object[] parameterValues = joinPoint.getArgs();//获取参数值列表
for (Object obj : parameterValues) {
if (obj instanceof String) {//Button是TextView的子类
WebViewEventBean eventBean = new WebViewEventBean.Builder()
.page_sessionid("page_sessionid" + System.currentTimeMillis())
.webview_time(System.currentTimeMillis())
.webview_url(obj.toString())
.build();
EventHelper.addEvent(eventBean);
}
}
}
}
32
1
/**
2
* 拦截所有的【onPageStarted方法】
3
*/
4
5
public class WebViewAspect {
6
("execution(* *.onPageStarted(..)) ")//定义匹配范围:执行指定方法时拦截
7
public void onPageStarted() {
8
}
9
10
("onPageStarted()")//@Around 拦截方法,这个注解可以同时拦截方法的执行前后
11
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
12
CherryCodeLogUtil.i("bqt", "【网页加载事件】" + joinPoint.getSignature().getClass().getSimpleName());
13
if (joinPoint.getSignature() instanceof MethodSignature) {
14
addEvent(joinPoint);
15
}
16
return joinPoint.proceed();
17
}
18
19
private static void addEvent(ProceedingJoinPoint joinPoint) {
20
Object[] parameterValues = joinPoint.getArgs();//获取参数值列表
21
for (Object obj : parameterValues) {
22
if (obj instanceof String) {//Button是TextView的子类
23
WebViewEventBean eventBean = new WebViewEventBean.Builder()
24
.page_sessionid("page_sessionid" + System.currentTimeMillis())
25
.webview_time(System.currentTimeMillis())
26
.webview_url(obj.toString())
27
.build();
28
EventHelper.addEvent(eventBean);
29
}
30
}
31
}
32
}
mWebView.setWebViewClient(new WebViewClient() {
private WebViewEventBean.Builder builder;
private long startTime;
private boolean finished=false;
@Override
public void onPageStarted(WebView webView, String url, Bitmap bitmap) {
super.onPageStarted(webView, url, bitmap);
startTime = System.currentTimeMillis();
finished=false;
builder = new WebViewEventBean.Builder()
.page_sessionid(page_sessionid)
.webview_url(url);
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (!finished) {
finished = true;
builder.webview_time(System.currentTimeMillis() - startTime)
.webview_loading_result("0");//加载网页结果(0成功,1失败)
}
}
@Override
public void onReceivedError(WebView webView, WebResourceRequest webResourceRequest, WebResourceError webResourceError) {
super.onReceivedError(webView, webResourceRequest, webResourceError);
if (!finished) {
finished = true;
builder.webview_time(System.currentTimeMillis() - startTime)
.webview_loading_result("1");//加载网页结果(0成功,1失败)
}
}
@Override
public void onReceivedHttpError(WebView webView, WebResourceRequest webResourceRequest, WebResourceResponse webResourceResponse) {
super.onReceivedHttpError(webView, webResourceRequest, webResourceResponse);
if (!finished) {
finished = true;
builder.webview_time(System.currentTimeMillis() - startTime)
.webview_loading_result("1");//加载网页结果(0成功,1失败)
}
}
@Override
public void onReceivedSslError(WebView webView, SslErrorHandler sslErrorHandler, SslError sslError) {
super.onReceivedSslError(webView, sslErrorHandler, sslError);
if (!finished) {
finished = true;
builder.webview_time(System.currentTimeMillis() - startTime)
.webview_loading_result("1");//加载网页结果(0成功,1失败)
}
}
});
56
56
1
mWebView.setWebViewClient(new WebViewClient() {
2
private WebViewEventBean.Builder builder;
3
private long startTime;
4
private boolean finished=false;
5
6
7
public void onPageStarted(WebView webView, String url, Bitmap bitmap) {
8
super.onPageStarted(webView, url, bitmap);
9
startTime = System.currentTimeMillis();
10
finished=false;
11
12
builder = new WebViewEventBean.Builder()
13
.page_sessionid(page_sessionid)
14
.webview_url(url);
15
}
16
17
18
public void onPageFinished(WebView view, String url) {
19
super.onPageFinished(view, url);
20
if (!finished) {
21
finished = true;
22
builder.webview_time(System.currentTimeMillis() - startTime)
23
.webview_loading_result("0");//加载网页结果(0成功,1失败)
24
}
25
}
26
27
28
public void onReceivedError(WebView webView, WebResourceRequest webResourceRequest, WebResourceError webResourceError) {
29
super.onReceivedError(webView, webResourceRequest, webResourceError);
30
if (!finished) {
31
finished = true;
32
builder.webview_time(System.currentTimeMillis() - startTime)
33
.webview_loading_result("1");//加载网页结果(0成功,1失败)
34
}
35
}
36
37
38
public void onReceivedHttpError(WebView webView, WebResourceRequest webResourceRequest, WebResourceResponse webResourceResponse) {
39
super.onReceivedHttpError(webView, webResourceRequest, webResourceResponse);
40
if (!finished) {
41
finished = true;
42
builder.webview_time(System.currentTimeMillis() - startTime)
43
.webview_loading_result("1");//加载网页结果(0成功,1失败)
44
}
45
}
46
47
48
public void onReceivedSslError(WebView webView, SslErrorHandler sslErrorHandler, SslError sslError) {
49
super.onReceivedSslError(webView, sslErrorHandler, sslError);
50
if (!finished) {
51
finished = true;
52
builder.webview_time(System.currentTimeMillis() - startTime)
53
.webview_loading_result("1");//加载网页结果(0成功,1失败)
54
}
55
}
56
});
部分设计细节
DevicesInfoHelper
public class DevicesInfoHelper {
private static DevicesInfoHelper instance = new DevicesInfoHelper();
private DevicesInfoHelper() {//构造方法私有
}
public static DevicesInfoHelper getInstance() {
return instance;
}
public String device_id = CherryCodeDeviceManager.getInstance().imei();//设备唯一ID(Android取IMEI,ios取UUID)
public String device_system_version = CherryCodeDeviceManager.getInstance().systemVersion();//操作系统版本(iOS8/Android6.0)
public String device_brand = DeviceUtils.getManufacturer();//设备品牌(华为/iPhone)
public String device_type = DeviceUtils.getModel();//设备型号(huaweimate8/iphone 6s)
public String device_root = AppUtils.isAppRoot() + "";//设备是否root或越狱
public String app_version = AppUtils.getAppVersionName();//当前APP的版本信息
public String device_resolution = CherryCodeDeviceManager.getInstance().getScreenWidth() + " * " + CherryCodeDeviceManager.getInstance().getScreenHeight();//设备分辨率
}
x
1
public class DevicesInfoHelper {
2
private static DevicesInfoHelper instance = new DevicesInfoHelper();
3
4
private DevicesInfoHelper() {//构造方法私有
5
}
6
7
public static DevicesInfoHelper getInstance() {
8
return instance;
9
}
10
11
public String device_id = CherryCodeDeviceManager.getInstance().imei();//设备唯一ID(Android取IMEI,ios取UUID)
12
public String device_system_version = CherryCodeDeviceManager.getInstance().systemVersion();//操作系统版本(iOS8/Android6.0)
13
public String device_brand = DeviceUtils.getManufacturer();//设备品牌(华为/iPhone)
14
public String device_type = DeviceUtils.getModel();//设备型号(huaweimate8/iphone 6s)
15
public String device_root = AppUtils.isAppRoot() + "";//设备是否root或越狱
16
public String app_version = AppUtils.getAppVersionName();//当前APP的版本信息
17
public String device_resolution = CherryCodeDeviceManager.getInstance().getScreenWidth() + " * " + CherryCodeDeviceManager.getInstance().getScreenHeight();//设备分辨率
18
}
BaseEventBean
/**
* Desc:每个事件中都将会包含这些字段
*
* @author <a href="http://www.cnblogs.com/baiqiantao">白乾涛</a><p>
* @tag 通用信息<p>
* @date 2018/4/26 11:49 <p>
*/
public class BaseEventBean {
public int type = EventEnum.BaseEventBean.value;
public String modelType = "";
public long time = System.currentTimeMillis();
public String visit_sessionid = "visit_sessionid" + System.currentTimeMillis();//会话ID(启动app随机生成的ID,应用于整个app生命周期)
public String device_id = DevicesInfoHelper.getInstance().device_id;//设备唯一ID(Android取IMEI,ios取UUID)
public String device_system_version = DevicesInfoHelper.getInstance().device_system_version;//操作系统版本(iOS8/Android6.0)
public String device_brand = DevicesInfoHelper.getInstance().device_brand;//设备品牌(华为/iPhone)
public String device_type = DevicesInfoHelper.getInstance().device_type;//设备型号(huaweimate8/iphone 6s)
public String device_root = DevicesInfoHelper.getInstance().device_root;//设备是否root或越狱
public String app_version = DevicesInfoHelper.getInstance().app_version;//当前APP的版本信息
public String device_resolution = DevicesInfoHelper.getInstance().device_resolution;//设备分辨率
public String network_operator = NetworkUtils.getNetworkOperatorName();//网络运营商(中国移动/中国电信)
public String network_version;//当前网络类型(WIFI,4G,3G)
public BaseEventBean() {
NetworkUtils.NetworkType type = NetworkUtils.getNetworkType();
if (type == NetworkUtils.NetworkType.NETWORK_2G) {
network_version = "2G";
} else if (type == NetworkUtils.NetworkType.NETWORK_3G) {
network_version = "3G";
} else if (type == NetworkUtils.NetworkType.NETWORK_4G) {
network_version = "4G";
} else if (type == NetworkUtils.NetworkType.NETWORK_WIFI) {
network_version = "WIFI";
} else if (type == NetworkUtils.NetworkType.NETWORK_NO) {
network_version = "无网络";
} else {
network_version = "网络未知";
}
}
}
42
42
1
/**
2
* Desc:每个事件中都将会包含这些字段
3
*
4
* @author <a href="http://www.cnblogs.com/baiqiantao">白乾涛</a><p>
5
* @tag 通用信息<p>
6
* @date 2018/4/26 11:49 <p>
7
*/
8
public class BaseEventBean {
9
10
public int type = EventEnum.BaseEventBean.value;
11
public String modelType = "";
12
public long time = System.currentTimeMillis();
13
public String visit_sessionid = "visit_sessionid" + System.currentTimeMillis();//会话ID(启动app随机生成的ID,应用于整个app生命周期)
14
15
public String device_id = DevicesInfoHelper.getInstance().device_id;//设备唯一ID(Android取IMEI,ios取UUID)
16
public String device_system_version = DevicesInfoHelper.getInstance().device_system_version;//操作系统版本(iOS8/Android6.0)
17
public String device_brand = DevicesInfoHelper.getInstance().device_brand;//设备品牌(华为/iPhone)
18
public String device_type = DevicesInfoHelper.getInstance().device_type;//设备型号(huaweimate8/iphone 6s)
19
public String device_root = DevicesInfoHelper.getInstance().device_root;//设备是否root或越狱
20
public String app_version = DevicesInfoHelper.getInstance().app_version;//当前APP的版本信息
21
public String device_resolution = DevicesInfoHelper.getInstance().device_resolution;//设备分辨率
22
23
public String network_operator = NetworkUtils.getNetworkOperatorName();//网络运营商(中国移动/中国电信)
24
public String network_version;//当前网络类型(WIFI,4G,3G)
25
26
public BaseEventBean() {
27
NetworkUtils.NetworkType type = NetworkUtils.getNetworkType();
28
if (type == NetworkUtils.NetworkType.NETWORK_2G) {
29
network_version = "2G";
30
} else if (type == NetworkUtils.NetworkType.NETWORK_3G) {
31
network_version = "3G";
32
} else if (type == NetworkUtils.NetworkType.NETWORK_4G) {
33
network_version = "4G";
34
} else if (type == NetworkUtils.NetworkType.NETWORK_WIFI) {
35
network_version = "WIFI";
36
} else if (type == NetworkUtils.NetworkType.NETWORK_NO) {
37
network_version = "无网络";
38
} else {
39
network_version = "网络未知";
40
}
41
}
42
}
ClickEventBean
public class ClickEventBean extends BaseEventBean {
public String page_sessionid;// 页面ID(每次启动打开页面随机生成的ID,只在当前页面有效)
public long click_id;// 被点击控件的ID
public String click_name;// 被点击控件的文字
public String click_parent_page;// 被点击控件的父页面名称
public long click_time;// 控件被点击的时间
public String click_view_type;// 控件的名称(TEXTVIEW,BUTTON)
private ClickEventBean(Builder builder) {
page_sessionid = builder.page_sessionid;
click_id = builder.click_id;
click_name = builder.click_name;
click_parent_page = builder.click_parent_page;
click_time = builder.click_time;
click_view_type = builder.click_view_type;
type = EventEnum.BaseEventBean.value;
modelType= EventConfigHelper.CLIENT_LOGS_EVENT_CLICK;
}
}
x
20
1
public class ClickEventBean extends BaseEventBean {
2
public String page_sessionid;// 页面ID(每次启动打开页面随机生成的ID,只在当前页面有效)
3
public long click_id;// 被点击控件的ID
4
public String click_name;// 被点击控件的文字
5
public String click_parent_page;// 被点击控件的父页面名称
6
public long click_time;// 控件被点击的时间
7
public String click_view_type;// 控件的名称(TEXTVIEW,BUTTON)
8
9
private ClickEventBean(Builder builder) {
10
page_sessionid = builder.page_sessionid;
11
click_id = builder.click_id;
12
click_name = builder.click_name;
13
click_parent_page = builder.click_parent_page;
14
click_time = builder.click_time;
15
click_view_type = builder.click_view_type;
16
17
type = EventEnum.BaseEventBean.value;
18
modelType= EventConfigHelper.CLIENT_LOGS_EVENT_CLICK;
19
}
20
}
EventEnum
public enum EventEnum {
BaseEventBean(0), PageEventBean(1), PageBackFrontEventBean(2), RequestEventBean(3),
ExceptionEventBean(4), ClickEventBean(5), WebViewEventBean(6), CustomEventBean(7);
public final int value;
EventEnum(int value) {
this.value = value;
}
}
10
10
1
public enum EventEnum {
2
BaseEventBean(0), PageEventBean(1), PageBackFrontEventBean(2), RequestEventBean(3),
3
ExceptionEventBean(4), ClickEventBean(5), WebViewEventBean(6), CustomEventBean(7);
4
5
public final int value;
6
7
EventEnum(int value) {
8
this.value = value;
9
}
10
}
CustomEventIdHelper
public class CustomEventIdHelper {
private Map<String, CustomEventIdBean> map;
private boolean hasInit = false;
private static CustomEventIdHelper instance = new CustomEventIdHelper();
private CustomEventIdHelper() {//构造方法私有
}
public static CustomEventIdHelper getInstance() {
return instance;
}
private void initMaps() {
try {
InputStream is = Utils.getContext().getResources().getAssets().open("CustomEvents.json");
int lenght = is.available();
byte[] buffer = new byte[lenght];
is.read(buffer);
String result = new String(buffer, "utf8");
//CherryCodeLogUtil.i("bqt", "【解析GSON】" + result);
map = new Gson().fromJson(result, new TypeToken<HashMap<String, CustomEventIdBean>>() {
}.getType());
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public CustomEventIdBean getCustomEventIdBeanById(String id) {
if (map != null) {
return map.get(id);
} else {
return null;
}
}
public void addEvent(String id) {
CherryCodeLogUtil.i("bq", "【自定义事件的id】" + id);
if (!hasInit && map == null) {
initMaps();
hasInit = true;
CherryCodeLogUtil.i("bqt", "【自定义事件的数量】" + (map == null ? "空" : map.size()));
}
if (map != null) {
CustomEventIdBean idBean = map.get(id.toUpperCase());
if (idBean != null) {
CustomEventBean eventBean = new CustomEventBean.Builder().event_id(idBean.event_ID)
.event_data(idBean.event_des)
.event_description(idBean.event_des)
.build();
if (CustomEventId.ID_START_APP.equalsIgnoreCase(id)) {
new EventDao().add(eventBean);
} else {
EventHelper.addEvent(eventBean);
}
}
}
}
}
62
62
1
public class CustomEventIdHelper {
2
private Map<String, CustomEventIdBean> map;
3
private boolean hasInit = false;
4
5
private static CustomEventIdHelper instance = new CustomEventIdHelper();
6
7
private CustomEventIdHelper() {//构造方法私有
8
}
9
10
public static CustomEventIdHelper getInstance() {
11
return instance;
12
}
13
14
private void initMaps() {
15
try {
16
InputStream is = Utils.getContext().getResources().getAssets().open("CustomEvents.json");
17
int lenght = is.available();
18
byte[] buffer = new byte[lenght];
19
is.read(buffer);
20
String result = new String(buffer, "utf8");
21
22
//CherryCodeLogUtil.i("bqt", "【解析GSON】" + result);
23
map = new Gson().fromJson(result, new TypeToken<HashMap<String, CustomEventIdBean>>() {
24
}.getType());
25
is.close();
26
} catch (IOException e) {
27
e.printStackTrace();
28
}
29
}
30
31
public CustomEventIdBean getCustomEventIdBeanById(String id) {
32
if (map != null) {
33
return map.get(id);
34
} else {
35
return null;
36
}
37
}
38
39
public void addEvent(String id) {
40
CherryCodeLogUtil.i("bq", "【自定义事件的id】" + id);
41
if (!hasInit && map == null) {
42
initMaps();
43
hasInit = true;
44
CherryCodeLogUtil.i("bqt", "【自定义事件的数量】" + (map == null ? "空" : map.size()));
45
}
46
if (map != null) {
47
CustomEventIdBean idBean = map.get(id.toUpperCase());
48
49
if (idBean != null) {
50
CustomEventBean eventBean = new CustomEventBean.Builder().event_id(idBean.event_ID)
51
.event_data(idBean.event_des)
52
.event_description(idBean.event_des)
53
.build();
54
if (CustomEventId.ID_START_APP.equalsIgnoreCase(id)) {
55
new EventDao().add(eventBean);
56
} else {
57
EventHelper.addEvent(eventBean);
58
}
59
}
60
}
61
}
62
}
EventHelper
public class EventHelper {
//todo 注意要根据情况判断是否需要限制一下存储数据的长度
public static void addEvent(BaseEventBean bean) {
if (isAddCurrentTypeEnentBean(bean)) {
new EventDao().add(bean);
}
}
public static void addEvents(List<BaseEventBean> list) {
List<BaseEventBean> addList = new ArrayList<>();
for (BaseEventBean bean : list) {
if (isAddCurrentTypeEnentBean(bean)) {
addList.add(bean);
}
}
new EventDao().addAll(addList);
}
/**
* 判断指定类型的采集事件是否需要收集(是否需要入库)
*/
public static boolean isAddCurrentTypeEnentBean(BaseEventBean bean) {
return bean.type == EventEnum.PageEventBean.value && EventConfigHelper.getInstance().isPagePathOpen()
|| (bean.type == EventEnum.PageBackFrontEventBean.value && EventConfigHelper.getInstance().isFrontbackOpen())
|| (bean.type == EventEnum.RequestEventBean.value /*&& EventConfigHelper.getInstance().isRequestOpen()*/)//必须
|| (bean.type == EventEnum.ExceptionEventBean.value /*&& EventConfigHelper.getInstance().isExceptionOpen()*/)//必须
|| bean.type == EventEnum.ClickEventBean.value && EventConfigHelper.getInstance().isClickOpen()
|| (bean.type == EventEnum.WebViewEventBean.value && EventConfigHelper.getInstance().isWebviewOpen())
|| (bean.type == EventEnum.CustomEventBean.value && EventConfigHelper.getInstance().isCustomizeOpen());
}
}
32
32
1
public class EventHelper {
2
3
//todo 注意要根据情况判断是否需要限制一下存储数据的长度
4
public static void addEvent(BaseEventBean bean) {
5
if (isAddCurrentTypeEnentBean(bean)) {
6
new EventDao().add(bean);
7
}
8
}
9
10
public static void addEvents(List<BaseEventBean> list) {
11
List<BaseEventBean> addList = new ArrayList<>();
12
for (BaseEventBean bean : list) {
13
if (isAddCurrentTypeEnentBean(bean)) {
14
addList.add(bean);
15
}
16
}
17
new EventDao().addAll(addList);
18
}
19
20
/**
21
* 判断指定类型的采集事件是否需要收集(是否需要入库)
22
*/
23
public static boolean isAddCurrentTypeEnentBean(BaseEventBean bean) {
24
return bean.type == EventEnum.PageEventBean.value && EventConfigHelper.getInstance().isPagePathOpen()
25
|| (bean.type == EventEnum.PageBackFrontEventBean.value && EventConfigHelper.getInstance().isFrontbackOpen())
26
|| (bean.type == EventEnum.RequestEventBean.value /*&& EventConfigHelper.getInstance().isRequestOpen()*/)//必须
27
|| (bean.type == EventEnum.ExceptionEventBean.value /*&& EventConfigHelper.getInstance().isExceptionOpen()*/)//必须
28
|| bean.type == EventEnum.ClickEventBean.value && EventConfigHelper.getInstance().isClickOpen()
29
|| (bean.type == EventEnum.WebViewEventBean.value && EventConfigHelper.getInstance().isWebviewOpen())
30
|| (bean.type == EventEnum.CustomEventBean.value && EventConfigHelper.getInstance().isCustomizeOpen());
31
}
32
}
后台服务 LogUploadService
/**
* Desc:通过广播接收器和服务的循环调用实现无限循环。该方式可唤醒cpu实现精确定时,适用于在后台执行一些长期的定时行为。
*
* @author <a href="http://www.cnblogs.com/baiqiantao">白乾涛</a><p>
* @tag 采用AlarmManger实现长期精确的定时任务<p>
* @date 2018/4/28 09:49 <p>
*/
public class LogUploadService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
EventUploadHelper.getInstance().upload();
setRepeatAlarm();
return super.onStartCommand(intent, flags, startId);
}
/*AlarmManager中定义的type有五个可选值:
ELAPSED_REALTIME 闹钟在睡眠状态下不可用,如果在系统休眠时闹钟触发,它将不会被传递,直到下一次设备唤醒;使用相对系统启动开始的时间
ELAPSED_REALTIME_WAKEUP 闹钟在手机睡眠状态下会唤醒系统并执行提示功能,使用相对时间
RTC 闹钟在睡眠状态下不可用,该状态下闹钟使用绝对时间,即当前系统时间
RTC_WAKEUP 表示闹钟在睡眠状态下会唤醒系统并执行提示功能,使用绝对时间
POWER_OFF_WAKEUP 表示闹钟在手机【关机】状态下也能正常进行提示功能,用绝对时间,但某些版本并不支持! */
private void setRepeatAlarm() {
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
if (manager != null) {
//定时任务为:发送一条广播。在收到广播后启动本服务,本服务启动后又发送一条广播……
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(this, AlarmReceiver.class), 0);
//参数表示闹钟(首次)执行时间。相对于系统启动时间,Returns milliseconds since boot, including time spent in sleep.
long triggerAtTime = SystemClock.elapsedRealtime() + EventConfigHelper.getInstance().uploadPeriod();
//使用【警报、闹铃】服务设置定时任务。CPU一旦休眠(比如关机状态),Timer中的定时任务就无法运行;而Alarm具有唤醒CPU的功能。
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
}
}
}
46
46
1
/**
2
* Desc:通过广播接收器和服务的循环调用实现无限循环。该方式可唤醒cpu实现精确定时,适用于在后台执行一些长期的定时行为。
3
*
4
* @author <a href="http://www.cnblogs.com/baiqiantao">白乾涛</a><p>
5
* @tag 采用AlarmManger实现长期精确的定时任务<p>
6
* @date 2018/4/28 09:49 <p>
7
*/
8
public class LogUploadService extends Service {
9
10
11
public void onCreate() {
12
super.onCreate();
13
}
14
15
16
public IBinder onBind(Intent intent) {
17
return null;
18
}
19
20
21
public int onStartCommand(Intent intent, int flags, int startId) {
22
EventUploadHelper.getInstance().upload();
23
setRepeatAlarm();
24
return super.onStartCommand(intent, flags, startId);
25
}
26
27
/*AlarmManager中定义的type有五个可选值:
28
ELAPSED_REALTIME 闹钟在睡眠状态下不可用,如果在系统休眠时闹钟触发,它将不会被传递,直到下一次设备唤醒;使用相对系统启动开始的时间
29
ELAPSED_REALTIME_WAKEUP 闹钟在手机睡眠状态下会唤醒系统并执行提示功能,使用相对时间
30
RTC 闹钟在睡眠状态下不可用,该状态下闹钟使用绝对时间,即当前系统时间
31
RTC_WAKEUP 表示闹钟在睡眠状态下会唤醒系统并执行提示功能,使用绝对时间
32
POWER_OFF_WAKEUP 表示闹钟在手机【关机】状态下也能正常进行提示功能,用绝对时间,但某些版本并不支持! */
33
private void setRepeatAlarm() {
34
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
35
if (manager != null) {
36
//定时任务为:发送一条广播。在收到广播后启动本服务,本服务启动后又发送一条广播……
37
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(this, AlarmReceiver.class), 0);
38
39
//参数表示闹钟(首次)执行时间。相对于系统启动时间,Returns milliseconds since boot, including time spent in sleep.
40
long triggerAtTime = SystemClock.elapsedRealtime() + EventConfigHelper.getInstance().uploadPeriod();
41
42
//使用【警报、闹铃】服务设置定时任务。CPU一旦休眠(比如关机状态),Timer中的定时任务就无法运行;而Alarm具有唤醒CPU的功能。
43
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
44
}
45
}
46
}
后台服务 AlarmReceiver
/**
* Desc:通过广播接收器和服务的循环调用实现无限循环。该方式可唤醒cpu实现精确定时,适用于在后台执行一些长期的定时行为。
*
* @author <a href="http://www.cnblogs.com/baiqiantao">白乾涛</a><p>
* @tag 采用AlarmManger实现长期精确的定时任务<p>
* @date 2018/4/28 09:49 <p>
*/
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context, LogUploadService.class));
EventUploadHelper.getInstance().upload();
}
}
15
1
/**
2
* Desc:通过广播接收器和服务的循环调用实现无限循环。该方式可唤醒cpu实现精确定时,适用于在后台执行一些长期的定时行为。
3
*
4
* @author <a href="http://www.cnblogs.com/baiqiantao">白乾涛</a><p>
5
* @tag 采用AlarmManger实现长期精确的定时任务<p>
6
* @date 2018/4/28 09:49 <p>
7
*/
8
public class AlarmReceiver extends BroadcastReceiver {
9
10
11
public void onReceive(Context context, Intent intent) {
12
context.startService(new Intent(context, LogUploadService.class));
13
EventUploadHelper.getInstance().upload();
14
}
15
}
2018-5-5