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


737

如何从Android应用程序获取崩溃数据(至少是堆栈跟踪)?至少在使用电缆检索我自己的设备上工作时,但理想情况下,该应用程序可以在野外运行的任何应用程序实例中运行,以便我可以对其进行改进并使其更牢固。



我看到这会将报告发送到远程服务器。它也可以将异常记录到本地文件吗?

1
适用于Android的应用程序崩溃报告code.google.com/p/acra
gdonald 2011年

这个看起来更健壮,如果在所有重试之后报告都无法上传,那可以将其记录为文件还是sqlite数据库呢?
JPM 2012年

Answers:


354

您可以尝试ACRA(Android应用程序崩溃报告)库:

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

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


8
这很容易设置和使用。建议用于上市前使用,甚至可能以后使用。
mxcl 2010年

1
开始使用它,它比我以前使用Flurry或我自己编写的自制错误报告好得多。到目前为止,我对“ acra”很着迷。
Adrian Spinei 2011年

8
Acra的最大优点是可以使用Google API轻松分析和可视化数据,有关如何执行此操作的示例,请参见jberkel.github.com/sms-backup-plus/acra-analysis
Jan Berkel

3
对我来说似乎很不稳定。ACRA本身崩溃了,并发送了有关其自身的崩溃报告,而不是有关的应用程序崩溃。-1
桑多尔

19
不再支持将Google Docs作为后端
eliocs 2014年

305

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

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

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");
?>

9
我认为这会在某些州/国家引起法律问题
setzamora 2011年

5
注意:HttpPost httpPost = new HttpPost(url);如果要定位蜂窝或更高版本,则现在必须处于异步任务(或处理程序...单独的线程)中
Bryan Denny

4
@Joset,什么样的法律问题,为什么?
varevarao 2012年

2
@varevarao可能存在法律问题,因为您正在使用此代码的“发送到服务器”功能在未经用户同意的情况下发送敏感的设备信息。
caw 2012年

2
defaultExceptionHandler似乎在Activity重新创建后仍然存在。我将答案更新为仅在尚未设置自定义处理程序的情况下才设置默认值。没有它,每个处理程序将保留并调用前一个,直到原始。这会导致重复日志记录问题以及内存泄漏问题。
Dave McClelland 2013年

57

您也可以尝试[BugSense] 原因:垃圾邮件重定向到另一个URL。BugSense收集并分析了所有崩溃报告,并为您提供有意义的可视化报告。它是免费的,并且只有1行代码可以集成。

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


4
我已经尝试过BugSense及其超赞的功能。简单而新鲜的UI,也非常快。没有愚蠢的功能膨胀。很容易看到最常见的崩溃,也可以深入研究堆栈跟踪。
vidstige 2012年

我们最近做的不仅仅是崩溃报告更加但这不是谈新功能等合适的地方
PanosJee

试过了 尽管stacktrace不太完整,但仍能解决错误,但实时崩溃数据并未通过。调试模式无效。不过,我从Application类开始采用了一次一次初始化的方法(这在大多数情况下是有意义的)。BugSense有一个了不起的仪表板,真是太可惜了,由于某种原因,崩溃报告不起作用,并且符号不在免费层中。我花了5分钟时间安装了GetSentry,它与这个通用客户端一起可以在Android上立即使用:github.com/joshdholtz/Sentry-Android
albertpeiro 2014年

3
BugSense非常简单,易于集成。但是太贵了。使用Flurry分析和解析,我们可以实现相同的错误检测功能。两者都是免费的,易于集成。
venkat 2015年

44

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

Android Market应用程序的新错误报告功能使开发人员能够接收崩溃并冻结其用户的报告。当他们登录其发布者帐户时,这些报告将可用。

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


我认为这不仅是2.2,而是Google提供的一项新的市场功能。几天前,我收到了一份崩溃报告,使用我的应用程序应该不应该有froyo设备。
Janusz

1
@Janusz确定吗?已经有适用于Nexus One的Froyo发行版,还不包括运行Froyo已有一段时间的Google员工。
pupeno

至少手机上的版本必须有更新,甚至只是另一个修订,但是否则该如何工作?
RoflcoptrException

我不知道这些报告是否之前已经发送给Google了。该报告有些奇怪,因为google显示了用于发送其视频的UI,这必须是操作系统的变化,而不仅仅是市场的变化。但是它说:Platforms 1每周报告一次报告,并且机器人不应处于活动状态。
Janusz

1
“报告”按钮代码已经存在于AOSP中一段时间​​了,几个月前,罗曼·盖伊(隐约地)在这里回答了有关此问题的问题。
Christopher Orr 2010年

30

可以使用来处理这些异常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关闭发生了该事件的组件,因此一段时间后我放弃了。


为什么只重新抛出未经检查的异常?在我看来,您应该重新抛出所有异常。
MatrixFrog 2010年


1
诀窍是获取先前的默认UncaughtExceptionHandler并在完成报告异常后处理该对象的异常。
汤姆(Tom)2010年

Constants.TAGAndroid框架的一部分吗?第一次看到它。似乎找不到它。
Haroun Hajem '18

1
@HarounHajem我们需要定义我们自己的。
KYHSGeekCode

22

我发现这个问题太旧了,希望我的回答对其他有同样问题的人有所帮助...

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


21

好的,我查看了rrainn和Soonil提供的示例,发现了一种不会弄乱错误处理的解决方案。

我修改了CustomExceptionHandler,以便它存储与我们关联的新线程中的原始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上的代码进行的修改, 您将拥有一个良好的工作基础,可用于将该字段登录到您的网络服务器或sd卡。


19

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

在此处输入图片说明


2
您如何让用户发送报告,或者如果您的应用不在Play商店中,报告将如何工作?您是否可以将其用于测试?
Lion789

您是否知道有公开其崩溃报告的开源Android项目?
贾斯汀·奇维

1
请注意,这将仅显示用户选择报告的崩溃和ANR
sojin

是的,的确,无论如何,当您拥有庞大的用户群时,都有很多报告所有崩溃的可能性,并且崩溃和此处报告的ANR都是最重要的,因为人们会报告更多次,因此您可以过滤并按优先级修复它们(更多报告=更重要的错误),同时(无报告=不是那个严重的错误)。
Eefret

18

我一直在为Android和iOS应用程序使用Crittercism-在techcrunch上听说过它们。到目前为止对他们还很满意!


仅供参考:NDK方面的崩溃报告不是基本计划的一部分,而是在“扩展崩溃报告”下。参见:crittercism.com/pricing
ahash

1
我刚刚在Twitter的Fabric.io Crashlytics上测试了NDK报告...很棒(在使用的第一天),当我发布将映射放到他们的系统中时,它需要一个额外的步骤,因此堆栈跟踪看起来不错,但是很麻烦我用来测试的NDK错误(策略性地在整个c ++中进行了“ abort();”调用,它为测试提供了堆栈跟踪。)
Hunter-Orionnoir


11

使用它来捕获异常详细信息:

String stackTrace = Log.getStackTraceString(exception); 

将此存储在数据库中并维护日志。


如何访问该数据库,请给我任何示例。如果可以的话请。感谢advacne
Rajendra Verma

8

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

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

免责声明:我是Polidea的CTO,也是该服务的共同创建者。


7

感谢您提供的Stackoverflow帮助我找到此答案的资源。

您可以直接在电子邮件中找到远程Android崩溃报告。记住,您必须将电子邮件放入CustomExceptionHandler类中

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

所需步骤:

1)在活动的onCreate中使用此部分代码。

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

2)根据我的phpscript,使用(rrainn)的CustomExceptionHandler类的此重写版本。

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();

    }
}


5

这非常残酷,但是可以在任何地方运行logcat,因此要在任何catch块中添加快速而肮脏的hack。 getRuntime().exec("logcat >> /sdcard/logcat.log");


这将提供所有应用程序的日志输出。按应用程序标记名进行过滤可能没问题,但不要认为这是一个好方法,因为条目可能是累积的。每次写入后清除logcat输出可能可以解决该问题。
bschandramohan 2011年

5

有一个称为架构的工具,这是一个崩溃分析工具,当您实时部署应用程序时以及在开发过程中,它将使您能够获取崩溃报告。将这个工具添加到您的应用程序也很简单。当您的应用程序崩溃时,可以从fabric.io仪表板查看崩溃报告。该报告被自动捕获。它不会征求用户的许可。他/她是否要发送错误/崩溃报告。这是完全免费的... https://get.fabric.io/


5

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()));
}

5

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

安装

android {
    dataBinding {
      enabled = true
    }
}

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

演示版

在此处输入图片说明


5

尽管此页面上的许多答案很有用,但很容易使它们过时。AppBrain网站汇总了统计信息,可让您找到当前最受欢迎的崩溃报告解决方案:

Android崩溃报告库

应用大脑网站

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


4

我们在公司内部使用我们自己的系统,它为我们提供了很好的服务。这是一个Android库,用于将崩溃报告发送到服务器以及接收报告并进行一些分析的服务器。服务器按异常名称,堆栈跟踪,消息将异常分组。它有助于确定需要解决的最关键的问题。我们的服务现在处于公开测试阶段,因此每个人都可以尝试。您可以在http://watchcat.co上创建帐户,也可以使用演示访问http://watchcat.co/reports/index.php?demo看看它的工作方式。


3

如果您要立即回答,可以使用logcat

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

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

$adb shell logcat -c

然后尝试运行您的应用,然后再次运行logcat。


这是否还不需要您将设备连接到PC(例如,使用电缆)?
杰拉德2013年

3

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

https://mint.splunk.com/

配置步骤少。

  1. 使用以上链接登录或注册并配置。创建完应用程序后,它们将提供一行以进行如下配置。
Mint.initAndStartSession(YourActivity.this, "api_key");
  1. 在应用程序的build.gradl中添加以下内容。
android {
...
    repositories {
        maven { url "https://mint.splunk.com/gradle/"}
    }
...
}

dependencies {
...
    compile "com.splunk.mint:mint:4.4.0"
...
}
  1. 添加我们上面复制的代码,并将其添加到每个活动中。

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

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

希望它可以帮助某人。


这个多少钱?
user2966445 '16

2

有关备用崩溃报告/异常跟踪服务的信息,请查看Raygun.io-它具有许多处理Android崩溃的好方法,包括将其插入应用程序时的体面用户体验(主Activity中有两行代码,粘贴到AndroidManifest中的XML行)。

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

免责声明:我建立了Android提供程序:)


1

刚开始使用ACRA https://github.com/ACRA/acra(使用Google Forms作为后端),并且设置和使用起来非常容易,这是默认设置。

但不建议将报告发送到Google表单(然后删除): https : //plus.google.com/118444843928759726538/posts/GTTgsrEQdN6 https://github.com/ACRA/acra/wiki/Notice-on-Google -表单电子表格用法

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

只需花费很少的精力,就可以将报告发送到Bugsense:http : //www.bugsense.com/docs/android#acra

注意:无错误的免费帐户每月最多只能报告500次


1

晚会晚了,我支持并相信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


1

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

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

  • 自动检测未处理的异常(示例代码
  • 收集诊断数据,例如内存使用情况,设备信息等(示例代码
  • 有效地将组按根本原因一起崩溃
  • 允许您跟踪用户在每次崩溃前采取的措施以帮助重现(示例代码

如果您想查看有关Android上的崩溃处理/报告的一些最佳实践,可以查看Bugsnag的崩溃报告库的完整源代码,该是完全开源的,请随时将其拆开并在您自己的应用程序中使用!


0

如果您的应用正在被其他人下载并在远程设备上崩溃,则您可能需要研究一个Android错误报告库(在本SO文章中引用)。如果只是在您自己的本地设备上,则可以使用LogCat。即使当崩溃发生时设备没有连接到主机,连接设备并发出adb logcat命令也会下载整个logcat历史记录(至少在缓冲的范围内,这通常是大量的日志数据) ,它不是无限的)。这些选项都可以回答您的问题吗?如果不能,您是否可以尝试澄清您的需求?



0

Google更改了您实际获得的崩溃报告数量。以前,您只有手动报告的错误报告。

自从上次开发者大会和Android Vitals推出以来,您还获得了允许共享诊断数据的用户崩溃报告。

您会看到从用户选择自动共享使用情况和诊断数据的Android设备收集的所有崩溃。提供前两个月的数据。

查看崩溃和应用程序无响应(ANR)错误


logcat表示/ data / user / 0 / com中有一个输出崩溃转储文件。出如何访问该文件!
迈克尔·

0

您可以直接在Android Studio中执行此操作。只需连接您的手机,运行该应用程序,然后使其崩溃,您便可以直接在Android Studio中查看stacktrace。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.