Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Journey to 99% Crash Free

Journey to 99% Crash Free

DroidKaigi 2017 Day 2 (17:10 - 17:40 Room 1)
「テスト0から目指すクラッシュフリー率99%」

Fumihiko Shiroyama

March 09, 2017
Tweet

More Decks by Fumihiko Shiroyama

Other Decks in Technology

Transcript

  1. About Me • Fumihiko Shiroyama • Android App Developer •

    Nikkei Inc. • https://github.com/srym • https://twitter.com/fushiroyama
  2. Agenda • Face up to the reality of crash! •

    Take the first step for Unit Testing • Make biggest gains without Unit Testing
  3. The Fact Your App is Crashing • 99% Crash Free?

    Good! • DAU 30000 $SBTIFT
  4. The Fact Your App is Crashing • 99% Crash Free?

    Good! • DAU 30000 • 99.9% Crash Free? Great!!
  5. The Fact Your App is Crashing • 99% Crash Free?

    Good! • DAU 30000 • 99.9% Crash Free? Great!! 4UJMM$SBTIFT
  6. Crashlytics • Automatically collects crash info • No need for

    uploading obfuscation mappings • Crashlytics#logException(e) for non-fatal
  7. Crash Reporting • Automatically collects crash info as well •

    Need for uploading obfuscation mappings (there is a helper gradle plugin) • FirebaseCrash.report(e) for non-fatal
  8. Timber public class CrashReportingTree extends Timber.Tree { @Override protected void

    log(int priority, String tag, String message, Throwable t) { if (priority == Log.VERBOSE || priority == Log.DEBUG || priority == Log.INFO) { return; } if (t != null) { Crashlytics.logException(t); FirebaseCrash.report(t); } } }
  9. Timber public class CrashReportingTree extends Timber.Tree { @Override protected void

    log(int priority, String tag, String message, Throwable t) { if (priority == Log.VERBOSE || priority == Log.DEBUG || priority == Log.INFO) { return; } if (t != null) { Crashlytics.logException(t); FirebaseCrash.report(t); } } }
  10. Timber public class CrashReportingTree extends Timber.Tree { @Override protected void

    log(int priority, String tag, String message, Throwable t) { if (priority == Log.VERBOSE || priority == Log.DEBUG || priority == Log.INFO) { return; } if (t != null) { Crashlytics.logException(t); FirebaseCrash.report(t); } } }
  11. Timber public class CrashReportingTree extends Timber.Tree { @Override protected void

    log(int priority, String tag, String message, Throwable t) { if (priority == Log.VERBOSE || priority == Log.DEBUG || priority == Log.INFO) { return; } if (t != null) { Crashlytics.logException(t); FirebaseCrash.report(t); } } } /PO'BUBM
  12. Timber public class MyApplication extends Application { @Override public void

    onCreate() { super.onCreate(); initTimber(); } private void initTimber() { if (BuildConfig.DEBUG) { Timber.plant(new Timber.DebugTree()); } else { Timber.plant(new CrashReportingTree()); } } }
  13. Timber public class MyApplication extends Application { @Override public void

    onCreate() { super.onCreate(); initTimber(); } private void initTimber() { if (BuildConfig.DEBUG) { Timber.plant(new Timber.DebugTree()); } else { Timber.plant(new CrashReportingTree()); } } }
  14. Timber public class MyApplication extends Application { @Override public void

    onCreate() { super.onCreate(); initTimber(); } private void initTimber() { if (BuildConfig.DEBUG) { Timber.plant(new Timber.DebugTree()); } else { Timber.plant(new CrashReportingTree()); } } }
  15. Fat Activity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);

    button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { User user = fetchUser(userId); processUser(user); } }); }
  16. Fat Activity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);

    button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { User user = fetchUser(userId); processUser(user); } }); }
  17. Fat Activity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);

    button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { User user = fetchUser(userId); processUser(user); } }); }
  18. public class UserFetcher { private User fetchUser(ApiClient apiClient, long userId)

    { return apiClient.findUserById(userId); } } Make it testable
  19. public class UserFetcher { public User fetchUser(ApiClient apiClient, long userId)

    { return apiClient.findUserById(userId); } } Make it testable
  20. public class UserFetcher { private final ApiClient apiClient; public UserFetcher(ApiClient

    apiClient) { this.apiClient = apiClient; } public User fetchUser(long userId) { return apiClient.findUserById(userId); } } Make it testable
  21. public class UserFetcher { private final ApiClient apiClient; public UserFetcher(ApiClient

    apiClient) { this.apiClient = apiClient; } public User fetchUser(long userId) { return apiClient.findUserById(userId); } } Make it testable
  22. Make it testable • This is so-called Dependency Injection •

    DI can be done without any libraries! • You can use DI library at any time though
  23. Everything is in Activity @Override protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { User user = fetchUser(userId); processUser(user); } }); }
  24. MVP

  25. What is MVP • Model - View - Presenter •

    View…Activity (Fragment) • Model…Business Logic (e.g. fetching data) • Presenter…Bridge between V & M
  26. Introduce MVP public interface UserContract { public interface View {

    void showUser(User user); void showError(Exception error); } public interface Interaction { void clickButton(long userId); } }
  27. Introduce MVP public interface UserContract { public interface View {

    void showUser(User user); void showError(Exception error); } public interface Interaction { void clickButton(long userId); } } $POUSBDUCFUXFFO 7JFXBOE1SFTFOUFS
  28. Introduce MVP public interface UserContract { public interface View {

    void showUser(User user); void showError(Exception error); } public interface Interaction { void clickButton(long userId); } }
  29. Introduce MVP public interface UserContract { public interface View {

    void showUser(User user); void showError(Exception error); } public interface Interaction { void clickButton(long userId); } } $BMMCBDLGSPN 1SFTFOUFS
  30. Introduce MVP public interface UserContract { public interface View {

    void showUser(User user); void showError(Exception error); } public interface Interaction { void clickButton(long userId); } }
  31. Introduce MVP public interface UserContract { public interface View {

    void showUser(User user); void showError(Exception error); } public interface Interaction { void clickButton(long userId); } } &WFOUGSPN 6*
  32. Introduce MVP public class UserPresenter implements UserContract.Interaction { private final

    UserContract.View view; private final UserFetcher userFetcher; public UserPresenter(UserContract.View view, UserFetcher userFetcher) { this.view = view; this.userFetcher = userFetcher; } @Override public void clickButton(long userId) { } }
  33. Introduce MVP public class UserPresenter implements UserContract.Interaction { private final

    UserContract.View view; private final UserFetcher userFetcher; public UserPresenter(UserContract.View view, UserFetcher userFetcher) { this.view = view; this.userFetcher = userFetcher; } @Override public void clickButton(long userId) { } }
  34. Introduce MVP public class UserPresenter implements UserContract.Interaction { private final

    UserContract.View view; private final UserFetcher userFetcher; public UserPresenter(UserContract.View view, UserFetcher userFetcher) { this.view = view; this.userFetcher = userFetcher; } @Override public void clickButton(long userId) { } }
  35. Introduce MVP @Override public void clickButton(long userId) { userFetcher.findUserAsync(userId, new

    UserFetcher.OnSuccessCallback() { @Override public void onSuccess(User user) { view.showUser(user); } }, new UserFetcher.OnFailureCallback() { @Override public void onFailure(Exception e) { view.showError(e); } }); }
  36. Introduce MVP @Override public void clickButton(long userId) { userFetcher.findUserAsync(userId, new

    UserFetcher.OnSuccessCallback() { @Override public void onSuccess(User user) { view.showUser(user); } }, new UserFetcher.OnFailureCallback() { @Override public void onFailure(Exception e) { view.showError(e); } }); }
  37. Introduce MVP @Override public void clickButton(long userId) { userFetcher.findUserAsync(userId, new

    UserFetcher.OnSuccessCallback() { @Override public void onSuccess(User user) { view.showUser(user); } }, new UserFetcher.OnFailureCallback() { @Override public void onFailure(Exception e) { view.showError(e); } }); }
  38. Introduce MVP public class MyActivity extends BaseActivity implements UserContract.View {

    @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public void showUser(User user) {} @Override public void showError(Exception error) {} }
  39. Introduce MVP public class MyActivity extends BaseActivity implements UserContract.View {

    @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public void showUser(User user) {} @Override public void showError(Exception error) {} }
  40. Introduce MVP @Override protected void onCreate(@Nullable Bundle savedInstanceState) { final

    UserPresenter presenter = new UserPresenter(this, userFetcher); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { presenter.clickButton(userId); } }); } @Override public void showUser(User user) { toastMessage(user.getName()); } @Override public void showError(Exception e) { toastMessage(e()); } }
  41. Introduce MVP @Override protected void onCreate(@Nullable Bundle savedInstanceState) { final

    UserPresenter presenter = new UserPresenter(this, userFetcher); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { presenter.clickButton(userId); } }); } @Override public void showUser(User user) { toastMessage(user.getName()); } @Override public void showError(Exception e) { toastMessage(e()); } }
  42. Introduce MVP @Override protected void onCreate(@Nullable Bundle savedInstanceState) { final

    UserPresenter presenter = new UserPresenter(this, userFetcher); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { presenter.clickButton(userId); } }); } @Override public void showUser(User user) { toastMessage(user.getName()); } @Override public void showError(Exception e) { toastMessage(e()); } }
  43. Introduce MVP @Override protected void onCreate(@Nullable Bundle savedInstanceState) { final

    UserPresenter presenter = new UserPresenter(this, userFetcher); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { presenter.clickButton(userId); } }); } @Override public void showUser(User user) { toastMessage(user.getName()); } @Override public void showError(Exception e) { toastMessage(e()); } }
  44. FragmentTransaction public void show(FragmentManager manager, String tag) { mDismissed =

    false; mShownByMe = true; FragmentTransaction ft = manager.beginTransaction(); ft.add(this, tag); ft.commit(); }
  45. FragmentTransaction public void show(FragmentManager manager, String tag) { mDismissed =

    false; mShownByMe = true; FragmentTransaction ft = manager.beginTransaction(); ft.add(this, tag); ft.commit(); }
  46. RxLifecycle • RxLifecycle IS GREAT!! • Not all programmers, nor

    projects always need this. • Vulture is for you!
  47. Vulture • https://github.com/srym/vulture • Handles async callback safely • Based

    on PauseHandler* picture from wikimedia.org * https://goo.gl/g6w3CS
  48. Vulture void fetchAsynchronously() { /* do heavy asynchronous task here

    */ doCallback("finished!"); } void doCallback(@NonNull String message) { FinishDialog.newInstance().show( getSupportFragmentManager(), "TAG"); }
  49. Vulture void fetchAsynchronously() { /* do heavy asynchronous task here

    */ doCallback("finished!"); } void doCallback(@NonNull String message) { FinishDialog.newInstance().show( getSupportFragmentManager(), "TAG"); }
  50. Vulture void fetchAsynchronously() { /* do heavy asynchronous task here

    */ doCallback("finished!"); } void doCallback(@NonNull String message) { FinishDialog.newInstance().show( getSupportFragmentManager(), "TAG"); }
  51. Vulture void fetchAsynchronously() { /* do heavy asynchronous task here

    */ doCallback("finished!"); } void doCallback(@NonNull String message) { FinishDialog.newInstance().show( getSupportFragmentManager(), "TAG"); } 6OTBGF
  52. Vulture @ObserveLifecycle public class YourActivity extends AppCompatActivity { void fetchAsynchronously()

    { SafeMainActivity.doCallbackSafely("finished!"); } @SafeCallback void doCallback(@NonNull String message) { FinishDialog.newInstance().show( getSupportFragmentManager(), "TAG"); } }
  53. Vulture @ObserveLifecycle public class YourActivity extends AppCompatActivity { void fetchAsynchronously()

    { SafeMainActivity.doCallbackSafely("finished!"); } @SafeCallback void doCallback(@NonNull String message) { FinishDialog.newInstance().show( getSupportFragmentManager(), "TAG"); } }
  54. Vulture @ObserveLifecycle public class YourActivity extends AppCompatActivity { void fetchAsynchronously()

    { SafeMainActivity.doCallbackSafely("finished!"); } @SafeCallback void doCallback(@NonNull String message) { FinishDialog.newInstance().show( getSupportFragmentManager(), "TAG"); } }
  55. Vulture @Override protected void onResume() { super.onResume(); SafeMainActivity.register(this); } @Override

    protected void onPause() { SafeMainActivity.unregister(); super.onPause(); }
  56. Vulture @Override protected void onResume() { super.onResume(); SafeMainActivity.register(this); } @Override

    protected void onPause() { SafeMainActivity.unregister(); super.onPause(); }
  57. Supported Types • Primitive types and its boxed types •

    String, Bundle, Parcelable, Serializable • ParcelableArray • ParcelableArrayList
  58. Special Thanks • Illustrations by ͍Β͢ͱ΍ • http://www.irasutoya.com/ • Thank

    you so much!!! • https://www.wikimedia.org/ • All the audiences! Thank you!