Potrzeba obs3ugi nieza3o ¿onego wyjêcia i wys3ania pliku dziennika
Aktualizacja: zobacz "akceptowane" rozwiązanie poniżej
Kiedy moja aplikacja tworzy nieobsługiwany wyjątek, zamiast po prostu zakończyć, chciałbym najpierw dać użytkownikowi możliwość wysłania pliku dziennika. Zdaję sobie sprawę, że robienie więcej pracy po uzyskaniu przypadkowego wyjątku jest ryzykowne, ale najgorsze jest to, że aplikacja kończy się awarią, a plik dziennika nie zostanie wysłany. To okazuje się trudniejsze niż się spodziewałem:)
Co działa: (1) zalewkowanie wyjątku uncaught, (2) wyodrębnianie informacji dziennika i zapis do pliku.
Co jeszcze nie działa: (3) rozpoczęcie aktywności wysyłania wiadomości e-mail. Ostatecznie będę miał jeszcze jedną czynność, aby zapytać użytkownika o zgodę. Jeśli aktywacja poczty e-mail będzie działać, nie spodziewam się większych problemów dla drugiego.
Sednem problemu jest to, że nieobsługiwany wyjątek jest przechwytywany w mojej klasie aplikacji. Ponieważ nie jest to aktywność, nie jest oczywiste, jak rozpocząć działalność z zamiarem.ACTION_SEND. Czyli normalnie do rozpocznij działanie, które nazywa startActivity i wznawia z onActivityResult. Metody te są wspierane przez aktywność, ale nie przez aplikację.
Jakieś sugestie, jak to zrobić?
Oto kilka wycinków kodu jako początkowy Przewodnik:
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 answers
Oto kompletne rozwiązanie (prawie: pominąłem układ interfejsu i obsługę przycisków) - pochodzące z wielu eksperymentów i różnych postów od innych związanych z problemami, które pojawiły się po drodze.
Jest wiele rzeczy, które musisz zrobić:
- obsłuż uncaughtException w podklasie aplikacji.
- po wyłapaniu wyjątku rozpocznij nową aktywność, aby poprosić Użytkownika o wysłanie kłoda.
- Wyodrębnij informacje dziennika z plików logcat i zapisz do Twój własne akta.
- Uruchom aplikację e-mail, dostarczając plik jako załącznik.
- Manifest: filtruj swoją aktywność, aby była rozpoznawana przez obsługę wyjątków.
- Opcjonalnie Ustaw Proguard, aby usunąć dziennik.d () I Log.v ().
Oto szczegóły:
(1 & 2) obsługuje uncaughtException, Uruchom aktywność dziennika wysyłania:
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) 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 + "\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) Uruchom aplikację e-mail (również w moim Sendlogu "Aktywność": {]}
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) Oto jak wygląda SendLog (musisz jednak dodać interfejs użytkownika):
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:
<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) Konfiguracja Proguard:
W projekcie.properties, Zmień linię konfiguracji. Musisz podać "optimize" lub Proguard usunie , a nie .v () i Log.D () wzywa.
proguard.config=${sdk.dir}/tools/proguard/proguard-android-optimize.txt:proguard-project.txt
W proguard-projekt.txt, dodaj następujące. To powiedz Proguardowi, żeby założył Log.v i Log.d nie mają żadnych skutków ubocznych (mimo, że mają, ponieważ piszą do logi) i w ten sposób można je usunąć podczas optymalizacji:
-assumenosideeffects class android.util.Log {
public static int v(...);
public static int d(...);
}
To jest to! Jeśli masz jakieś sugestie dotyczące ulepszeń, daj mi znać, a ja mogę to zaktualizować.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-27 20:25:23
Obecnie istnieje wiele narzędzi do reprtowania awarii, które robią to łatwo.
-
Crashlytics - narzędzie do raportowania awarii, bezpłatne, ale daje podstawowe raporty Zalety: Free
Gryphonet - bardziej zaawansowane narzędzie do raportowania, wymaga pewnego rodzaju opłaty. Zalety: Łatwe odtwarzanie awarii, ANR, powolność...
Jeśli jesteś prywatnym deweloperem, to proponuję Crashlytics, ale jeśli jest to duża organizacja, to pójdę na Gryfonet.
Powodzenia!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-15 09:41:42
Spróbuj użyć zamiast tego ACRA-obsługuje wysyłanie śladu stosu, jak również mnóstwo innych przydatnych informacji debugowania do twojego backendu lub do skonfigurowanego dokumentu Google Docs.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-02 18:13:59
@PeriHartman odpowiedź działa dobrze, gdy wątek UI rzuca nieobciążony wyjątek. Zrobiłem kilka ulepszeń, gdy wyjątek uncaught jest wyrzucany przez wątek non UI.
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
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-18 03:02:19
Ładnie wyjaśnione. Ale jedna uwaga tutaj, zamiast zapisu do pliku za pomocą File Writer i streamingu, skorzystałem bezpośrednio z opcji logcat-F. Oto kod
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();
}
To pomogło mi w spłukiwaniu najnowszych informacji o buforze. Używanie streamingu plików dało mi jeden problem, że nie spłukiwało najnowszych dzienników z bufora. Ale w każdym razie, to był naprawdę pomocny przewodnik. Dziękuję.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-17 13:18:54