http://connpass.com/event/20240/ How to implement crash report of Android-Library
Hand-made CrashReportofAndroid Librarypotatotips #22@petitviolet
View Slide
About me• @petitviolet• Fringe81 Co., Ltd.• ࠂSDKͷAndroid/iOS։ൃ• ࠷ۙScalaॻ͍ͯ·͢
ΫϥογϡϨϙʔτ
CrashReport• ΞϓϦέʔγϣϯ͕Ϋϥογϡͨ࣌͠ͷঢ়گΛϨϙʔτͱͯ͠ૹ৴• ͷछྨ• OS• ελοΫτϨʔε• etc.
crashlyticshttps://try.crashlytics.com/
ACRAhttps://github.com/ACRA/acra
Desire• ϥΠϒϥϦىҼͷΫϥογϡϩάऩू͍ͨ͠• ΞϓϦͷΫϥογϡϩάऩू͠ͳ͍• ͦΕCrashlyticsʹ͓ͤ• ಠࣗͷσʔλ͕ཉ͍͠
UncaughtException• (جຊతʹ)Ϋϥογϡ͢Δͱ͖ʹඈͿྫ֎• catch͞Εͳ͔ͬͨͷͰuncaught• ErrorͰͳ͘Exception
UncaughtExceptionHandler• UncaughtExceptionΛͲ͏ѻ͏͔• σϑΥϧτͷಈ࡞getDefaultUncaughtExceptionHandler• Defaultͷಈ࡞Λյ͞ͳ͍ͨΊʹอଘ
جຊ෦ class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { private static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID; private final Thread.UncaughtExceptionHandler mDefaultHandler; private WeakReference mContext; volatile private static boolean sCrashing = false; public MyUncaughtExceptionHandler(Context context) { mContext = new WeakReference<>(context.getApplicationContext()); mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); } }
جຊ෦ class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { private static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID; private final Thread.UncaughtExceptionHandler mDefaultHandler; private WeakReference mContext; volatile private static boolean sCrashing = false; public MyUncaughtExceptionHandler(Context context) { mContext = new WeakReference<>(context.getApplicationContext()); mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); } }UncaughtExceptionHandlerΛܧঝ
جຊ෦ class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { private static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID; private final Thread.UncaughtExceptionHandler mDefaultHandler; private WeakReference mContext; volatile private static boolean sCrashing = false; public MyUncaughtExceptionHandler(Context context) { mContext = new WeakReference<>(context.getApplicationContext()); mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); } }DefaultͷUncaughtExceptionHandlerΛอଘUncaughtExceptionHandlerΛܧঝ
جຊ෦@Override public void uncaughtException(final Thread thread, final Throwable ex) { if (sCrashing) { mDefaultHandler.uncaughtException(thread, ex); return; } sCrashing = true; StackTraceElement[] stackTraceElements = ex.getStackTrace(); boolean inLibraryException = false; for (StackTraceElement stackTraceElement : stackTraceElements) { if (stackTraceElement.getClassName().contains(PACKAGE_NAME)) { inLibraryException = true; break; } } // ϥΠϒϥϦͰͷΫϥογϡͩͬͨ߹ϩάΛૹΔ if (inLibraryException) { try { sendCrashReport(ex); } catch (Exception e) { e.printStackTrace(); } } // ϝΠϯεϨουͩͬͨ߹ࢮ͵͔͠ແ͍ if (thread.getId() == Looper.getMainLooper().getThread().getId()|| !inLibraryException) { mDefaultHandler.uncaughtException(thread, ex); } }
@Override public void uncaughtException(final Thread thread, final Throwable ex) { if (sCrashing) { mDefaultHandler.uncaughtException(thread, ex); return; } sCrashing = true; StackTraceElement[] stackTraceElements = ex.getStackTrace(); boolean inLibraryException = false; for (StackTraceElement stackTraceElement : stackTraceElements) { if (stackTraceElement.getClassName().contains(PACKAGE_NAME)) { inLibraryException = true; break; } }͢ͰʹΫϥογϡͨ͠ޙͳΒࢮ͵
@Override public void uncaughtException(final Thread thread, final Throwable ex) { if (sCrashing) { mDefaultHandler.uncaughtException(thread, ex); return; } sCrashing = true; StackTraceElement[] stackTraceElements = ex.getStackTrace(); boolean inLibraryException = false; for (StackTraceElement stackTraceElement : stackTraceElements) { if (stackTraceElement.getClassName().contains(PACKAGE_NAME)) { inLibraryException = true; break; } }͢ͰʹΫϥογϡͨ͠ޙͳΒࢮ͵LibraryͷΫϥογϡ͔Ͳ͏͔ఆ
// ϥΠϒϥϦͰͷΫϥογϡͩͬͨ߹ϩάΛૹΔ if (inLibraryException) { try { sendCrashReport(ex); } catch (Exception e) { e.printStackTrace(); } } // ϝΠϯεϨουͩͬͨ߹ࢮ͵͔͠ແ͍ if (thread.getId() == Looper.getMainLooper().getThread().getId()|| !inLibraryException) { mDefaultHandler.uncaughtException(thread, ex); } }
// ϥΠϒϥϦͰͷΫϥογϡͩͬͨ߹ϩάΛૹΔ if (inLibraryException) { try { sendCrashReport(ex); } catch (Exception e) { e.printStackTrace(); } } // ϝΠϯεϨουͩͬͨ߹ࢮ͵͔͠ແ͍ if (thread.getId() == Looper.getMainLooper().getThread().getId()|| !inLibraryException) { mDefaultHandler.uncaughtException(thread, ex); } }LibraryͷΫϥογϡͳΒϩάૹ৴
// ϥΠϒϥϦͰͷΫϥογϡͩͬͨ߹ϩάΛૹΔ if (inLibraryException) { try { sendCrashReport(ex); } catch (Exception e) { e.printStackTrace(); } } // ϝΠϯεϨουͩͬͨ߹ࢮ͵͔͠ແ͍ if (thread.getId() == Looper.getMainLooper().getThread().getId()|| !inLibraryException) { mDefaultHandler.uncaughtException(thread, ex); } }Library֎ͷΫϥογϡ͔UIεϨουͳΒࢮ͵LibraryͷΫϥογϡͳΒϩάૹ৴
جຊ෦@Override public void uncaughtException(final Thread thread, final Throwable ex) { if (sCrashing) { mDefaultHandler.uncaughtException(thread, ex); return; } sCrashing = true; StackTraceElement[] stackTraceElements = ex.getStackTrace(); boolean inLibraryException = false; for (StackTraceElement stackTraceElement : stackTraceElements) { if (stackTraceElement.getClassName().contains(PACKAGE_NAME)) { inLibraryException = true; break; } } // ϥΠϒϥϦͰͷΫϥογϡͩͬͨ߹ϩάΛૹΔ if (inLibraryException) { try { sendCrashReport(ex); } catch (Exception e) { e.printStackTrace(); } } // ϝΠϯεϨουͩͬͨ߹ࢮ͵͔͠ແ͍ if (thread.getId() == Looper.getMainLooper().getThread().getId()|| !inLibraryException) { mDefaultHandler.uncaughtException(thread, ex); } }LibraryͷΫϥογϡ͔Ͳ͏͔ఆLibraryͷΫϥογϡͳΒϩάૹ৴Library֎ͷΫϥογϡ͔UIεϨουͳΒࢮ͵͢ͰʹΫϥογϡͨ͠ޙͳΒࢮ͵
େࣄͳ͜ͱ• ϥΠϒϥϦΫϥογϡϩάΈͯվળ• ΞϓϦͷअຐΛ͠ͳ͍• σϑΥϧτͷಈ࡞Λࡴ͞ͳ͍• ϑϦʔζͤ͞ͳ͍Α͏ʹ• ແݶΫϥογϡ͠ͳ͍Α͏ʹཧ• ໘Ͱϑϥάཧ