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);
  }
}
Author: Peri Hartman, 2013-11-11

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ć:

  1. obsłuż uncaughtException w podklasie aplikacji.
  2. po wyłapaniu wyjątku rozpocznij nową aktywność, aby poprosić Użytkownika o wysłanie kłoda.
  3. Wyodrębnij informacje dziennika z plików logcat i zapisz do Twój własne akta.
  4. Uruchom aplikację e-mail, dostarczając plik jako załącznik.
  5. Manifest: filtruj swoją aktywność, aby była rozpoznawana przez obsługę wyjątków.
  6. 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ć.
 220
Author: Peri Hartman,
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.

  1. Crashlytics - narzędzie do raportowania awarii, bezpłatne, ale daje podstawowe raporty Zalety: Free

  2. 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!

 8
Author: Ariel Bell,
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.

Https://github.com/ACRA/acra

 3
Author: Martin Konecny,
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
}
 3
Author: Jack Ruan,
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ę.

 1
Author: schow,
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