首页 文章

如何从Android应用程序获取崩溃数据?

提问于
浏览
691

如何从我的Android应用程序获取崩溃数据(至少是堆栈跟踪)?至少在使用有线电视检索我自己的设备时,最好是从我在野外运行的应用程序的任何实例,以便我可以改进它并使其更加坚固 .

30 回答

  • 2

    Google Play开发者控制台实际上为您提供了已崩溃并已发送报告的应用程序的堆栈跟踪,它还有一个非常好的图表可帮助您查看信息,请参阅下面的示例:

    enter image description here

  • 11

    Google Firebase是Google最新(2016)的方式,可以为您提供手机上的崩溃/错误数据 . 将它包含在build.gradle文件中:

    compile 'com.google.firebase:firebase-crash:9.0.0'
    

    在不需要用户输入的情况下自动记录致命崩溃,您还可以记录非致命崩溃或其他类似事件:

    try
    {
    
    }
    catch(Exception ex)
    {
        FirebaseCrash.report(new Exception(ex.toString()));
    }
    
  • 1

    您可以直接在Android Studio中执行此操作 . 只需连接手机,运行应用程序,让它崩溃,您就可以直接在Android Studio中查看堆栈跟踪 .

  • 4

    刚刚开始使用Google表格作为后端使用ACRA https://github.com/ACRA/acra,它是默认的's very easy to setup & use, it' .

    BUT Sending reports to Google Forms are going to be deprecated (then removed): https://plus.google.com/118444843928759726538/posts/GTTgsrEQdN6 https://github.com/ACRA/acra/wiki/Notice-on-Google-Form-Spreadsheet-usage

    无论如何,您可以定义自己的发件人https://github.com/ACRA/acra/wiki/AdvancedUsage#wiki-Implementing_your_own_sender,例如,您可以尝试通过电子邮件发送邮件 .

    只需最少的努力就可以向bugsense发送报告:http://www.bugsense.com/docs/android#acra

    NB bugsense免费帐户限制为每月500个报告

  • 2

    Flurry analytics为您提供崩溃信息,硬件模型,Android版本和实时应用程序使用统计信息 . 在新的SDK中,他们似乎提供了更详细的崩溃信息http://www.flurry.com/flurry-crash-analytics.html .

  • 18

    您也可以为它使用整个(简单)服务,而不仅仅是库 . 我们公司刚刚发布了一项服务:http://apphance.com .

    它有一个简单的.jar库(适用于Android),您可以在5分钟内添加和集成,然后该库不仅会收集崩溃信息,还会收集正在运行的应用程序的日志,并且它可以让您的测试人员直接从设备报告问题 - 包括整个上下文(设备旋转,是否连接到wifi或更多) . 您可以使用非常好用且有用的Web面板查看日志,您可以在其中跟踪应用程序的会话,崩溃,日志,统计信息等 . 该服务现在处于封闭测试阶段,但您可以请求访问权限,我们会尽快给您 .

    免责声明:我是Polidea的首席技术官,也是该服务的共同创始人 .

  • 7

    我们在公司内部使用我们自己开发的系统,它为我们提供了很好的服务 . 它是一个安卓库,可以向服务器和服务器发送崩溃报告,接收报告并进行一些分析 . 服务器按例外名称,堆栈跟踪,消息对异常进行分组 . 它有助于确定需要修复的大多数关键问题 . 我们的服务现已公开测试,所以每个人都可以尝试 . 您可以在http://watchcat.co创建帐户,或者您可以使用演示访问http://watchcat.co/reports/index.php?demo查看其工作原理 .

  • 1

    谷歌改变了你实际获得的崩溃报告的数量 . 以前您只有手动报告的错误报告 .

    自上次开发者大会和Android Vitals介绍以来,您还可以获得能够共享诊断数据的用户的崩溃报告 .

    您将看到从用户已选择自动共享使用和诊断数据的Android设备收集的所有崩溃 . 前两个月有数据 .

    View crashes & application not responding (ANR) errors

  • 18

    我看到这个问题太老了,希望我的回答对其他有同样问题的人有帮助...

    试试Crashlytics . 它将深入了解所有具有您的应用程序的设备上的所有崩溃,并通过电子邮件向您发送通知 . 最好的部分是它完全免费使用..

  • 3

    对于示例应用程序和调试目的,我使用一个简单的解决方案,允许我将堆栈跟踪写入设备的SD卡和/或将其上传到服务器 . 这个解决方案的灵感来自Project android-remote-stacktrace(特别是保存到设备和上传到服务器的部分),我认为它解决了Soonil提到的问题 . 它不是最佳的,但它可以工作,如果你想在 生产环境 应用程序中使用它,你可以改进它 . 如果您决定将堆栈跟踪上传到服务器,则可以使用php脚本( index.php )来查看它们 . 如果你有兴趣,你可以找到下面的所有资源 - 一个用于你的应用程序的java类和两个用于托管上传的栈跟踪的服务器的php scrips .

    在上下文(例如主Activity)中,调用

    if(!(Thread.getDefaultUncaughtExceptionHandler() instanceof CustomExceptionHandler)) {
        Thread.setDefaultUncaughtExceptionHandler(new CustomExceptionHandler(
                "/sdcard/<desired_local_path>", "http://<desired_url>/upload.php"));
    }
    

    CustomExceptionHandler

    public class CustomExceptionHandler implements UncaughtExceptionHandler {
    
        private UncaughtExceptionHandler defaultUEH;
    
        private String localPath;
    
        private String url;
    
        /* 
         * if any of the parameters is null, the respective functionality 
         * will not be used 
         */
        public CustomExceptionHandler(String localPath, String url) {
            this.localPath = localPath;
            this.url = url;
            this.defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
        }
    
        public void uncaughtException(Thread t, Throwable e) {
            String timestamp = TimestampFormatter.getInstance().getTimestamp();
            final Writer result = new StringWriter();
            final PrintWriter printWriter = new PrintWriter(result);
            e.printStackTrace(printWriter);
            String stacktrace = result.toString();
            printWriter.close();
            String filename = timestamp + ".stacktrace";
    
            if (localPath != null) {
                writeToFile(stacktrace, filename);
            }
            if (url != null) {
                sendToServer(stacktrace, filename);
            }
    
            defaultUEH.uncaughtException(t, e);
        }
    
        private void writeToFile(String stacktrace, String filename) {
            try {
                BufferedWriter bos = new BufferedWriter(new FileWriter(
                        localPath + "/" + filename));
                bos.write(stacktrace);
                bos.flush();
                bos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private void sendToServer(String stacktrace, String filename) {
            DefaultHttpClient httpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(url);
            List<NameValuePair> nvps = new ArrayList<NameValuePair>();
            nvps.add(new BasicNameValuePair("filename", filename));
            nvps.add(new BasicNameValuePair("stacktrace", stacktrace));
            try {
                httpPost.setEntity(
                        new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
                httpClient.execute(httpPost);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    upload.php

    <?php
        $filename = isset($_POST['filename']) ? $_POST['filename'] : "";
        $message = isset($_POST['stacktrace']) ? $_POST['stacktrace'] : "";
        if (!ereg('^[-a-zA-Z0-9_. ]+$', $filename) || $message == ""){
            die("This script is used to log debug data. Please send the "
                    . "logging message and a filename as POST variables.");
        }
        file_put_contents($filename, $message . "\n", FILE_APPEND);
    ?>
    

    index.php

    <?php
        $myDirectory = opendir(".");
        while($entryName = readdir($myDirectory)) {
            $dirArray[] = $entryName;
        }
        closedir($myDirectory);
        $indexCount = count($dirArray);
        sort($dirArray);
        print("<TABLE border=1 cellpadding=5 cellspacing=0 \n");
        print("<TR><TH>Filename</TH><TH>Filetype</th><th>Filesize</TH></TR>\n");
        for($index=0; $index < $indexCount; $index++) {
            if ((substr("$dirArray[$index]", 0, 1) != ".") 
                    && (strrpos("$dirArray[$index]", ".stacktrace") != false)){ 
                print("<TR><TD>");
                print("<a href=\"$dirArray[$index]\">$dirArray[$index]</a>");
                print("</TD><TD>");
                print(filetype($dirArray[$index]));
                print("</TD><TD>");
                print(filesize($dirArray[$index]));
                print("</TD></TR>\n");
            }
        }
        print("</TABLE>\n");
    ?>
    
  • 5

    感谢 Stackoverflow 中的资源帮助我找到这个答案 .

    你可以找到 your remotely Android crash reports directly into your email . 记得你必须 put your email inside CustomExceptionHandler class .

    public static String sendErrorLogsTo = "tushar.pandey@virtualxcellence.com" ;
    

    所需步骤:

    1)在你的活动的onCreate中使用你的代码的这一部分 .

    if(!(Thread.getDefaultUncaughtExceptionHandler() instanceof CustomExceptionHandler)) {
            Thread.setDefaultUncaughtExceptionHandler(new CustomExceptionHandler(this));
        }
    

    根据我的phpscript,使用这个被覆盖的CustomExceptionHandler类(rrainn) .

    package com.vxmobilecomm.activity;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.io.StringWriter;
    import java.io.Writer;
    import java.lang.Thread.UncaughtExceptionHandler;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpResponse;
    import org.apache.http.NameValuePair;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.entity.UrlEncodedFormEntity;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.entity.BufferedHttpEntity;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.apache.http.message.BasicNameValuePair;
    
    import android.app.Activity;
    import android.content.Context;
    import android.content.pm.ApplicationInfo;
    import android.content.pm.PackageManager;
    import android.content.pm.PackageManager.NameNotFoundException;
    import android.os.AsyncTask;
    import android.util.Log;
    
    public class CustomExceptionHandler implements UncaughtExceptionHandler {
    
        private UncaughtExceptionHandler defaultUEH;
        public static String sendErrorLogsTo = "tushar.pandey@virtualxcellence.com" ;
    
        Activity activity;
    
        public CustomExceptionHandler(Activity activity) {
            this.defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
            this.activity = activity;
        }
    
        public void uncaughtException(Thread t, Throwable e) {
    
            final Writer result = new StringWriter();
            final PrintWriter printWriter = new PrintWriter(result);
            e.printStackTrace(printWriter);
            String stacktrace = result.toString();
            printWriter.close();
            String filename = "error" + System.nanoTime() + ".stacktrace";
    
            Log.e("Hi", "url != null");
            sendToServer(stacktrace, filename);
    
            StackTraceElement[] arr = e.getStackTrace();
            String report = e.toString() + "\n\n";
            report += "--------- Stack trace ---------\n\n";
            for (int i = 0; i < arr.length; i++) {
                report += "    " + arr[i].toString() + "\n";
            }
            report += "-------------------------------\n\n";
    
            report += "--------- Cause ---------\n\n";
            Throwable cause = e.getCause();
            if (cause != null) {
                report += cause.toString() + "\n\n";
                arr = cause.getStackTrace();
                for (int i = 0; i < arr.length; i++) {
                    report += "    " + arr[i].toString() + "\n";
                }
            }
            report += "-------------------------------\n\n";
    
            defaultUEH.uncaughtException(t, e);
        }
    
        private void sendToServer(String stacktrace, String filename) {
            AsyncTaskClass async = new AsyncTaskClass(stacktrace, filename,
                    getAppLable(activity));
            async.execute("");
        }
    
        public String getAppLable(Context pContext) {
            PackageManager lPackageManager = pContext.getPackageManager();
            ApplicationInfo lApplicationInfo = null;
            try {
                lApplicationInfo = lPackageManager.getApplicationInfo(
                        pContext.getApplicationInfo().packageName, 0);
            } catch (final NameNotFoundException e) {
            }
            return (String) (lApplicationInfo != null ? lPackageManager
                    .getApplicationLabel(lApplicationInfo) : "Unknown");
        }
    
        public class AsyncTaskClass extends AsyncTask<String, String, InputStream> {
            InputStream is = null;
            String stacktrace;
            final String filename;
            String applicationName;
    
            AsyncTaskClass(final String stacktrace, final String filename,
                    String applicationName) {
                this.applicationName = applicationName;
                this.stacktrace = stacktrace;
                this.filename = filename;
            }
    
            @Override
            protected InputStream doInBackground(String... params) 
            { 
                HttpClient httpclient = new DefaultHttpClient();
                HttpPost httppost = new HttpPost(
                        "http://suo-yang.com/books/sendErrorLog/sendErrorLogs.php?");
    
                Log.i("Error", stacktrace);
    
                try {
                    List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(
                            6);
    
                    nameValuePairs.add(new BasicNameValuePair("data", stacktrace));
                    nameValuePairs.add(new BasicNameValuePair("to",sendErrorLogsTo));
                    nameValuePairs.add(new BasicNameValuePair("subject",applicationName));
    
                    httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
    
                    HttpResponse response = httpclient.execute(httppost);
    
                    HttpEntity entity1 = response.getEntity();
    
                    BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(
                            entity1);
    
                    is = bufHttpEntity.getContent();
    
                } catch (ClientProtocolException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
                return is;
            }
    
            @Override
            protected void onPostExecute(InputStream result) {
                super.onPostExecute(result);
    
                Log.e("Stream Data", getStringFromInputStream(is));
            }
        }
    
        // convert InputStream to String
        private static String getStringFromInputStream(InputStream is) {
    
            BufferedReader br = null;
            StringBuilder sb = new StringBuilder();
    
            String line;
            try {
    
                br = new BufferedReader(new InputStreamReader(is));
                while ((line = br.readLine()) != null) {
                    sb.append(line);
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (br != null) {
                    try {
                        br.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            return sb.toString();
    
        }
    }
    
  • 1

    好吧,我看了提供的样品rrainn和Soonil,我发现了一个解决方案,它不会搞错误处理 .

    我修改了CustomExceptionHandler,因此它存储了我们与新关联的Thread中的原始UncaughtExceptionHandler . 在新的“uncaughtException”结束时 - 方法我只使用存储的UncaughtExceptionHandler调用旧函数 .

    在DefaultExceptionHandler类中,你需要某事 . 像这样:

    public class DefaultExceptionHandler implements UncaughtExceptionHandler{
      private UncaughtExceptionHandler mDefaultExceptionHandler;
    
      //constructor
      public DefaultExceptionHandler(UncaughtExceptionHandler pDefaultExceptionHandler)
      {
           mDefaultExceptionHandler= pDefaultExceptionHandler;
      }
      public void uncaughtException(Thread t, Throwable e) {       
            //do some action like writing to file or upload somewhere         
    
            //call original handler  
            mStandardEH.uncaughtException(t, e);        
    
            // cleanup, don't know if really required
            t.getThreadGroup().destroy();
      }
    }
    

    通过对http://code.google.com/p/android-remote-stacktrace代码的修改,您可以在将字段登录到Web服务器或SD卡时拥有良好的工作基础 .

  • 3

    使用它来捕获异常细节:

    String stackTrace = Log.getStackTraceString(exception);
    

    将其存储在数据库中并维护日志 .

  • 3

    这非常粗野,但可以在任何地方运行logcat,所以快速而肮脏的黑客是添加到任何catch块 getRuntime().exec("logcat >> /sdcard/logcat.log");

  • 0

    虽然此页面上的许多答案都很有用,但它们很容易变得过时 . AppBrain网站汇总了统计数据,使您可以找到当前最流行的崩溃报告解决方案:

    Android crash reporting libraries

    您可以看到,在发布此图片时,Crashlytics用于5.24%的应用程序和12.38%的安装 .

  • 4

    这个安卓库名为Sherlock . 它为您提供崩溃的完整报告以及设备和应用程序信息 . 每当发生崩溃时,它会在通知栏中显示通知,并在单击通知时打开崩溃详细信息 . 您还可以通过电子邮件或其他共享选项与他人共享崩溃详细信息 .

    Installation

    android {
        dataBinding {
          enabled = true
        }
    }
    
    compile('com.github.ajitsing:sherlock:1.0.0@aar') {
        transitive = true
    }
    

    Demo

  • 0

    迟到了,我支持并相信ACRA是最好的选择 . 它易于设置和配置 . 我创建了一个详细的指南,其中包含来自各地的输入,使用ACRA获取崩溃报告,并使用MandrillAp将其邮寄到我的电子邮件地址 .

    链接到帖子:https://androidician.wordpress.com/2015/03/29/sending-crash-reports-with-acra-over-email-using-mandrill/

    链接到github上的示例项目:https://github.com/ayushhgoyal/AcraSample

  • 0

    您可以尝试ACRA (Application Crash Report for Android)库:

    ACRA是一个库,使Android应用程序能够自动将崩溃报告发布到GoogleDoc表单 . 它面向Android应用程序开发人员,帮助他们在崩溃或行为错误时从应用程序中获取数据 .

    它很容易在您的应用中安装,高度可配置,不需要您在任何地方托管服务器脚本...报告将发送到Google Doc电子表格!

  • 5

    我在这里制作了自己的版本:http://androidblogger.blogspot.com/2009/12/how-to-improve-your-application-crash.html

    这基本上是一回事,但我使用的是邮件而不是http连接来发送报告,更重要的是,我在报告中添加了一些信息,如应用程序版本,操作系统版本,手机型号或可用内存 . .

  • 8

    使用 Thread.setDefaultUncaughtExceptionHandler() 可以处理这些异常,但这似乎与Android处理异常的方法相混淆 . 我试图使用这种性质的处理程序:

    private class ExceptionHandler implements Thread.UncaughtExceptionHandler {
        @Override
        public void uncaughtException(Thread thread, Throwable ex){
            Log.e(Constants.TAG, "uncaught_exception_handler: uncaught exception in thread " + thread.getName(), ex);
    
            //hack to rethrow unchecked exceptions
            if(ex instanceof RuntimeException)
                throw (RuntimeException)ex;
            if(ex instanceof Error)
                throw (Error)ex;
    
            //this should really never happen
            Log.e(Constants.TAG, "uncaught_exception handler: unable to rethrow checked exception");
        }
    }
    

    但是,即使重新抛出异常,我也无法获得所需的行为,即记录异常,同时仍然允许Android关闭它发生的组件,所以我过了一会儿就放弃了 .

  • 54

    如果你想立即回答你可以使用logcat

    $adb shell logcat -f /sdcard/logoutput.txt *:E

    如果您的日志中有太多垃圾,请先尝试清除它 .

    $adb shell logcat -c

    然后尝试运行您的应用程序,然后再次登录 .

  • 18

    我是Bugsnag的创始人之一,我们正是为此目的而设计的 . Bugsnag会自动捕获Android应用中的未处理异常并将其发送到我们的仪表板,您可以在其中优先处理修复并深入了解诊断信息 .

    在选择或构建崩溃报告系统时,需要考虑以下一些重要事项,以及一些代码段:

    • 自动检测未处理的异常(example code

    • 收集诊断数据,如内存使用情况,设备信息等(example code

    • 有效地根据原因将组崩溃在一起

    • 允许您跟踪用户在每次崩溃前执行的操作以帮助重现(example code

    如果你想在Android上看到关于崩溃处理/报告的一些最佳实践,你可以查看完全开源的Bugsnag的crash reporting library的完整源代码,随意拆开它并在你自己的应用程序中使用它!

  • 0

    如果您的应用被其他人下载并在远程设备上崩溃,您可能需要查看Android错误报告库(在this SO post中引用) . 如果在崩溃发生时's just on your own local device, you can use LogCat. Even if the device wasn' t连接到主机,连接设备并发出adb logcat命令将下载整个logcat历史记录(至少在缓冲的范围内,这通常是日志数据的一个,它's just not infinite). Do either of those options answer your question? If not can you attempt to clarify what you'再找一点?

  • 19

    现在Firebase Crash报告非常流行并且更易于使用 . 有关更多信息,请参阅以下链接:Firebase Crash Reporting

    希望它会对你有所帮助 .

  • 24

    你也可以试试BugSense . BugSense收集并分析了所有崩溃报告,并为您提供有意义的可视化报告 . 它's free and it' s只有1行代码才能集成 .

    免责声明:我是联合创始人

  • 14

    我一直在为我的Android和iOS应用程序使用Crittercism - 在techcrunch上听说过它们 . 到目前为止,他们非常满意!

  • 284

    对于备用崩溃报告/异常跟踪服务,请查看Raygun.io - 它有一堆很好的逻辑来处理Android崩溃,包括将其插入应用程序时的体面用户体验(主要活动中的两行代码和几行代码) XML粘贴到AndroidManifest中) .

    当您的应用程序崩溃时,它会自动获取堆栈跟踪,硬件/软件的环境数据,用户跟踪信息,您指定的任何自定义数据等 . 它以异步方式将其发布到API,因此不会阻止UI线程,并将其缓存如果没有可用的网络,请访问磁盘 .

    免责声明:我 Build 了Android提供商:)

  • 42

    在Android 2.2中,现在可以从Android Market应用程序自动获取崩溃报告:

    Android Market应用程序的新错误报告功能使开发人员能够从其用户接收崩溃和冻结报告 . 登录发布商帐户后即可使用这些报告 .

    http://developer.android.com/sdk/android-2.2-highlights.html

  • 5

    我找到了一个很棒的Web应用程序来跟踪错误报告 .

    https://mint.splunk.com/

    配置的步骤很少 .

    • 使用上述链接登录或注册和配置 . 完成创建应用程序后,他们将提供如下配置的行 .

    Mint.initAndStartSession(YourActivity.this,“api_key”);

    • 在应用程序的build.gradl中添加以下内容 .

    android {
    ...
    存储库{
    maven {url“https://mint.splunk.com/gradle/”}
    }
    ...
    }

    依赖{
    ...
    编译“com.splunk.mint:mint:4.4.0”
    ...
    }

    • 添加我们上面复制的代码并将其添加到每个活动中 .

    Mint.initAndStartSession(YourActivity.this,“api_key”);

    而已 . 您登录并转到应用程序仪表板,您将收到所有错误报告 .

    希望它可以帮到某人 .

  • 344

    有一个名为fabric的工具,这是一个崩溃分析工具,它允许您在实时部署应用程序和开发期间获取崩溃报告 . 将此工具添加到您的应用程序也很简单 . 当您的应用程序崩溃时,可以从fabric.io仪表板查看崩溃报告 . 报告被自动捕获 . 它不会要求用户许可 . 他/她是否想要发送错误/崩溃报告 . 这是完全免费的...... https://get.fabric.io/

相关问题