Необходимо обработать неперехваченное исключение и отправить файл журнала
обновление: пожалуйста, см." принятое " решение ниже
когда мое приложение создает необработанное исключение, а не просто завершение, я хотел бы сначала дать пользователю возможность отправить файл журнала. Я понимаю, что делать больше работы после получения случайного исключения рискованно, но, эй, худшее, что приложение завершает сбой, и файл журнала не отправляется. Это оказывается сложнее, чем я ожидал :)
что работает: (1) захват неперехваченное исключение, (2) извлечение информации журнала и запись в файл.
что еще не работает: (3) Запуск активности для отправки электронной почты. В конечном счете, у меня будет еще одна активность, чтобы спросить разрешения пользователя. Если я получаю работу с электронной почтой, я не ожидаю больших проблем для другого.
суть проблемы заключается в том, что необработанное исключение перехватывается в мой класс Application. Поскольку это не деятельность, не очевидно, как начать деятельность с Намерение.ACTION_SEND. То есть, обычно для запуска действия вызывается startActivity и возобновляется с onActivityResult. Эти методы поддерживаются деятельностью, но не приложением.
какие-либо предложения о том, как это сделать?
вот некоторые фрагменты кода в качестве начального руководства:
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[] {"[email protected]"});
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);
}
}
5 ответов:
вот полное решение (почти: я опустил макет пользовательского интерфейса и обработку кнопок) - получено из большого количества экспериментов и различных сообщений от других, связанных с проблемами, которые возникли на этом пути.
есть несколько вещей, которые вам нужно сделать:
- обрабатывать uncaughtException в подклассе приложения.
- после перехвата исключения, запустите новое действие, чтобы попросить пользователя отправить бревно.
- извлечение информации журнала из файлов logcat и напишите на свой свой файл.
- запустите приложение электронной почты, предоставив свой файл в качестве вложения.
- Манифест: отфильтруйте свою активность, чтобы ее распознал обработчик исключений.
- дополнительно должны быть настройки, чтобы убрать бревно.d () и Log.в.)(
Итак, вот подробности:
(1 & 2) обрабатывать uncaughtException, начать отправлять журнал активности:
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 Активность):
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 + "\n"); writer.write ("Device: " + model + "\n"); writer.write ("App version: " + (info == null ? "(null)" : info.versionCode) + "\n"); 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):
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[] {"[email protected]"}); 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 (вам нужно будет добавить пользовательский интерфейс):
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:
в проект.свойства, измените строку конфигурации. Вы должны указать "оптимизировать" или Proguard will не удалить журнал.v () и Log.д() звонки.
proguard.config=${sdk.dir}/tools/proguard/proguard-android-optimize.txt:proguard-project.txtIn proguard-проект.txt добавьте следующее. Расскажите ProGuard, чтобы взять журнал.v и Log.d не имеют побочных эффектов (даже если они делают, так как они пишут в журналы) и, таким образом, могут быть удалены во время оптимизации:
-assumenosideeffects class android.util.Log { public static int v(...); public static int d(...); }вот именно! Если у вас есть какие-либо предложения по улучшению, пожалуйста, дайте мне знать, и я могу обновлять.
сегодня есть много инструментов повторной печати аварии, которые делают это легко.
crashlytics - инструмент отчетности аварии, бесплатно, но дает вам основные отчеты Преимущества: Бесплатно
Gryphonet - более продвинутый инструмент отчетности, требует какой-то платы. Преимущества: легкая рекреация аварий, АНР, медлительность...
Если вы частный разработчик, я бы предложил Crashlytics, но если это большая организация, я бы пошел на Грифонет.
Удачи!
попробуйте вместо этого использовать ACRA-он обрабатывает отправку трассировки стека, а также тонны другой полезной отладочной информации на ваш сервер или в документ Google Docs, который вы настроили.
ответ @ PeriHartman хорошо работает, когда поток пользовательского интерфейса выдает неперехваченное исключение. Я сделал некоторые улучшения, когда неперехваченное исключение выбрасывается потоком без пользовательского интерфейса.
public boolean isUIThread(){ return Looper.getMainLooper().getThread() == Thread.currentThread(); } public void handleUncaughtException(Thread thread, Throwable e) { e.printStackTrace(); // not all Android versions will print the stack trace automatically if(isUIThread()) { invokeLogActivity(); }else{ //handle non UI thread throw uncaught exception new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { invokeLogActivity(); } }); } } private void invokeLogActivity(){ 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 }
хорошо объяснено. Но одно наблюдение здесь, вместо записи в файл с помощью File Writer и Streaming, я использовал опцию logcat-f напрямую. Вот код
String[] cmd = new String[] {"logcat","-f",filePath,"-v","time","<MyTagName>:D","*:S"}; try { Runtime.getRuntime().exec(cmd); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }это помогло мне в промывке последней информации буфера. Использование потоковой передачи файлов дало мне одну проблему, что она не сбрасывала последние журналы из буфера. Но в любом случае, это было действительно полезным руководством. Спасибо.
Comments