需要处理未捕获的异常并发送日志文件 [英] Need to handle uncaught exception and send log file

查看:33
本文介绍了需要处理未捕获的异常并发送日志文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新:请参阅下面的已接受"解决方案

当我的应用程序创建未处理的异常时,而不是简单地终止,我想首先让用户有机会发送日志文件.我意识到在获得随机异常后做更多的工作是有风险的,但是,嘿,最糟糕的是应用程序完成崩溃并且日志文件没有被发送.事实证明这比我预期的要棘手:)

When my app creates an unhandled exception, rather than simply terminating, I'd like to first give the user an opportunity to send a log file. I realize that doing more work after getting a random exception is risky but, hey, the worst is the app finishes crashing and the log file doesn't get sent. This is turning out to be trickier than I expected :)

工作原理:(1) 捕获未捕获的异常,(2) 提取日志信息并写入文件.

What works: (1) trapping the uncaught exception, (2) extracting log info and writing to a file.

什么还不起作用:(3) 开始发送电子邮件的活动.最终,我将有另一个活动来征求用户的许可.如果我让电子邮件活动正常工作,我预计不会给对方带来太多麻烦.

What doesn't work yet: (3) starting an activity to send email. Ultimately, I'll have yet another activity to ask the user's permission. If I get the email activity working, I don't expect much trouble for the other.

问题的症结在于未处理的异常在我的 Application 类中被捕获.由于这不是活动,因此如何使用 Intent.ACTION_SEND 开始活动并不明显.也就是说,通常要启动一个活动,调用 startActivity 并使用 onActivityResult 继续.Activity支持这些方法,Application不支持.

The crux of the problem is that the unhandled exception is caught in my Application class. Since that isn't an Activity, it's not obvious how to start an activity with Intent.ACTION_SEND. That is, normally to start an activity one calls startActivity and resumes with onActivityResult. These methods are supported by Activity but not by Application.

有什么关于如何做到这一点的建议吗?

Any suggestions on how to do this?

这里有一些代码片段作为入门指南:

Here are some code snips as a starting guide:

public class MyApplication extends Application
{
  defaultUncaughtHandler = Thread.getDefaultUncaughtExceptionHandler();
  public void onCreate ()
  {
    Thread.setDefaultUncaughtExceptionHandler (new Thread.UncaughtExceptionHandler()
    {
      @Override
      public void uncaughtException (Thread thread, Throwable e)
      {
        handleUncaughtException (thread, e);
      }
    });
  }

  private void handleUncaughtException (Thread thread, Throwable e)
  {
    String fullFileName = extractLogToFile(); // code not shown

    // The following shows what I'd like, though it won't work like this.
    Intent intent = new Intent (Intent.ACTION_SEND);
    intent.setType ("plain/text");
    intent.putExtra (Intent.EXTRA_EMAIL, new String[] {"me@mydomain.com"});
    intent.putExtra (Intent.EXTRA_SUBJECT, "log file");
    intent.putExtra (Intent.EXTRA_STREAM, Uri.parse ("file://" + fullFileName));
    startActivityForResult (intent, ACTIVITY_REQUEST_SEND_LOG);
  }

  public void onActivityResult (int requestCode, int resultCode, Intent data)
  {
    if (requestCode == ACTIVITY_REQUEST_SEND_LOG)
      System.exit(1);
  }
}

推荐答案

这是完整的解决方案(几乎:我省略了 UI 布局和按钮处理) - 源自大量实验和其他人发布的与出现的问题相关的各种帖子一路向上.

Here's the complete solution (almost: I omitted the UI layout and button handling) - derived from a lot of experimentation and various posts from others related to issues that came up along the way.

您需要做很多事情:

  1. 在您的应用程序子类中处理 uncaughtException.
  2. 捕获异常后,启动一个新的Activity,要求用户发送一个日志.
  3. 从 logcat 的文件中提取日志信息并写入您的自己的文件.
  4. 启动电子邮件应用,以附件形式提供您的文件.
  5. 清单:过滤您的活动以供您的异常处理程序识别.
  6. 或者,设置 Proguard 以去除 Log.d() 和 Log.v().

现在,这里是详细信息:

Now, here are the details:

(1 & 2) 处理 uncaughtException,开始发送日志活动:

(1 & 2) Handle uncaughtException, start send log activity:

public class MyApplication extends Application
{
  public void onCreate ()
  {
    // Setup handler for uncaught exceptions.
    Thread.setDefaultUncaughtExceptionHandler (new Thread.UncaughtExceptionHandler()
    {
      @Override
      public void uncaughtException (Thread thread, Throwable e)
      {
        handleUncaughtException (thread, e);
      }
    });
  }

  public void handleUncaughtException (Thread thread, Throwable e)
  {
    e.printStackTrace(); // not all Android versions will print the stack trace automatically

    Intent intent = new Intent ();
    intent.setAction ("com.mydomain.SEND_LOG"); // see step 5.
    intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK); // required when starting from Application
    startActivity (intent);

    System.exit(1); // kill off the crashed app
  }
}

(3) 提取日志(我把它作为我的 SendLog 活动):

(3) Extract log (I put this an my SendLog Activity):

private String extractLogToFile()
{
  PackageManager manager = this.getPackageManager();
  PackageInfo info = null;
  try {
    info = manager.getPackageInfo (this.getPackageName(), 0);
  } catch (NameNotFoundException e2) {
  }
  String model = Build.MODEL;
  if (!model.startsWith(Build.MANUFACTURER))
    model = Build.MANUFACTURER + " " + model;

  // Make file name - file must be saved to external storage or it wont be readable by
  // the email app.
  String path = Environment.getExternalStorageDirectory() + "/" + "MyApp/";
  String fullName = path + <some name>;

  // Extract to file.
  File file = new File (fullName);
  InputStreamReader reader = null;
  FileWriter writer = null;
  try
  {
    // For Android 4.0 and earlier, you will get all app's log output, so filter it to
    // mostly limit it to your app's output.  In later versions, the filtering isn't needed.
    String cmd = (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) ?
                  "logcat -d -v time MyApp:v dalvikvm:v System.err:v *:s" :
                  "logcat -d -v time";

    // get input stream
    Process process = Runtime.getRuntime().exec(cmd);
    reader = new InputStreamReader (process.getInputStream());

    // write output stream
    writer = new FileWriter (file);
    writer.write ("Android version: " +  Build.VERSION.SDK_INT + "
");
    writer.write ("Device: " + model + "
");
    writer.write ("App version: " + (info == null ? "(null)" : info.versionCode) + "
");

    char[] buffer = new char[10000];
    do 
    {
      int n = reader.read (buffer, 0, buffer.length);
      if (n == -1)
        break;
      writer.write (buffer, 0, n);
    } while (true);

    reader.close();
    writer.close();
  }
  catch (IOException e)
  {
    if (writer != null)
      try {
        writer.close();
      } catch (IOException e1) {
      }
    if (reader != null)
      try {
        reader.close();
      } catch (IOException e1) {
      }

    // You might want to write a failure message to the log here.
    return null;
  }

  return fullName;
}

(4) 启动电子邮件应用程序(也在我的 SendLog 活动中):

(4) Start an email app (also in my SendLog Activity):

private void sendLogFile ()
{
  String fullName = extractLogToFile();
  if (fullName == null)
    return;

  Intent intent = new Intent (Intent.ACTION_SEND);
  intent.setType ("plain/text");
  intent.putExtra (Intent.EXTRA_EMAIL, new String[] {"log@mydomain.com"});
  intent.putExtra (Intent.EXTRA_SUBJECT, "MyApp log file");
  intent.putExtra (Intent.EXTRA_STREAM, Uri.parse ("file://" + fullName));
  intent.putExtra (Intent.EXTRA_TEXT, "Log file attached."); // do this so some email clients don't complain about empty body.
  startActivity (intent);
}

(3 & 4) 这是 SendLog 的样子(不过,您必须添加 UI):

(3 & 4) Here's what SendLog looks like (you'll have to add the UI, though):

public class SendLog extends Activity implements OnClickListener
{
  @Override
  public void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
    requestWindowFeature (Window.FEATURE_NO_TITLE); // make a dialog without a titlebar
    setFinishOnTouchOutside (false); // prevent users from dismissing the dialog by tapping outside
    setContentView (R.layout.send_log);
  }

  @Override
  public void onClick (View v) 
  {
    // respond to button clicks in your UI
  }

  private void sendLogFile ()
  {
    // method as shown above
  }

  private String extractLogToFile()
  {
    // method as shown above
  }
}

(5) 清单:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ... >
    <!-- needed for Android 4.0.x and eariler -->
    <uses-permission android:name="android.permission.READ_LOGS" /> 

    <application ... >
        <activity
            android:name="com.mydomain.SendLog"
            android:theme="@android:style/Theme.Dialog"
            android:textAppearance="@android:style/TextAppearance.Large"
            android:windowSoftInputMode="stateHidden">
            <intent-filter>
              <action android:name="com.mydomain.SEND_LOG" />
              <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
     </application>
</manifest>

(6) 设置 Proguard:

(6) Setup Proguard:

在 project.properties 中,更改配置行.您必须指定优化",否则 Proguard 将删除 Log.v() 和 Log.d() 调用.

In project.properties, change the config line. You must specify "optimize" or Proguard will not remove Log.v() and Log.d() calls.

proguard.config=${sdk.dir}/tools/proguard/proguard-android-optimize.txt:proguard-project.txt

在 proguard-project.txt 中,添加以下内容.这告诉 Proguard 假设 Log.v 和 Log.d 没有副作用(即使它们在写入日志后有副作用),因此可以在优化期间删除:

In proguard-project.txt, add the following. This tell Proguard to assume Log.v and Log.d have no side effects (even though they do since they write to the logs) and thus can be removed during optimization:

-assumenosideeffects class android.util.Log {
    public static int v(...);
    public static int d(...);
}

就是这样!如果您对此有任何改进建议,请告诉我,我可能会对此进行更新.

That's it! If you have any suggestions for improvements to this, please let me know and I may update this.

这篇关于需要处理未捕获的异常并发送日志文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆