Android客户端收集Crash信息的常用方法

在开发过程中,我们经常面临的问题是解决造成程序Crash的bug,而解决这些问题的途径无非是通过Crash的时候遗留的log进行分析,快速定位问题发生的原因然后解决问题。大多数时候我们都是在开发过程中进行自测的时候发现问题,这一类的问题发生的时候log信息通常都会显示在控制台上,通过简单分析基本能够得到解决,而真正难以解决的是测试人员在平时测试的时候偶然发现的一个bug,然而又不容易复现,或者是用户使用过程中产生的偶现的Crash,通常这一类问题我们都是无法直接看到Crash发生时的log信息的,解决的方法只能是按照他人提供的操作步骤机械性的进行重复操作,盼着bug能够再现……

针对以上问题,在此提出两种解决思路,当然不是能够直接解决你的Crash的万能代码,而是提供一个在别人手机上发生Crash的时候,你依旧能够获得Crash时产生的log信息,方便开发者定位问题。

针对第二种情况,用户反馈的Crash,在此推荐在你的应用程序中集成腾讯Bugly,这是一款使用十分方便的Crash跟踪工具,集成也十分简单,他们的介绍是:

一种愉悦的开发方式
腾讯Bugly,为移动开发者提供专业的异常上报,运营统计和内测分发解决方案,帮助开发者快速发现并解决异常,同时掌握产品运营动态,及时跟进用户反馈。
腾讯bugly接入首页

使用之后当用户程序由于异常产生Crash的时候,该Crash的log信息会直接上报到你注册的应用中,和在控制台中看到的Crash信息无异,同时提供其他一系列相关数据,包括产生Crash的手机型号,SDK版本等,一图胜千言:

这里写图片描述

这里写图片描述

针对第二种情况,身边测试人员反馈的Crash,或者你自己的手机未接入电脑的时候产生的Crash,这些情况下你都无法直接在控制台上看到log信息,此时提供的解决方案是将发生Crash时候的log信息直接存到手机里,该log信息以文本形式存储,可以直接打开查看,Android为我们提供了UncaughtExceptionHandler这个类来进行该操作,至于这个类的具体介绍网上有很多博文,请自行百度,在此提供工具类。可直接使用,使用方法见后文。

/**
 * Created by ShiXiuwen on 2017/1/14.
 * <p>
 * Description:将程序的Crash信息写进文件
 */

public class Utils_CrashHandler implements Thread.UncaughtExceptionHandler {

    private static final String TAG = "Util_CrashHandler";

    private Context mContext;
    private static Utils_CrashHandler INSTANCE = new Utils_CrashHandler();
    private Thread.UncaughtExceptionHandler mDefaultHandler;
    public static Utils_CrashHandler getInstance() {
        return INSTANCE;
    }

    public void init(Context ctx) {
        mContext = ctx.getApplicationContext();
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    @Override
    public void uncaughtException(final Thread t, final Throwable e) {
        try {
            Writer writer = new StringWriter();
            PrintWriter printWriter = new PrintWriter(writer);
            e.printStackTrace(printWriter);
            Throwable cause = e.getCause();
            while (cause != null) {
                cause.printStackTrace(printWriter);
                cause = cause.getCause();
            }
            printWriter.close();
            String result = writer.toString();

            File file = new File(getDiskCacheDir(mContext, "crash"), "crash.log");
            if (file.exists() && file.length() > 5 * 1024 * 1024) {     // <5M
                boolean delete = file.delete();
            }

            File folder = new File(getDiskCacheDir(mContext, "crash"));
            if (folder.exists() && folder.isDirectory()) {
                if (!file.exists()) {
                    boolean newFile = file.createNewFile();
                    if(!newFile){
                        return;
                    }
                }
            } else {
                boolean mkDir = folder.mkdir();
                if (mkDir) {
                    boolean newFile = file.createNewFile();
                    if(!newFile){
                        return;
                    }
                }
            }
            FileOutputStream fos = new FileOutputStream(file, true);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);
            String format = sdf.format(new Date());
            byte[] bytes = (format +        //追加Crash时间
                    "\n" + result +   //Crash内容
                    "\n" + "######## ~~~ T_T ~~~ ########" +    //分割线
                    "\n").getBytes();
            fos.write(bytes);
            fos.flush();
            fos.close();
        } catch (Exception exception) {
            exception.printStackTrace();
        }
        mDefaultHandler.uncaughtException(t, e);    //该代码不执行的话程序无法终止
    }

    /**
     * @param context context
     * @param dirName dirName
     * @return dir
     */
    private String getDiskCacheDir(Context context, String dirName) {
        String cachePath = null;
        if ("mounted".equals(Environment.getExternalStorageState())) {
            File externalCacheDir = context.getExternalCacheDir();
            if (externalCacheDir != null) {
                cachePath = externalCacheDir.getPath();
            }
        }
        if (cachePath == null) {
            File cacheDir = context.getCacheDir();
            if ((cacheDir != null) && (cacheDir.exists())) {
                cachePath = cacheDir.getPath();
            }
        }
        //0/emulate/Android/data/data/com.***.***/crash/crash.log
        return cachePath + File.separator + dirName;
    }
}

使用方法

在程序的Application类中,直接注册一句代码即可,eg:

/**
 * Created by ShiXiuwen on 2017/1/14.
 * <p>
 * Description:Application 初始化操作
 */

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        //初始化CrashHandler收集崩溃信息到本地
        Utils_CrashHandler.getInstance().init(this);
    }
}

使用该工具类之后,手机到的Crash信息会被保存在手机的缓存目录中,比如你的项目包名为:

com.mycompanyname.demo

则log文件在手机中的存储路径为:

//0/emulate/Android/data/data/com.mycompanyname.demo/crash/crash.log

不同时间发生的Crash都会保存到该文件中,和控制台中输出的log信息无异。

其实在实际开发过程中还会遇到另外一种情况,那就是系统底层发生Crash,比如你调用了JNI文件而产生的Crash,有时候即使你的手机连接在控制台上也无法打印出log信息,因为该这类Crash发生的时候很可能相关进程会直接被kill掉,导致AndroidStudio清楚了控制台上的log信息,该情况下提供的解决思路是打开系统控制台,使用adb logcat命令抓取你想要的log,这样能够保证log不会被清除,不过该方法信息量有点大,要加上合适的过滤条件或者准确的控制开始及结束来定位(操作之前开始打印log,发生Crash的时候立即按Ctrl+C停止log打印),再或者配合log的时间标识记住大概产生Crash的时候log的打印时间,以此定位具体log信息。adb logcat的具体使用方法推荐博客:

http://blog.csdn.net/tumuzhuanjia/article/details/39555445

以上是本人使用过的log收集方法,如客官您有更好的建议,欢迎留言告知,当感激不尽~

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值