Android 实现异常捕捉并推送到钉钉

时间:2024-04-04 14:25:23

前言
开发过程中,碰到异常崩溃什么的都是无可避免的,这里介绍一种记录方法:将异常捕获并以钉钉消息的形式发送到钉钉聊天群;
自定义机器人
一、获取自定义机器人webhook
在机器人管理页面选择“自定义”机器人,输入机器人名字并选择要发送消息的群。
1、进入群聊天设置
Android 实现异常捕捉并推送到钉钉
2、找到群机器人入口
Android 实现异常捕捉并推送到钉钉
3、找到自定义机器人,并添加

Android 实现异常捕捉并推送到钉钉
Android 实现异常捕捉并推送到钉钉
4、命名机器人、选择要消息接收群
Android 实现异常捕捉并推送到钉钉
5、获得这个机器人对应的Webhook地址
Android 实现异常捕捉并推送到钉钉
格式如下所示

https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxxxxxx

二、使用机器人
获取到Webhook地址后,就可以使用任何方式向这个地址发起HTTP POST 请求,实现给我们想要告知的群组发送消息。当前自定义机器人支持文本(text)、连接(link)、markdown(markdown)三种消息类型,大家可以根据自己的需求选择合适的消息类型
下面是官网定义的消息格式

{
     "msgtype": "text",
     "text": {
         "content": "我就是我,  @1825718XXXX 是不一样的烟火"  //被@人的手机号
     },
     "at": {
         "atMobiles": [
             "1825718XXXX"
         ], 
         "isAtAll": false   //@所有人时:true,否则为:false
     }
 }

Android 实现异常捕捉并推送到钉钉
自定义机器人发送消息时,可以通过手机号码指定“被@人列表”。在“被@人列表”里面的人员,在收到该消息时,会有@消息提醒(免打扰会话仍然通知提醒,首屏出现“有人@你”)
异常捕获工具类

public class CrashUtils implements UncaughtExceptionHandler {
    private UncaughtExceptionHandler exceptionHandler;

    public CrashUtils() {
        exceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
    }

    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        ex.printStackTrace(printWriter);
        writeToFile(
                writer.toString() + "\n" + thread.getName() + "\n"
                        + thread.toString(), FileUtils.getCrashSavePath());
        exceptionHandler.uncaughtException(thread, ex);
    }

    private void writeToFile(String stacktrace, String filename) {    
        ErrorInfoBean infoBean = new ErrorInfoBean(UsersCache.getInstance().getPhone(),
                SystemUtils.getDeviceModel(), AppUtils.getVersion(MyApplication.getInstance()),
                TimeUtils.getCurData(), "APP Crash Info:\n" + stacktrace, SPUtil.newInstance(MyApplication.getInstance()).getString(AppConstants.REQUEST_INFO));

        try {
            File targetFile = new File(filename);
            if (!targetFile.exists()) {
                targetFile.createNewFile();
            }
            if (targetFile.exists()) {
                try {
                    if (Environment.getExternalStorageState().equals(
                            Environment.MEDIA_UNMOUNTED)) {
                        return;
                    }
                    FileWriter fileWriter = new FileWriter(targetFile);
                    BufferedWriter bos = new BufferedWriter(fileWriter);
                    bos.write(infoBean.toString());
                    //                    bos.write(android.os.Build.VERSION.SDK_INT + "\n");
                    //                    bos.write(android.os.Build.MODEL + "\n");
                    //                    bos.write("APP :" + SystemUtils.getVersionName() + "\n");
                    //                    bos.write("time :"
                    //                            + DateFormat.format("yyyy-MM-dd kk:mm:ss",
                    //                            new Date(System.currentTimeMillis())) + "\n");
                    //                    bos.write(stacktrace);
                    bos.flush();
                    bos.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     *  {
     * "msgtype": "text",
     *      "text": {
     *          "content": "我就是我,  @1825718XXXX 是不一样的烟火"
     *      }
     * }
     *
     * @param stacktrace
     */
    public static void sendDingDing(String stacktrace) {
        JSONObject reqBody = new JSONObject();
        reqBody.put("accessToken", "429670b34acbe67723ea19be29ea2801f23b9eaf2900033bcf5874e400b18cf3");
        reqBody.put("content", stacktrace);

        ApiClient.requestService.sendDing(NetworkUtils.getRequestBody(reqBody.toString()))
                .compose(RxSchedulerUtils.normalSchedulersTransformer())
                .subscribe(new BaseSubscriber<SimpleResult>() {

                    @Override
                    public void onSuccess(SimpleResult entity) {
                        LogUtils.logd(entity.getData());
                    }

                    @Override
                    public void onFailure(SimpleResult entity, int errCode, String errMsg) {
                    }
                });
    }
}

在Subscriber抛出错误的地方调用发送的方法

public abstract class BaseSubscriber<T> extends Subscriber<T> {
    @Override
    public void onStart() {
        super.onStart();
        if (!NetworkUtils.isNetworkConnected(MyApplication.getInstance())) {
            if (!isUnsubscribed()) {
                unsubscribe();
            }
            onFailure(null, 0, MyApplication.getInstance().getString(R.string.no_network));
        }
    }

    @Override
    public void onCompleted() {
    }

    @Override
    public void onNext(T t) {
        if (t == null) {
        	onFailure(null, 0, MyApplication.getInstance().getString(R.string.no_network));
            return;
        }
        onSuccess(t);
    }

    @Override
    public void onError(Throwable e) {
        e.printStackTrace();
        FileUtils.saveLog(e.toString());
        ErrorInfoBean infoBean = new ErrorInfoBean(UsersCache.getInstance().getPhone(),
                SystemUtils.getDeviceModel(), AppUtils.getVersion(MyApplication.getInstance()),
                TimeUtils.getCurData(), e.toString(), SPUtil.newInstance(MyApplication.getInstance()).getString(AppConstants.REQUEST_INFO));
                
        CrashUtils.sendDingDing(infoBean.toString());  //发送给钉钉
    }

    public abstract void onSuccess(T result);

    public abstract void onFailure(T result, int errCode, String errMsg);
}

这样就算接入成功了,可以在群消息里接收你的错误日志了
Android 实现异常捕捉并推送到钉钉