获取崩溃日志并通过电子邮件发送

5

经过我的搜索,我找到了以下代码来获取崩溃日志。

try {
      Process process = Runtime.getRuntime().exec("logcat -d");
      BufferedReader bufferedReader = new BufferedReader(
      new InputStreamReader(process.getInputStream()));

      StringBuilder log=new StringBuilder();
      String line;
      while ((line = bufferedReader.readLine()) != null) 
      {
        log.append(line);
      }

但是我应该在哪里添加代码,以便在我的应用程序崩溃时获得崩溃报告。

此外,我想在应用程序崩溃后将其发送电子邮件或发送到服务器,但如何调用发送电子邮件/ HTTP POST方法的操作?

请给予建议,提前致谢。


这个SO问题可能会给你一些关于你的需求的想法。 - Lukasz 'Severiaan' Grela
如果您想要崩溃报告,有许多免费的解决方案,例如Crashlytics(http://www.crashlytics.com/)或开源解决方案ARCA(https://code.google.com/p/acra/)。 - Hein
7个回答

19

处理崩溃日志的最佳方法是创建一个 UncaughtExceptionHandler,并根据您的需求进行处理。创建一个 BaseActivity 类,并将所有活动与其扩展,并将这些代码放入 BaseActivity 类中。

private Thread.UncaughtExceptionHandler handleAppCrash = 
                                         new Thread.UncaughtExceptionHandler() {
        @Override
        public void uncaughtException(Thread thread, Throwable ex) {
            Log.e("error", ex.toString());
            //send email here
        }
    };

只需在BaseActivityonCreate()方法中启用它,方法是使用:

Thread.setDefaultUncaughtExceptionHandler(handleAppCrash);

所以,现在无论何时应用程序崩溃,uncaughtException()都会被调用,然后您需要相应地处理该崩溃。


1
我该如何在我的活动中“启用”它? - Rod

3

我建议您使用ARCAhttps://github.com/ACRA/acra

请在build.gradle中加入arca--自10月29日起,它采用了Apache 2.0许可证。

compile 'ch.acra:acra:4.9.0' //TODO:  Apache 2.0 license https://github.com/ACRA/acra

在扩展Application的类中,在“class”声明的顶部添加以下内容。
@ReportsCrashes(mailTo = "someone@somewhere.com",
        customReportContent = { ReportField.APP_VERSION_CODE, ReportField.APP_VERSION_NAME, ReportField.ANDROID_VERSION, ReportField.PHONE_MODEL, ReportField.CUSTOM_DATA, ReportField.STACK_TRACE, ReportField.LOGCAT },
        mode = ReportingInteractionMode.TOAST,
        resToastText = R.string.resToastText) //you get to define resToastText
public class MyApplication extends Application {

然后在同一Application类中覆盖以下方法:

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);

    // The following line triggers the initialization of ACRA
    ACRA.init(this);
}

2

在我的情况下,我有一个bug,在我的手机上无法复制。我只想从一个孤独的测试人员那里获得堆栈跟踪。我找到的最简单的方法是将它复制到用户的剪贴板中,并要求他们将其发送给我。以下是代码:

import android.app.Application;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;

import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * Copies the stack trace the exception that is causing your application to crash into the clip board.
 * Ask your testers to paste it into an email / text message to you.
 *
 * @author Stuart Clark
 */

public class CrashDebugApplication extends Application {
  @Override
  public void onCreate() {
    super.onCreate();

    Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
      @Override
      public void uncaughtException(Thread thread, Throwable e) {
        // Get the stack trace.
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        e.printStackTrace(pw);

        // Add it to the clip board and close the app
        ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clip = ClipData.newPlainText("Stack trace", sw.toString());
        clipboard.setPrimaryClip(clip);
        System.exit(1);
      }
    });

  }
}

然后在Android清单文件中设置android:name属性,例如:

<application android:icon="@mipmap/ic_launcher" android:name=".CrashDebugApplication">

如果您没有这个资源,请删除“R.style.MyDialogTheme”。 - A T - student

2
这里是关于如何设置自己的崩溃报告的完整说明,当您的应用程序再次启动并要求用户通过电子邮件发送日志时,它将向用户显示如下对话框:

enter image description here

1- 创建一个名为UnexpectedCrashSaver的类:
public class UnexpectedCrashSaver implements Thread.UncaughtExceptionHandler {
private Thread.UncaughtExceptionHandler defaultUEH;
private Context app = null;
public UnexpectedCrashSaver(Context app) {
    this.defaultUEH = Thread.getDefaultUncaughtExceptionHandler();
    this.app = app;
}
public void uncaughtException(Thread t, Throwable e) {
    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";
    App.instance().toastShort("saving!");

    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";
    try {
        FileOutputStream trace = app.openFileOutput("stack.trace",
                Context.MODE_PRIVATE);
        trace.write(report.getBytes());
        trace.close();
    } catch(IOException ioe) {
        // ...
    }
    defaultUEH.uncaughtException(t, e);
}
}

2- 在Application类的onCreate()方法中添加以下代码:

        Thread.setDefaultUncaughtExceptionHandler(new UnexpectedCrashSaver(this));

如果您没有应用程序类,请将以下代码添加到所有活动的onCreate()方法中:(如果您有BaseActivity,请将其放在BaseActivity的onCreate()方法中)

Thread.setDefaultUncaughtExceptionHandler(new UnexpectedCrashSaver(ActivityName.this));

3- 创建一个名为checkbox.xml的布局:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<CheckBox
    android:id="@+id/checkbox"
    style="?android:attr/textAppearanceMedium"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp" />
 </FrameLayout>

4- 将以下方法添加到你的 MainActivity 类中:

 private void checkForCrash()
{
    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
    boolean crash_never_ask_again = preferences.getBoolean("crash_never_ask_again", false);
    if(crash_never_ask_again)//Previously user check the checkbox of never ask me again about sending crash dialog
        return;
    String dialog_message = "In the last run, the program encountered an error, we apologize for this, you can kindly send us the error information to fix this error in future updates.";
    String button_positive_text = "Send";
    String button_negative_text = "Close";
    String checkbox_text = "Never ask again";
    String email = "crashreport@example.com";

    String line;
    String trace="";
    try {
        BufferedReader reader = new BufferedReader(new InputStreamReader(MainActivity.this.openFileInput("stack.trace")));
        while((line = reader.readLine()) != null) {
            trace += line+"\n";
        }
    } catch(FileNotFoundException fnfe) {
        // ...
    } catch(IOException ioe) {
        // ...
    }
    if(trace.length() < 10)//We didn't have any crash
        return;

    View checkBoxView = View.inflate(this, R.layout.checkbox, null);
    CheckBox checkBox =  checkBoxView.findViewById(R.id.checkbox);
    checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            SharedPreferences.Editor editor = preferences.edit();
            editor.putBoolean("checkbox_checked",true);
            editor.apply();
        }
    });
    checkBox.setText(checkbox_text);

    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
    builder.setCancelable(true);
    //builder.setIcon(R.drawable.ic_setting);
    builder.setMessage(dialog_message);
    builder.setView(checkBoxView);
    builder.setCancelable(false);
    String finalTrace = trace;
    builder.setPositiveButton(button_positive_text, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Intent sendIntent = new Intent(Intent.ACTION_SEND);
            String subject = "Error report";
            String body = "Mail this to appdeveloper@gmail.com: " + "\n" + finalTrace + "\n";

            sendIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {email});
            sendIntent.putExtra(Intent.EXTRA_TEXT, body);
            sendIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
            sendIntent.setType("message/rfc822");
            MainActivity.this.startActivity(Intent.createChooser(sendIntent, "Title:"));
            MainActivity.this.deleteFile("stack.trace");
        }
    });
    builder.setNegativeButton(button_negative_text, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            MainActivity.this.deleteFile("stack.trace");
            boolean checkbox_checked = preferences.getBoolean("checkbox_checked", false);
            if(checkbox_checked)
            {
                SharedPreferences.Editor editor = preferences.edit();
                editor.putBoolean("crash_never_ask_again",true);
                editor.apply();
            }
                dialog.dismiss();
        }
    });
    AlertDialog alert = builder.create();
    alert.show();

}

5- 在MainActivity的onCreate方法中调用第4步创建的方法:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    checkForCrash();
    setContentView(R.layout.activity_main);
    //...

这就是全部啦!


0
创建BaseActivity
public class BaseActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_base);
    Thread.setDefaultUncaughtExceptionHandler(handleCrash);
}

private Thread.UncaughtExceptionHandler handleCrash =
        new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread thread, Throwable e) {
                Log.e("error", e.toString());
                //send email here
            }
        };

}

请注意,BaseActivity 正在扩展 AppCompatActivity。而 YourActivity 正在扩展 BaseActivity
public class YourActivity extends BaseActivity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_your);
     //Basically your activity is just extending BaseActivity, and all exceptions are 
     //are taken care of in BaseActivity.
 }
}

现在,当您的应用程序中的任何活动检测到未处理的异常时,它将在BaseActivity中被检测到,然后您可以将错误报告发送到您的电子邮件。

0

看一下这个项目。LINK 这是一个小的项目,可以将堆栈跟踪信息发送到您的服务器,以便您在自己的服务器上获取它们。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接