欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 如何收集已发布程序的崩溃信息

程序员文章站 2023-11-25 10:21:34
我们写程序的时候都希望能写出一个没有任何bug的程序,期望在任何情况下都不会发生程序崩溃。不过理想是丰满的,现实是骨感的。没有一个程序员能保证自己写的程序绝对不会出现异常崩...

我们写程序的时候都希望能写出一个没有任何bug的程序,期望在任何情况下都不会发生程序崩溃。不过理想是丰满的,现实是骨感的。没有一个程序员能保证自己写的程序绝对不会出现异常崩溃。特别是针对用户数达到几十万几百万的程序,当你用户数达到一定数量级后,就算你的程序出现个别异常崩溃情况也不用惊讶。

既然我们写的程序都有可能发生异常崩溃,如果是还没发布的程序,我们可以通过测试抓取log来分析。不过针对已经发布的程序,我们没法重现现象,所以让用户反馈程序异常信息就很重要。下面我们说说如何收集程序运行过程的异常信息。

1、android异常捕获接口

复制代码 代码如下:

//当线程因未捕获的异常而突然终止时,调用处理程序的接口
static interface uncaughtexceptionhandler

2、设置线程捕获异常
从上面的接口我们可以看到,这个接口是针对线程来说,也就是说我们如果需要监控某个线程运行情况,只要把这个接口实现了,然后把监控方法设置到具体的线程里面即可。一般来说,我们最需要监控的就是我们的ui线程也就是主线程。
复制代码 代码如下:

//设置当线程由于未捕获到异常而突然终止,并且没有为该线程定义其他处理程序时所调用的默认处理程序。
static void setdefaultuncaughtexceptionhandler(thread.uncaughtexceptionhandler eh)

3、uncaughtexceptionhandler 实例
复制代码 代码如下:

class mythoucrashhandler implements uncaughtexceptionhandler
{
    private static final string tag = "mythoucrashhandler---->";
    private uncaughtexceptionhandler defaultueh;
  //构造函数,获取默认的处理方法
    public mythoucrashhandler()
    {
        this.defaultueh = thread.getdefaultuncaughtexceptionhandler();
    }
  //这个接口必须重写,用来处理我们的异常信息
    @override
    public void uncaughtexception(thread thread, throwable ex)
    {
        final writer result = new stringwriter();
        final printwriter printwriter = new printwriter(result);
     //获取跟踪的栈信息,除了系统栈信息,还把手机型号、系统版本、编译版本的唯一标示
        stacktraceelement[] trace = ex.getstacktrace();
        stacktraceelement[] trace2 = new stacktraceelement[trace.length+3];
        system.arraycopy(trace, 0, trace2, 0, trace.length);
        trace2[trace.length+0] = new stacktraceelement("android", "model", android.os.build.model, -1);
        trace2[trace.length+1] = new stacktraceelement("android", "version", android.os.build.version.release, -1);
        trace2[trace.length+2] = new stacktraceelement("android", "fingerprint", android.os.build.fingerprint, -1);
    //追加信息,因为后面会回调默认的处理方法
        ex.setstacktrace(trace2);
        ex.printstacktrace(printwriter);
     //把上面获取的堆栈信息转为字符串,打印出来
        string stacktrace = result.tostring();
        printwriter.close();
        log.e(tag, stacktrace);
        //这里把刚才异常堆栈信息写入sd卡的log日志里面
        if(environment.getexternalstoragestate().equals(environment.media_mounted))
        {
            string sdcardpath = environment.getexternalstoragedirectory().getpath();
            writelog(stacktrace, sdcardpath + "/mythou");
        }
        defaultueh.uncaughtexception(thread, ex);
    }
  //写入log信息的方法,写入到sd卡里面
    private void writelog(string log, string name)
    {
        charsequence timestamp = dateformat.format("yyyymmdd_kkmmss", system.currenttimemillis());
        string filename = name + "_" + timestamp + ".log";
        try
        {
            fileoutputstream stream = new fileoutputstream(filename);
            outputstreamwriter output = new outputstreamwriter(stream);
            bufferedwriter bw = new bufferedwriter(output);
       //写入相关log到文件
            bw.write(log);
            bw.newline();
            bw.close();
            output.close();
        }
        catch (ioexception e)
        {
            e.printstacktrace();
        }
    }
}

上面就是实现了获取处理跟踪信息的方法,上面的方法是参照vlc的异常处理机制编写的。做了一些简单修改。不过上面只是获取了异常信息,如果程序安装到用户机器上,我们没法获取到这些信息,总不能让用户把机器拿过来给你,然后你把log拷贝出来吧。(这个我以前做嵌入式的时候到试过,让客户把机器拿过来,拷贝里面的log,那时候做的机器无法联网。现在想起来都纠结,o(∩_∩)o哈哈~) 为了不再纠结,我们需要一个可以把log发送到我们服务器的功能,下面是把一个服务信息发送到我们指定服务器功能。

3、通过网络发送log
复制代码 代码如下:

   public class sendcrashlog extends asynctask<string, string, boolean>
    {
        public sendcrashlog() { }
        @override
        protected boolean doinbackground(string... params)
        {
            if (params[0].length() == 0)
                return false;
            httpclient httpclient = new defaulthttpclient();
       //你的服务器,这里只是举个例子。把异常信息当作http请求发送到服务器
            httppost httppost = new httppost("http://www.mythou/getlog.php");
       //这里把相关的异常信息转为http post请求的数据参数
            try {
                list<namevaluepair> namevaluepairs = new arraylist<namevaluepair>(1);
                namevaluepairs.add(new basicnamevaluepair("model", params[0]));
                namevaluepairs.add(new basicnamevaluepair("device", params[1]));
                httppost.setentity(new urlencodedformentity(namevaluepairs));
         //发送相关请求信息
                httpclient.execute(httppost);
            } catch (clientprotocolexception e) {
                e.printstacktrace();
                return false;
            } catch (ioexception e) {
                e.printstacktrace();
                return false;
            }
            log.d(tag, "device model sent.");
            return true;
        }
        @override
        protected void onpostexecute(boolean result) {
        }
    }

上面就是我上一篇文章讲的异步任务的使用,我们在异步任务里面编写了一个发送http请求的服务,用来把相关的异常信息发送到我们指定的服务器上面。这个需要你的服务器解析发送的http请求,这个难度不大,一般做个web的人都知道如何做。在上面的异常处理里面再调用这里的发送方法:
复制代码 代码如下:

sendcrashlogsendlog = new sendcrashlog();
//刚才的异常信息字符串
sendlog .execute(stacktrace);

通过上面的方法就可以把异常信息发送到指定的服务器,也就可以跟踪客户使用软件的情况,方便我们修改程序的问题。当然这个信息收集一般都隐私和后台流量问题,这个需要在程序里面做点提示,免得背上流氓软件的骂名。