Android捕捉崩溃情况推送到钉钉
前言
开发过程中,发测了,如果遇到崩溃情况,想记录下来有哪几个方法?一、用bugly,设置是开发模式,崩溃了会马上上报。二、收集崩溃信息发送到邮箱。那现在再试一种方式,就是推送崩溃信息到钉钉。
钉钉机器人了解
我们参考钉钉的开发文档:自定义机器人 ,先要登录pc版或者网页版的钉钉,然后在聊天群页面增加一个机器人:
记住这个hook地址,这个其实就是相当于一个请求接口。
开始使用机器人
获取到Webhook地址后,用户可以使用任何方式向这个地址发起HTTP POST 请求,即可实现给该群组发送消息。注意,发起POST请求时,必须将字符集编码设置成UTF-8。
当前自定义机器人支持文本(text)、连接(link)、markdown(markdown)三种消息类型,大家可以根据自己的使用场景选择合适的消息类型,达到最好的展示样式。具体的消息类型参考下一节内容。
自定义机器人发送消息时,可以通过手机号码指定“被@人列表”。在“被@人列表”里面的人员,在收到该消息时,会有@消息提醒(免打扰会话仍然通知提醒,首屏出现“有人@你”)
比如,我想推送文本信息:
{
"msgtype": "text",
"text": {
"content": "应用崩溃了, @1825718XXXX 快去看看是什么回事"
},
"at": {
"atMobiles": [
"1825718XXXX"
],
"isAtAll": false
}
}
我们请求一个接口,接口地址就是上面的hook地址,请求方式为post,然后设置post的内容为上面这段。那钉钉里面就会收到信息。
崩溃信息收集
知道钉钉的推送原理后,其实我们只要把崩溃信息保存下来,然后请求一下,就可以把崩溃信息推送到钉钉了。我们在网上找到一个收集异常信息的类:
public class CrashHandler implements Thread.UncaughtExceptionHandler {
//系统默认的异常处理器
private Thread.UncaughtExceptionHandler defaultCrashHandler;
private static CrashHandler crashHandler = new CrashHandler();
private Context mContext;
//私有化构造函数
private CrashHandler() {
}
//获取实例
public static CrashHandler getInstance() {
return crashHandler;
}
public void init(Context context) {
if (L.Debug) {
mContext = context;
defaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
//设置系统的默认异常处理器
Thread.setDefaultUncaughtExceptionHandler(this);
}
}
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
//把错误信息保存在sp中,然后在下次进入页面的时候再上传错误信息
saveErrorInfo(throwable);
if (defaultCrashHandler != null) {
//如果在自定义异常处理器之前,系统有自己的默认异常处理器的话,调用它来处理异常信息
defaultCrashHandler.uncaughtException(thread, throwable);
} else {
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
}
public void sendError() {
if (L.Debug) {
final SharedPreferences sp = mContext.getSharedPreferences("errorInfo", Context.MODE_PRIVATE);
String data = sp.getString("data", "");
if (!data.equals("")) {
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("msgtype", "text");
JSONObject jsonObject1 = new JSONObject();
jsonObject1.put("content", data);
jsonObject.put("text", jsonObject1.toString());
MMCHttp.<String>post("https://oapi.dingtalk.com/robot/send?access_token=a02fce3ced263e2272dc93867e56394dfe3bf4b44104b6e06b660953cc5a").upJson(jsonObject.toString()).execute(new StringCallback() {
@Override
public void onSuccess(Response<String> response) {
Log.i("---", response.body());
sp.edit().putString("data", "").commit();
}
@Override
public void onError(Response<String> response) {
super.onError(response);
Log.i("---", response.body());
}
});
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
private void saveErrorInfo(Throwable throwable) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(getAppInfo(mContext));
stringBuffer.append("崩溃时间:").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())).append("\n");
stringBuffer.append("手机系统:").append(Build.VERSION.RELEASE).append("\n");
stringBuffer.append("手机型号:").append(Build.MODEL).append("\n");
stringBuffer.append("崩溃信息:").append(throwable.getMessage());
SharedPreferences sp = mContext.getSharedPreferences("errorInfo", Context.MODE_PRIVATE);
sp.edit().putString("data", stringBuffer.toString()).commit();
}
/**
* 获取应用程序信息
*/
public String getAppInfo(Context context) {
try {
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
return "应用包名:" + packageInfo.packageName + "\n应用版本:" + packageInfo.versionName + "\n";
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}
就这样完成了推送应用崩溃信息到钉钉群的功能。
拓展
可以利用bugly的异常报告功能,直接推送崩溃异常情况到钉钉。bugly那里可以设置,每天早上9点,会推送一段json信息到你的接口。但这个接口,你要处理一下,不能直接填上面hook的地址,因为bugly推送的json,和上面钉钉发送的json不是一样的。所以,你需要写一个php,例如:
<?php
try {
$response = $HTTP_RAW_POST_DATA ;
$objContent = json_decode($response,true);
$appName = $objContent['eventContent']['appName'];
$num = count($objContent['eventContent']["datas"]);
$crashCount = $objContent['eventContent']["datas"][$num-1]["crashCount"];
$crashUser = $objContent['eventContent']["datas"][$num-1]["crashUser"];
$accessUser = $objContent['eventContent']["datas"][$num-1]["accessUser"];
$version = $objContent['eventContent']["datas"][$num-1]["version"];
$crashCount2 = 0;
$crashUser2 = 0;
$accessUser2 = 0;
$version2 = "";
if($num>1){
$crashCount2 = $objContent['eventContent']["datas"][$num-2]["crashCount"];
$crashUser2 = $objContent['eventContent']["datas"][$num-2]["crashUser"];
$accessUser2 = $objContent['eventContent']["datas"][$num-2]["accessUser"];
$version2 = $objContent['eventContent']["datas"][$num-2]["version"];
}
if($appName!=""){
$webhook = "https://oapi.dingtalk.com/robot/send?access_token=a02fce3ced263e2272dc93867e56394dfe3bf4b44104b6e06b660953cc5";
if($accessUser>$accessUser2){
$message="应用:".$appName.",版本:".$version.",联网用户:".$accessUser.",崩溃用户:".$crashUser.",崩溃数:".$crashCount;
}else{
$message="应用:".$appName.",版本:".$version2.",联网用户:".$accessUser2.",崩溃用户:".$crashUser2.",崩溃数:".$crashCount2;
}
$data = array ('msgtype' => 'text','text' => array ('content' => $message));
$data_string = json_encode($data);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $webhook);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_HTTPHEADER, array ('Content-Type: application/json;charset=utf-8'));
curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// 线下环境不用开启curl证书验证, 未调通情况可尝试添加该代码
curl_setopt ($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt ($ch, CURLOPT_SSL_VERIFYPEER, 0);
$data = curl_exec($ch);
curl_close($ch);
echo $data;
}else{
echo "请输入正确的内容";
}
} catch (Exception $e) {
echo $e;
}
这个php其实就是做了一个中转的功能,把bugly传过来的数据,进行解析,然后整理出你想发到钉钉群的内容,再调用钉钉那个接口,把数据推送到钉钉。整个流程大概就这样。