Android program makes its own Log log collection system

write in front

In our code, try{}catch is usually used to catch some expected exceptions. However, usually, our code will have unexpected exception information. How do we capture these exceptions and upload them to Do you use your own server to analyze and fix bugs?

UncaughtExceptionHandler接口

Java provides us with a mechanism to catch and handle unchecked exceptions thrown in a thread object to avoid program termination. We can do it with UncaughtExceptionHandler.
So how to use it?
Prime, we need to define a class and implement UncaughtExceptionHandler

public class CatchExceptionUtil implements UncaughtExceptionHandler {
    // ......
}

Then, write an initialization method

public class CatchExceptionUtil implements UncaughtExceptionHandler {
    /**
     * 初始化
     * @param context
     */
    public void init(Context context) {
        mContext = (MyApplication) context;
        // 获取系统默认的UncaughtException处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        // 设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }
}

Override the uncaughtException method

public class CatchExceptionUtil implements UncaughtExceptionHandler {
    /**
     * 初始化
     * @param context
     */
    public void init(Context context) {
        mContext = (MyApplication) context;
        // 获取系统默认的UncaughtException处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        // 设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 当UncaughtException发生时会转入该函数来处理
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (!handleException(ex) && mDefaultHandler != null) {
            // 如果用户没有处理则让系统默认的异常处理器来处理
            mDefaultHandler.uncaughtException(thread, ex);
        } else {
            // 程序出现了异常,通常我们在这里退出,并重新启动应用
            ToastUtil.showShortToast(mContext,"出现未知异常");
            forceExit();
        }
}

In the above code, we see a very critical method, handleException , we need to do our own processing here, such as uploading crash information to the server, or storing Log logs to SD card, etc.
The following is the method of handleException

    /**
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
     * 
     * @param ex
     * @return true:如果处理了该异常信息;否则返回false.
     */
    private boolean handleException(Throwable ex) {
        if (ex == null) {
            return false;
        }
        // 使用Toast来显示异常信息
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                Looper.loop();
            }
        }.start();
        // 收集设备参数信息
        collectDeviceInfo(mContext);
        // 保存日志文件
        saveCrashInfo2File(ex);
        //上传日志信息
        uploadingLog();
        return true;
    }

All the methods of this class will be posted at the end of the article. Next, we will call this class in Application

        CatchExceptionUtil crashHandler = CatchExceptionUtil.getInstance();//
        crashHandler.init(getApplicationContext());

The following is the complete code of this class


/**
 * @brief 异常崩溃处理类
 * @details 当程序发生未捕获异常时,由该类来接管程序并记录发送错误报告。
 */
public class CatchExceptionUtil implements UncaughtExceptionHandler {

    public static final String TAG = "CatchExceptionUtil";

    // 系统默认的UncaughtException处理类
    private UncaughtExceptionHandler mDefaultHandler;
    // CrashHandler实例
    private static CatchExceptionUtil INSTANCE = new CatchExceptionUtil();
    // 程序的Context对象
    private MyApplication mContext;
    // 用来存储设备信息和异常信息
    private Map<String, String> infos = new HashMap<String, String>();

    // 用于格式化日期,作为日志文件名的一部分
    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");

    private StringBuffer context;

    private String mId;

    /** 保证只有一个CrashHandler实例 */
    private CatchExceptionUtil() {
    }

    /** 获取CrashHandler实例 ,单例模式 */
    public static CatchExceptionUtil getInstance() {
        return INSTANCE;
    }

    /**
     * 初始化
     * 
     * @param context
     */
    public void init(Context context) {
        mContext = (MyApplication) context;
        // 获取系统默认的UncaughtException处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        // 设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 当UncaughtException发生时会转入该函数来处理
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (!handleException(ex) && mDefaultHandler != null) {
            // 如果用户没有处理则让系统默认的异常处理器来处理
            mDefaultHandler.uncaughtException(thread, ex);
        } else {
            // 退出程序
            ToastUtil.showShortToast(mContext,"出现未知异常");
//          Intent intent = new Intent(mContext, MainActivity.class);
//          intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//          mContext.startActivity(intent);
            forceExit();
            //android.os.Process.killProcess(android.os.Process.myPid());
//          Intent intent = new Intent(mContext.getApplicationContext(), MainActivity.class);
//          PendingIntent restartIntent = PendingIntent.getActivity(
//                  mContext.getApplicationContext(), 0, intent,
//                  PendingIntent.FLAG_ONE_SHOT);
//          //退出程序
//          AlarmManager mgr = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
//          mgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 0,
//                  restartIntent); // 1秒钟后重启应用
            //application.finishActivity();

        }


    }

    private void forceExit() {
        MyApplication.getInstance().finishAllActivity();
        android.os.Process.killProcess(android.os.Process.myPid());
        System.exit(1);
    }

    /**
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
     * 
     * @param ex
     * @return true:如果处理了该异常信息;否则返回false.
     */
    private boolean handleException(Throwable ex) {
        if (ex == null) {
            return false;
        }
        // 使用Toast来显示异常信息
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                Looper.loop();
            }
        }.start();
        // 收集设备参数信息
        collectDeviceInfo(mContext);
        // 保存日志文件
        saveCrashInfo2File(ex);
        //上传日志信息
        uploadingLog();
        return true;
    }

    /**上传日志信息*/
    private void uploadingLog() {
        //需要将参数上传
        String uid=mContext.getUserEntity().getUserId();
        String platform="ANDROID";
        String con = context.toString();
        String device_mode= Build.MODEL ;
        String device_id= Build.ID;
        RequestParams params = new RequestParams(HttpUrlManager.clientcrash());
        params.addBodyParameter("uid", uid);
        params.addBodyParameter("platform", platform);
        params.addBodyParameter("content", con);
        params.addBodyParameter("device_mode", device_mode);
        params.addBodyParameter("device_id", device_id);
        params.addBodyParameter("version", SystemUtil.GetVersionName(mContext));
        x.http().post(params, new Callback.CommonCallback<String>() {
            @Override
            public void onCancelled(CancelledException arg0) {
            }

            @Override
            public void onError(Throwable arg0, boolean arg1) {
                forceExit();
            }

            @Override
            public void onFinished() {

            }

            @Override
            public void onSuccess(String arg0) {
                forceExit();
        }
        });
    }

    /**
     * 收集设备参数信息
     * 
     * @param ctx
     */
    public void collectDeviceInfo(Context ctx) {
        try {
            PackageManager pm = ctx.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),
                    PackageManager.GET_ACTIVITIES);
            if (pi != null) {
                String versionName = pi.versionName == null ? "null" : pi.versionName;
                String versionCode = pi.versionCode + "";
                infos.put("versionName", versionName);
                infos.put("versionCode", versionCode);
            }
        } catch (NameNotFoundException e) {
            Log.e(TAG, "an error occured when collect package info", e);
        }
        Field[] fields = Build.class.getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                infos.put(field.getName(), field.get(null).toString());
            } catch (Exception e) {
                Log.e(TAG, "an error occured when collect crash info", e);
            }
        }
    }

    /**
     * 保存错误信息到文件中
     * 
     * @param ex
     * @return 返回文件名称,便于将文件传送到服务器
     */
    @SuppressLint("SdCardPath")
    private String saveCrashInfo2File(Throwable ex) {

        context = new StringBuffer();
        for (Map.Entry<String, String> entry : infos.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            if ("ID".equals(key)) {
                mId = value;
            }
            context.append(key + "=" + value + "\n");
        }



        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        ex.printStackTrace(printWriter);
        Throwable cause = ex.getCause();
        while (cause != null) {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
        printWriter.close();
        String result = writer.toString();
        context.append(result);
        try {
            long timestamp = System.currentTimeMillis();
            String time = formatter.format(new Date());
            String fileName = "crash-" + time + "-" + timestamp + ".log";
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                String path = "/sdcard/crash/";
                File dir = new File(path);
                if (!dir.exists()) {
                    dir.mkdirs();
                }
                FileOutputStream fos = new FileOutputStream(path + fileName);
                fos.write(context.toString().getBytes());
                fos.close();
            }
            Log.e(TAG, context.toString());
            return fileName;
        } catch (Exception e) {
            Log.e(TAG, "an error occured while writing file...", e);
        }
        return null;
    }
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325193146&siteId=291194637