• Like
  • Save
Device fragmentation vs clean code
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

Device fragmentation vs clean code

  • 824 views
Published

Trying to implement the same functionality on Android on different devices, OS versions, manufacturers etc. can be more eventful than it should. …

Trying to implement the same functionality on Android on different devices, OS versions, manufacturers etc. can be more eventful than it should.

This presentation is a guide about how you can achieve your task at hand without compromising code quality and readability. Specifically, I demonstrate how to use Dependency Injection, the MVP pattern and other techniques to your favour, for graceful handling and degrading experience at production code and automated tests.

Published in Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
824
On SlideShare
0
From Embeds
0
Number of Embeds
4

Actions

Shares
Downloads
2
Comments
0
Likes
2

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Device Fragmentation Clean Code Iordanis “Jordan” Giannakakis @iordanis_g #fragVsCode
  • 2. Introduction • • • • Iordanis “Jordan” Giannakakis Android Team Lead – Shazam @iordanis_g #fragVsCode @iordanis_g #fragVsCode
  • 3. How Shazam works @iordanis_g #fragVsCode
  • 4. Bad vs. good code @iordanis_g #fragVsCode
  • 5. How is fragmentation related to clean code? @iordanis_g #fragVsCode
  • 6. Is fragmentation really a problem? android.support @iordanis_g #fragVsCode
  • 7. Is fragmentation really a problem? @iordanis_g #fragVsCode
  • 8. Is clean code really a concern? @iordanis_g #fragVsCode
  • 9. Is clean code really a concern? A couple of months later... @iordanis_g #fragVsCode
  • 10. SharedPreferences.Editor • commit() – slow • apply() – fast @iordanis_g #fragVsCode
  • 11. API example – MainActivity protected void onResume() { super.onResume(); SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences.Editor editor = sharedPreferences.edit(); prefEditor.putBoolean("has_launched_before", true); // Will only be available for versions after Froyo if(VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) { editor.apply(); } else { editor.commit(); } } @iordanis_g #fragVsCode
  • 12. Fixed it! @iordanis_g #fragVsCode
  • 13. What are the issues? protected void onResume() { super.onResume(); SharedPreferences sharedPreferences = Glue PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences.Editor editor = sharedPreferences.edit(); Business prefEditor.putBoolean("has_launched_before", true); Logic // Will only be available for versions after Froyo if(VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) { editor.apply(); } else { editor.commit(); } Glue } @iordanis_g #fragVsCode
  • 14. How to unit test? Good luck! @iordanis_g #fragVsCode
  • 15. Dependency Injection @iordanis_g #fragVsCode
  • 16. Dependency Injection Framework @iordanis_g #fragVsCode
  • 17. New MainActivity @Override public void onResume() { super.onResume(); editor.putBoolean("has_launched_before", true); preferencesSaver.save(editor); } @iordanis_g #fragVsCode
  • 18. Before & After protected void onResume() { super.onResume(); SharedPreferences sharedPreferences = PreferenceManager .getDefaultSharedPreferences(this); SharedPreferences.Editor editor = sharedPreferences.edit(); prefEditor.putBoolean( "has_launched_before", true); // Will only be available for versions after Froyo if(VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) { editor.apply(); } else { editor.commit(); } } public void onResume() { super.onResume(); editor.putBoolean( "has_launched_before", true); preferencesSaver.save(editor); } @iordanis_g #fragVsCode
  • 19. New MainActivity – Constructors // Constructor the system will call to instantiate this activity public MainActivity() { this(sharedPreferencesEditor(), preferencesSaver()); } // Constructor that can be used in production and test code public MainActivity(Editor editor, PreferencesSaver preferencesSaver) { this.editor = editor; this.preferencesSaver = preferencesSaver; } @iordanis_g #fragVsCode
  • 20. Module classes public class SharedPreferencesEditorModule { public static SharedPreferences.Editor sharedPreferencesEditor() { Context context = getApplication().getApplicationContext(); SharedPreferences preferences = getDefaultSharedPreferences(context); Editor editor = preferences.edit(); return editor; } } public class PreferencesSaverModule { public static PreferencesSaver preferencesSaver() { if(VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) { return new ApplyPreferencesSaver(); } return new CommitPreferencesSaver(); } } @iordanis_g #fragVsCode
  • 21. Application public class MyCoolApplication extends Application { private static MyCoolApplication application; @Override public void onCreate() { application = this; } public static MyCoolApplication getApplication() { return application; } } @iordanis_g #fragVsCode
  • 22. How to unit test? @Mock Editor editor; @Mock PreferencesSaver saver; @Test public void canSaveLaunchFlag() { mockery.checking(new Expectations() {{ atLeast(1).of(editor).putBoolean("has_launched_before", true); oneOf(saver).save(editor); }}); MainActivity activity = new MainActivity(editor, saver); activity.onResume(); } @iordanis_g #fragVsCode
  • 23. Capabilities example – Google+ @iordanis_g #fragVsCode
  • 24. Capabilities example public void onCreate(Bundle savedInstanceState) { // Set layout View googlePlus = findViewById(R.id.google_plus); try { getPackageManager().getApplicationInfo("com.android.vending", 0); googlePlus.setVisibility(VISIBLE); } catch (PackageManager.NameNotFoundException e) { googlePlus.setVisibility(GONE); } } @iordanis_g #fragVsCode
  • 25. MVP pattern @iordanis_g #fragVsCode
  • 26. The Model public class GooglePlayAvailability { private PackageManager packageManager; public GooglePlayAvailability(PackageManager packageManager) { this.packageManager = packageManager; } public boolean googlePlayIsAvailable() { try { packageManager.getApplicationInfo("com.android.vending", 0); return true; } catch (PackageManager.NameNotFoundException e) { return false; } } } @iordanis_g #fragVsCode
  • 27. The View public interface GooglePlusView { public void showGooglePlus(); public void hideGooglePlus(); } @iordanis_g #fragVsCode
  • 28. Activity as View public class MainActivity extends Activity implements GooglePlusView { public void onCreate(Bundle savedInstanceState) { //Set layout... googlePlus = findViewById(R.id.google_plus); //Injected googlePlusPresenter googlePlusPresenter.setView(this); googlePlusPresenter.present(); } public void showGooglePlus() { googlePlus.setVisibility(VISIBLE); } public void hideGooglePlus() { googlePlus.setVisibility(GONE); } } @iordanis_g #fragVsCode
  • 29. The Presenter public class GooglePlusPresenter { private GooglePlayAvailability googlePlayAvailability; private GooglePlusView googlePlusView; public GooglePlusPresenter(GooglePlayAvailability availability) { googlePlayAvailability = availability; } public void present() { if (googlePlayAvailability.googlePlayIsAvailable()) { googlePlusView.showGooglePlus(); } else { googlePlusView.hideGooglePlus(); } } } @iordanis_g #fragVsCode
  • 30. How to unit test? @Mock GooglePlayAvailability googlePlayAvailability; @Mock GooglePlusView googlePlus; private GooglePlusPresenter presenter; @Test public void hidesGooglePlusIfNotAvailable() { mockery.checking(new Expectations() {{ allowing(googlePlayAvailability).googlePlayIsAvailable(); will(returnValue(false)); oneOf(googlePlus).hideGooglePlus(); }}); presenter.present(); } @iordanis_g #fragVsCode
  • 31. Device size diversity @iordanis_g #fragVsCode
  • 32. Device size diversity @iordanis_g #fragVsCode
  • 33. How about Acceptance Tests? @iordanis_g #fragVsCode
  • 34. How about Acceptance Tests? public class ShazamTest extends ActivityInstrumentationTestCase2<ActivityUnderTest> { private static final int TIMEOUT = 5000; private Solo solo; protected void setUp() { solo = new Solo(getInstrumentation()); } } @iordanis_g #fragVsCode
  • 35. How about Acceptance Tests? public void testCanShazamOnPhone() { // Action // User hits the big Shazam button View shazamButton = solo.getView(R.id.big_button); solo.clickOnView(shazamButton); // Assertions // Track details are showing on full screen assertThat(solo.waitForCondition(titleShowsOnFullScreen(), TIMEOUT), is(true)); } @iordanis_g #fragVsCode
  • 36. How about Acceptance Tests? public void testShazamingOnTablet() { // Action // Hit small Shazam button and wait for animation to finish final View shazamButton = solo.getView(R.id.small_button); solo.clickOnView(shazamButton); waitForAnimationToFinish(); // Assertions // Track details are showing on side-panel assertThat(solo.waitForCondition(titleShowsOnSidePanel(), TIMEOUT), is(true)); } @iordanis_g #fragVsCode
  • 37. Fixed it! @iordanis_g #fragVsCode
  • 38. Test frameworks GWEN https://github.com/shazam/gwen @iordanis_g #fragVsCode
  • 39. New Acceptance Test protected void setUp() { solo = new Solo(getInstrumentation()); user = new User(solo); } public void testCanShowTrackAfterShazaming() { when(user).shazams(); then(user).seesDetailsForTrack(); } @iordanis_g #fragVsCode
  • 40. User class public class User implements Actor, Asserter { private final Solo solo; public void shazams() { Action<Solo, Void> action; if (isTablet()) { action = new ShazamTabletButtonAction(); } else { action = new ShazamPhoneButtonAction(); } action.actOn(solo); } public void seesDetailsForTrack() { Assertion<Solo> assertion; if (isTablet()) { assertion = new TabletDetailsAssertion(); } else { assertion = new PhoneDetailsAssertion(); } assertion.assertWith(solo); } } @iordanis_g #fragVsCode
  • 41. Actions public class ShazamPhoneButtonAction implements Action<Solo, Void> { public void actOn(Solo solo) { View bigButton = solo.getView(R.id.big_button); solo.clickOnView(bigButton); } } public class ShazamTabletButtonAction implements Action<Solo, Void> { public void actOn(Solo solo) { View smallButton = solo.getView(R.id.small_button); solo.clickOnView(smallButton); } } @iordanis_g #fragVsCode
  • 42. Assertions public class PhoneDetailsAssertion implements Assertion<Solo>{ public void assertWith(Solo solo) { assertThat(solo.waitForCondition(titleShowsOnFullScreen(), TIMEOUT), is(true)); } } public class TabletDetailsAssertion implements Assertion<Solo> { public void assertWith(Solo solo) { waitForAnimationToFinish(); assertThat(solo.waitForCondition(titleShowsOnSidePanel(), TIMEOUT), is(true)); } } @iordanis_g #fragVsCode
  • 43. Fixed it! @iordanis_g #fragVsCode
  • 44. Hiring... Questions? @iordanis_g #fragVsCode github.com/iordanis/androidCleanCode