【问题标题】:Getting the crash log and send it as email获取崩溃日志并将其作为电子邮件发送
【发布时间】:2013-12-05 11:55:45
【问题描述】:

从我的搜索中,我得到了以下用于获取崩溃日志的代码。

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 方法。

请提前告知和感谢。

【问题讨论】:

标签: android email crash


【解决方案1】:

处理崩溃日志的最佳方法是创建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() 将被调用,您必须相应地处理崩溃。

【讨论】:

  • 如何在我的活动中“启用”它?
【解决方案2】:

我建议你使用 ARCA https://github.com/ACRA/acra

在你的 build.gradle 中包含 arca——它使用 apache 2.0 许可证,截至 10 月 29 日。

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

在扩展 Application 的类中,将其放在“类”声明的顶部。

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

【讨论】:

    【解决方案3】:

    看看这个项目。 LINK 将堆栈跟踪发布到您的服务器是一个小项目,因此您可以将它们放在自己的服务器上。

    【讨论】:

      【解决方案4】:

      我还没有尝试过,但是查看它会给出当前日志而不是崩溃报告 参考How do I obtain crash-data from my Android application?

      【讨论】:

        【解决方案5】:

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

        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 Manifesto 中设置android:name 属性,即

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

        【讨论】:

          【解决方案6】:

          以下是设置您自己的崩溃报告器的完整说明,当您的应用重新启动时,它将向用户显示这样的对话框,并要求他/她通过电子邮件发送日志:

          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, R.style.MyDialogTheme);
              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方法中调用第四步创建的方法:

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

          就是这样!

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-11-05
            • 1970-01-01
            • 1970-01-01
            • 2012-01-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多