日志埋点 核心设计


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
   @Override
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
   @Override
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
    @Override
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
    @Override
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
    @Override
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
    @Override
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
    @Override
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
@Aspect
5
public class OnClickAspect {
6
    @Pointcut("execution(* android.view.View.On*Listener.on*Click(..)) ")//定义匹配范围:执行指定方法时拦截
7
    public void onClick() {//匹配View.OnClickListener中的onClick方法和View.OnLongClickListener中的OnLongClickListener方法
8
    }
9
    
10
    @Pointcut("execution(* *.on*ItemClick(..)) ")//如果有多个匹配范围,可以定义多个,多个规则之间通过 || 或 && 控制
11
    public void onItemClick() {//匹配任意名字以on开头以ItemClick结尾的方法
12
    }
13
    
14
    @Pointcut("execution(@butterknife.OnClick * *(..))")//匹配通过butterknife的OnClick注解添加的点击事件
15
    public void butterKnifeOnClick() {
16
    }
17
    
18
    @Around("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
@Aspect
5
public class WebViewAspect {
6
    @Pointcut("execution(* *.onPageStarted(..)) ")//定义匹配范围:执行指定方法时拦截
7
    public void onPageStarted() {
8
    }
9
    
10
    @Around("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
    @Override
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
    @Override
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
    @Override
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
    @Override
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
    @Override
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
    @Override
11
    public void onCreate() {
12
        super.onCreate();
13
    }
14
    
15
    @Override
16
    public IBinder onBind(Intent intent) {
17
        return null;
18
    }
19
    
20
    @Override
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
    @Override
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




附件列表

    猜你喜欢

    转载自www.cnblogs.com/baiqiantao/p/70d7db3fb5cf89d6314371d196e45abf.html